Drag, drop and local storage

DragAndDrop

Една от многото интересни задачи в курса по JavaScript е свързана с упражняването на Drag and Drop техниката , а също така и използването на Local storage за съхраняване на информация.
За целта създавам игра, в която играчът събира разхвърлени торби с боклук и ги изхвърля в приготвена за целта кофа. При поднасяне на торба над кофата, капака на кофата се отваря. Също така се отчита времето за събиране на всички торби и се прави класиране. След края на всяка игра резултата на играча се записва Local storage. Веднага след това всички резултати се изчитат от Local storage, сортират се и имената на петте най-добри играчи се показват на екрана.

1.Drag and drop.

За да изпълня Drag and drop техниката съм подходил по следния начин:

  1. При създаването на кофата за боклук съм прикачил към нея два event-а. Първият слуша дали има изпуснат боклук в кофата (ondrop) и извиква функцията drop(ev), която измества торбата с боклук в кофата. Вторият слуша дали има провлачване на боклук върху кофата (ondragover) и съответно извиква функцията allowDrop(ev), която да отвори капака на кофата.
    function createTrashCan() {
    var trashCan = document.createElement("div");
    trashCan.id = "trashCan";
    trashCan.addEventListener("drop", drop, false);
    trashCan.addEventListener("dragover", allowDrop, false);
    document.body.appendChild(trashCan);
    }
    
  2. При създаването на площа, върху която е разпилян боклука прикачам към нея event, който слуша дали има провлачване на боклук върху тази площ (ondragover) и съответно извиква функцията closeTrashCan(), която да затвори капака на кофата.
    function createTrashArea() {
    var trashArea = document.createElement("div");
    trashArea.id = "trashArea";
    trashArea.addEventListener("dragover", closeTrashCan, false);
    document.body.appendChild(trashArea);
    createRandomBags();
    }
    
  3. При създаването на всяка от торбите с боклук (функциите createBags() -> createDivElements()) поставям атрибута на „div“ контейнера й draggable = „true“. Също така прикачам към него event, който слуша дали провлачване е започнало (ondragstart) и съответно извиква функцията drag(ev), която записва ID-to  на съответната торба с боклук в специален dataTransfer обект. Така, когато пуснем дадената торба и извикаме функцията drop(ev) може да вземем информацията за ID-то на торбата обратно от същия обект. Използват се двете функции dataTransfer.setData и dataTransfer.getData, съответно за записване и прочитане на информация от обекта.

2.Local storage.

Резултата от всяка игра се записва в Local storage-a -> localStorage.setItem(playerName, seconds). Веднага след записа всички резултати се прочитат обратно от Local storage, сортират се и първите пет се изкарват на екран – loadLocalStorageOnScoreBoard().

function savePlayerScore() {
localStorage.setItem(playerName, seconds);
loadLocalStorageOnScoreBoard();
}

function loadLocalStorageOnScoreBoard() {
if (!localStorage.length || localStorage.length == 0) {
return;
}

var sorterItems = sortLocalStorage();
var resultHTML = "<ul>";

if (localStorage.length > 5) {
var numberOfItemsInScoreBoard = 5;
}
else {
numberOfItemsInScoreBoard = localStorage.length;
}

for (var i = 0; i < numberOfItemsInScoreBoard; i++) {
var playerName = sorterItems[i].key;
var playerValue = sorterItems[i].value;
resultHTML +=
'<li>' +
(i + 1) + ". " + playerName + " : " + playerValue + 's'
'</li>';
}
resultHTML += "</ul>";
document.getElementById("scoreBoard").innerHTML = resultHTML;
}

function sortLocalStorage() {
if (localStorage.length > 0) {
var localStorageArray = new Array();
for (i = 0; i < localStorage.length; i++) {
localStorageArray[i] = new LocalStorageItem(
localStorage.key(i),
localStorage.getItem(localStorage.key(i))
);
}
}
var sortedArray = localStorageArray.sort(comparator);
return sortedArray;
}

Използвал съм отделна функция comparator() за сортиране на информацията в Local storage. Така направена функцията comparator(a,b) и подадена на array.sort() ще сортира масива от обекти (LocalStorageItem(key, value)) в низходящ ред според резултата на играча.

function comparator(a, b) {
return parseInt(a.value) - parseInt(b.value);
}

//Class that represents Local storage item
function LocalStorageItem(key, value)
{
this.key = key;
this.value = value;
}

Пълният javascript код следва, а тук може да изпробвате демото на играта -> DEMO.

function renderPlaygroud() {
playerName = document.getElementById("playerName").value;

removeInitialInfo();
createTrashCan();
createTrashArea();
createScoreBoard()
createTimer();
}

function removeInitialInfo() {
var initialInfo = document.getElementById("initialInfo");
document.body.removeChild(initialInfo);
}

