-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinteractive1.js
226 lines (182 loc) · 9.86 KB
/
interactive1.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// I. Dynamically creating 4 div (box for the pieces) within my Puzzle container and then the img element within each div.
let puzzleContainer = document.getElementById("puzzle-container");
function creatingGrid() { //2*2 puzzle
let rows = 2;
let columns = 2;
for(let i=0; i<rows*columns; i++){ //creating 4divs and 4img
let pieceDiv = document.createElement('div') // creating div element
pieceDiv.className = "piece"; //class name added
puzzleContainer.appendChild(pieceDiv); //Nesting them in puzzlecontainer.
let img = document.createElement("img"); //creating img element
img.className = "images"; //class name added
img.id = `img${i}`; //unique ids
img.setAttribute("draggable", "true"); // Attribute required for events below, source: https://medium.com/@tatismolin/how-to-implement-drag-and-drop-functionality-using-vanilla-javascript-9ddfe2402695
pieceDiv.appendChild(img); //dynamically created 4 with individual id elements and nested them in the divs just created above.
}
}
creatingGrid(); //calling function
let img0 = document.getElementById("img0"); //Retrieving img elements just created
img0.src = "puzzle1/piece1.png"; //adding the source for the img elements.
img0.width = "200"; // Specifying width -space when browser loading the page
img0.height = "232"; //Specifying height -space when browser loading the page
img0.alt = "puzzle piece 1" //Specifying al text - accessibility
let img1 = document.getElementById("img1"); //Same as above, done 4 times
img1.src = "puzzle1/piece2.png";
img1.width = "232";
img1.height = "200";
img1.alt = "puzzle piece 2"
let img2 = document.getElementById("img2");
img2.src = "puzzle1/piece3.png";
img2.width = "232";
img2.height = "200";
img2.alt = "puzzle piece 3"
let img3 = document.getElementById("img3");
img3.src = "puzzle1/piece4.png";
img3.width = "199";
img3.height = "232";
img3.alt = "puzzle piece 4"
//II. Adding event listeners to start and reset buttons (Game Management)
let pieceContainer = document.getElementById("piece-container");
document.addEventListener('DOMContentLoaded', function () { // Assuring page is loaded
let startButton = document.getElementById('start');
let resetButton = document.getElementById('reset');
// Event listener for the start button
startButton.addEventListener('click', function(e){
startButton.style.display = 'none'; // Hiding the start button
resetButton.style.display = 'flex'; // Showing the reset button
Array.from(dropZone).forEach(function(zone){ //Iterating through elements with this class name as DOM returns the elements as an array
zone.style.border = "1px solid black"; //adding borders to div to indicate users where to put the pieces
});
let pieces = document.getElementsByClassName("piece");
let element = e.target; //moving pieces (div which include their nested img) currently in Puzzle container to Piece container on click event for start button.
e.preventDefault();
Array.from(pieces).forEach(function(piece){ //Iterating through my piece divs (DOM understand class name as an array of all the element with that class)
let leftPosition = Math.floor(Math.random()*50); //giving them random position (iterated through numbers for the multiplier to make sure that the piece where relatively contained within the piece container.
let topPosition = Math.floor(Math.random()*50);
piece.style.position = "absolute";
piece.style.left = `${leftPosition}%`;
piece.style.top = `${topPosition}%`
pieceContainer.appendChild(piece);
});
});
// Event listener for the reset button
resetButton.addEventListener('click', function(e) {
resetButton.style.display = 'none'; // Hiding the reset button
startButton.style.display = 'flex'; // Showing the start button
let element = e.target;
window.location.reload(); // https://www.freecodecamp.org/news/javascript-refresh-page-how-to-reload-a-page-in-js/#:~:text=The%20simplest%20way%20to%20refresh,and%20loading%20the%20latest%20content.
});
});
//III. After adding the event, I realised that I needed to create new divs that will remain in the puzzle container so I can later drop the divs that have the nested img in them.
function creatingDropZones() { //2*2 puzzle - same principle as creatingGrid()
let rows = 2;
let columns = 2;
for(let i=0; i<rows*columns; i++){
let dropZone = document.createElement('div')
dropZone.className = "drop-zone";
puzzleContainer.appendChild(dropZone);
}
}
creatingDropZones();
//IV. Implementing logic of the game with drag and drop events:
/* A. Creating unique ID dynamically to my drag and drop divs, to later be able to create a function to match the correct place of each piece*/
let dropZone = document.getElementsByClassName("drop-zone");
Array.from(dropZone).forEach(function(dropZone, i){
dropZone.id = `drop-${i}`;
});
let pieceDiv = document.getElementsByClassName("piece");
Array.from(pieceDiv).forEach(function(pieceDiv, i){
pieceDiv.id = `drag-${i}`;
});
//B. Adding drag and drop events to the divs. Source: https://www.youtube.com/watch?v=_G8G1OrEOrI&ab_channel=DarwinTech
/*Process explained:
After implementing drag and drop, I realised that it does not work for mobile devices as they are mouse based events.
Instead I need to use touch start, move and end. As such, to make my code as clear as possible, I have created 3 functions below.
Since event listeners can take 2 parameters, each take either drag/drop/move or touch event as a firt parameter along with calling the appropriate function that will handle their common logic and account for their differences.*/
let draggableImages = document.getElementsByClassName("images");
var globalDraggedItemId = null; // set data and get data are methods that do not exist for touch events. Creating global variable to maintain state.
function handleStart(e) {
e.stopPropagation(); // ensure that the click event is just on the image not its parent
if (e.type === 'dragstart') {
console.log(e);
e.dataTransfer.setData('text/plain', e.currentTarget.id); //Seeting the data to add id to my target element that is my div and retriving it in drop- source: https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setData
}
if (e.type === 'touchstart') {
e.preventDefault();
}
globalDraggedItemId = e.currentTarget.id; // setting data, source https://stackoverflow.com/questions/53530511/imitating-drag-and-drop-events-with-touch-events-for-mobile-devices
}
function handleOverMove(e){
e.preventDefault();
e.stopPropagation();
let draggedImage = e.currentTarget;
draggedImage.style.zIndex = 1000; // Without giving high index to the images and low to their divs, clicking on images didnt work if a div was above it.
let originalContainer = draggedImage.parentElement;
originalContainer.style.zIndex = 1;
}
function handleDropEnd(e) {
e.preventDefault();
e.stopPropagation();
let draggedItem;
let targetContainer = e.currentTarget;
if (targetContainer.querySelector('img')){ //if my dropdiv (target container) has an image already, user cant put another there.
} else {
if (e.type === 'drop') {
let data = e.dataTransfer.getData("text/plain"); //retrieving id and moving it the div to target.
draggedItem = document.getElementById(data);
} else if (e.type === 'touchend') {
draggedItem = document.getElementById(globalDraggedItemId);
}
if (draggedItem) {
e.currentTarget.appendChild(draggedItem);
globalDraggedItemId = null; // Reset the global variable
}
checkPosition(); //calling function for alert messages below
}
}
Array.from(draggableImages).forEach(function(image){ //Iterating through each element and assigning touch and mouse event to each.
image.addEventListener('dragstart', handleStart);
image.addEventListener('touchstart', handleStart);
});
Array.from(dropZone).forEach(function(zone){
zone.addEventListener('dragover', handleOverMove);
zone.addEventListener('touchmove', handleOverMove);
zone.addEventListener('drop', handleDropEnd);
zone.addEventListener('touchend', handleDropEnd);
});
// V. Finish message
let correctPosition = { // creating an object using individual ids created earlier.
img0: "drop-0",
img1: "drop-1",
img2: "drop-2",
img3: "drop-3",
};
function checkPosition() {
let allPlaced = true;
let allCorrect = true;
for (let [imgId, dropId] of Object.entries(correctPosition)) { //iterating through my object that contains the correct positions of each piece.
let image = document.getElementById(imgId);
let currentDrop = image.parentElement.id;
// Checking if all pieces are placed in the drop zone divs
if (!image.parentElement || !image.parentElement.classList.contains('drop-zone')) {
allPlaced = false;
}
// Checking if the images' parents' id are that of the dropzone id => aka they are they correctly placed
if (currentDrop !== dropId) {
allCorrect = false;
}
}
if (allPlaced && !allCorrect) {// All pieces are placed, but some are wrong
setTimeout(() => {
alert("You are almost there! But some pieces are in the wrong place.");
}, 500); // Delay to allow for the puzzle to be visually completed before showing the message
} else if (allPlaced && allCorrect) { // All pieces are placed correctly
Array.from(dropZone).forEach(function(drop){ //purely aesthetics considerations, otherwise, we would see the borders
drop.style.border = "none";
})
setTimeout(() => {
alert("Congratulations! You completed the puzzle.");
}, 500);
}
return true;
}