function createTrashCan() {
var trashCan = document.createElement("div");
trashCan.id = "trashCan";
trashCan.addEventListener("drop", drop, false);
trashCan.addEventListener("dragover", allowDrop, false);
document.body.appendChild(trashCan);
}

function createTrashArea() {
var trashArea = document.createElement("div");
trashArea.id = "trashArea";
trashArea.addEventListener("dragover", closeTrashCan, false);
document.body.appendChild(trashArea);
createBags();
}

function createScoreBoard() {
var scoreBoard = document.createElement("div");
scoreBoard.id = "scoreBoard";
document.body.appendChild(scoreBoard);
loadLocalStorageOnScoreBoard();
}

function createBags() {
var numberBags = 10;
var docFragment = document.createDocumentFragment();
for (var i = 0; i < numberBags; i++) {
var divElement = createDivElement(i);
docFragment.appendChild(divElement);
}
var trashArea = document.getElementById("trashArea");
trashArea.appendChild(docFragment);
}

function createDivElement(number) {
var divElement = document.createElement("div");

//Add inline styles
divElement.id = "div" + number;
divElement.style.width = "76px";
divElement.style.height = "118px";
divElement.style.backgroundImage = "url('pictures/trash.png')";
divElement.style.position = "absolute";
divElement.style.top = generateRandomNumberFromInterval(0, 480) + "px";
divElement.style.left = generateRandomNumberFromInterval(0, 800) + "px";
divElement.draggable = "true";
divElement.addEventListener("dragstart", drag, false);

return divElement;
}

function generateRandomNumberFromInterval(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}

function createTimer() {
timer = document.createElement("div");
timer.id = "timer";
document.body.appendChild(timer);

seconds = 0;
updateTimer();
window.setTimeout("tick()", 1000);
}

function tick() {
seconds += 1;
updateTimer();
window.setTimeout("tick()", 1000);
}

function updateTimer() {
var timeString = playerName + " : " + seconds + "s";
timer.innerHTML = timeString;
}

function allowDrop(ev) {
openTrashCan();
ev.preventDefault();
}

function drag(ev) {
ev.dataTransfer.setData("dragged-id", ev.target.id);
}

function drop(ev) {
ev.preventDefault();
var data = ev.dataTransfer.getData("dragged-id");
var element = document.getElementById(data);
element.parentNode.removeChild(element);
closeTrashCan();

var numberOfBags = document.getElementById("trashArea").childNodes.length;
if (numberOfBags == 0) {
savePlayerScore(playerName, seconds);
hidePlayGround();
var message = document.createElement('p');
message.innerHTML =
"Congratulations! You have managed to clean the trash for " + seconds + 's';
document.body.appendChild(message);
createResetButton();
}
}

function hidePlayGround() {
var trashCan = document.getElementById("trashCan");
trashCan.parentNode.removeChild(trashCan);
var trashArea = document.getElementById("trashArea");
trashArea.parentNode.removeChild(trashArea);
var timer = document.getElementById("timer");
timer.style.display = "none";
}

function createResetButton() {
var button = document.createElement("input");
button.type = "button";
button.value = "Restart Game";
button.addEventListener("click", function () { document.location.reload(true) }, false);
document.body.appendChild(button);
}

function openTrashCan() {
var trashCan = document.getElementById("trashCan");
trashCan.style.backgroundImage = "url('pictures/opened.png')"
}

function closeTrashCan() {
var trashCan = document.getElementById("trashCan");
trashCan.style.backgroundImage = "url('pictures/closed.png')"
}

function loadLocalStorageOnScoreBoard() {
if (!localStorage.length || localStorage.length == 0) {
return;
}

var sorterItems = sortLocalStorage();
var resultHTML = "<ul>";

if (localStorage.length > 5) {
var numberOfItemsInScoreBoard = 5;
}
else {
numberOfItemsInScoreBoard = localStorage.length;
}

for (var i = 0; i < numberOfItemsInScoreBoard; i++) {
var playerName = sorterItems[i].key;
var playerValue = sorterItems[i].value;
resultHTML +=
'<li>' +
(i + 1) + ". " + playerName + " : " + playerValue + 's'
'</li>';
}
resultHTML += "</ul>";
document.getElementById("scoreBoard").innerHTML = resultHTML;
}

function sortLocalStorage() {
if (localStorage.length > 0) {
var localStorageArray = new Array();
for (i = 0; i < localStorage.length; i++) {
localStorageArray[i] = new LocalStorageItem(
localStorage.key(i),
localStorage.getItem(localStorage.key(i))
);
}
}
var sortedArray = localStorageArray.sort(comparator);
return sortedArray;
}

function comparator(a, b) {
return parseInt(a.value) - parseInt(b.value);
}

//Class that represents Local storage item
function LocalStorageItem(key, value)
{
this.key = key;
this.value = value;
}

function savePlayerScore() {
localStorage.setItem(playerName, seconds);
loadLocalStorageOnScoreBoard();
}