Detecting the collision of 2 divs with JavaScript - javascript

I'm making a simple Space Invaders Clone in Web and ran into an issue. The code works well so far with the exception of the collision system. I need to destroy an enemy every time that the player's bullet hits it. In order to make it, I'm grabbing the x and y coordinates of both the enemies' div and the bullet's div. The issue appears on the values comparison: apparently getBoundingClientRect() grabs the x and y coordinates from within the element excluding its real size. Hence, according to my code, only when the bullet is perfectly inside the enemy that things would trigger. Is there a better way of doing it? How so?
Thanks in advance.
const shoot = (x) => {
//Creates bullet
const main = document.getElementById("main");
const div_bullet = document.createElement("div");
//Appends child
main.appendChild(div_bullet);
//Sets position
div_bullet.style.top = "340px";
div_bullet.style.left = x + "px";
//Gives it a class
div_bullet.classList.add("div_bullet");
div_bullet.setAttribute("id", "bullet");
//Deletes bullet
setTimeout(() => {
div_bullet.remove();
}, 1000);
};
const load_game = () => {
//Adding movement to the spaceship
const space_ship = document.getElementById("space_ship");
let x = 375;
window.addEventListener("keydown", (e) => {
switch (e.key) {
case "ArrowLeft":
if (x <= 15) break;
x = x - 10;
space_ship.style.left = x + "px";
break;
case "ArrowRight":
if (x >= 725) break;
x = x + 10;
space_ship.style.left = x + "px";
break;
case "ArrowUp":
if (document.getElementsByClassName("div_bullet").length === 0)
shoot(x + 20);
break;
default:
break;
}
});
};
const collision_system = () => {
setInterval(() => {
//Checks if any enemy was hit
const e1 = document.getElementById("e1");
const e2 = document.getElementById("e2");
const e3 = document.getElementById("e3");
const e4 = document.getElementById("e4");
const e5 = document.getElementById("e5");
const enemies_position = [{
x: e1.getBoundingClientRect().x,
y: e1.getBoundingClientRect().y
},
{
x: e2.getBoundingClientRect().x,
y: e2.getBoundingClientRect().y
},
{
x: e3.getBoundingClientRect().x,
y: e3.getBoundingClientRect().y
},
{
x: e4.getBoundingClientRect().x,
y: e4.getBoundingClientRect().y
},
{
x: e5.getBoundingClientRect().x,
y: e5.getBoundingClientRect().y
},
];
if (document.getElementById("bullet")) {
const x_bullet = document
.getElementById("bullet")
.getBoundingClientRect().x;
const y_bullet = document
.getElementById("bullet")
.getBoundingClientRect().y;
console.log(
"X: " + enemies_position[0].x + "Y: " + enemies_position[0].y
);
console.log("X bullet: " + x_bullet + "Y bullet : " + y_bullet);
for (let i = 0; i < 5; i++) {
if (
enemies_position[i].x === x_bullet &&
enemies_position[i].y === y_bullet
)
alert("Shoot!");
}
}
}, 10);
};
document.addEventListener("DOMContentLoaded", () => {
load_game();
collision_system();
});
* {
padding: 0px;
margin: 0px;
}
body {
background-color: black;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.main {
width: 800px;
height: 500px;
border: 2px white solid;
border-radius: 10px;
}
#keyframes enemies_move {
from {
left: 80px;
}
to {
left: -80px;
}
}
.enemies {
margin-top: 20px;
position: fixed;
width: 800px;
height: 50px;
display: flex;
justify-content: space-evenly;
}
.enemies div {
position: relative;
width: 50px;
height: 50px;
background-color: orange;
animation: enemies_move 4s alternate infinite;
}
.barriers {
width: 800px;
height: 20px;
position: fixed;
top: 460px;
display: flex;
justify-content: space-evenly;
}
.barriers div {
width: 100px;
height: 20px;
background-color: white;
}
.space_ship {
width: 50px;
height: 50px;
border: 2px white solid;
position: relative;
top: 440px;
left: 375px;
}
#keyframes shoot_bullet {
from {
top: 340px;
}
to {
top: -50px;
}
}
.div_bullet {
left: 17px;
width: 15px;
height: 40px;
background-color: white;
position: relative;
animation: shoot_bullet 1s;
}
<div id="main" class="main">
<div class="enemies">
<div id="e1"></div>
<div id="e2"></div>
<div id="e3"></div>
<div id="e4"></div>
<div id="e5"></div>
</div>
<div class="barriers">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div id="space_ship" class="space_ship"></div>
</div>

The problem in your code is that you are using only the x and y of the bullet and enemy to check for collisions. You need to use the width and height of both items to check if they collide.
The checks needed to see if two rects collide is
function rectCollision(rectA, rectB) {
return (
rectA.x < rectB.x + rectB.width &&
rectA.x + rectA.width > rectB.x &&
rectA.y < rectB.y + rectB.height &&
rectA.height + rectA.y > rectB.y
);
}
And using this in your code makes it work
const shoot = (x) => {
//Creates bullet
const main = document.getElementById("main");
const div_bullet = document.createElement("div");
//Appends child
main.appendChild(div_bullet);
//Sets position
div_bullet.style.top = "340px";
div_bullet.style.left = x + "px";
//Gives it a class
div_bullet.classList.add("div_bullet");
div_bullet.setAttribute("id", "bullet");
//Deletes bullet
setTimeout(() => {
div_bullet.remove();
}, 1000);
};
const load_game = () => {
//Adding movement to the spaceship
const space_ship = document.getElementById("space_ship");
let x = 375;
window.addEventListener("keydown", (e) => {
switch (e.key) {
case "ArrowLeft":
if (x <= 15) break;
x = x - 10;
space_ship.style.left = x + "px";
break;
case "ArrowRight":
if (x >= 725) break;
x = x + 10;
space_ship.style.left = x + "px";
break;
case "ArrowUp":
if (document.getElementsByClassName("div_bullet").length === 0)
shoot(x + 20);
break;
default:
break;
}
});
};
const collision_system = () => {
setInterval(() => {
//Checks if any enemy was hit
const e1 = document.getElementById("e1");
const e2 = document.getElementById("e2");
const e3 = document.getElementById("e3");
const e4 = document.getElementById("e4");
const e5 = document.getElementById("e5");
const enemies_position = [
e1.getBoundingClientRect(),
e2.getBoundingClientRect(),
e3.getBoundingClientRect(),
e4.getBoundingClientRect(),
e5.getBoundingClientRect(),
];
if (document.getElementById("bullet")) {
const bullet = document
.getElementById("bullet")
.getBoundingClientRect();
console.log(
"X: " + enemies_position[0].x + "Y: " + enemies_position[0].y
);
console.log("X bullet: " + bullet.x + "Y bullet : " + bullet.y);
for (let i = 0; i < 5; i++) {
if (
rectCollision(enemies_position[i], bullet)
)
alert("Shoot!");
}
}
}, 10);
};
function rectCollision(rectA, rectB) {
return (rectA.x < rectB.x + rectB.width &&
rectA.x + rectA.width > rectB.x &&
rectA.y < rectB.y + rectB.height &&
rectA.height + rectA.y > rectB.y)
}
document.addEventListener("DOMContentLoaded", () => {
load_game();
collision_system();
});
* {
padding: 0px;
margin: 0px;
}
body {
background-color: black;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.main {
width: 800px;
height: 500px;
border: 2px white solid;
border-radius: 10px;
}
#keyframes enemies_move {
from {
left: 80px;
}
to {
left: -80px;
}
}
.enemies {
margin-top: 20px;
position: fixed;
width: 800px;
height: 50px;
display: flex;
justify-content: space-evenly;
}
.enemies div {
position: relative;
width: 50px;
height: 50px;
background-color: orange;
animation: enemies_move 4s alternate infinite;
}
.barriers {
width: 800px;
height: 20px;
position: fixed;
top: 460px;
display: flex;
justify-content: space-evenly;
}
.barriers div {
width: 100px;
height: 20px;
background-color: white;
}
.space_ship {
width: 50px;
height: 50px;
border: 2px white solid;
position: relative;
top: 440px;
left: 375px;
}
#keyframes shoot_bullet {
from {
top: 340px;
}
to {
top: -50px;
}
}
.div_bullet {
left: 17px;
width: 15px;
height: 40px;
background-color: white;
position: relative;
animation: shoot_bullet 1s;
}
<div id="main" class="main">
<div class="enemies">
<div id="e1"></div>
<div id="e2"></div>
<div id="e3"></div>
<div id="e4"></div>
<div id="e5"></div>
</div>
<div class="barriers">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div id="space_ship" class="space_ship"></div>
</div>

Related

HTML5 Canvas rectangles click coordinates is inconsistent

I made a canvas size as A4, i draw a rectangles inside and when i click the light blue rectangle it will call a function but the coordinates of the click on the rectangle is inconsistent and every row the y level is needed to click more lower then the light blue rectangle.
How i make the coordinates more accurate?
(Code need full screen)
'use strict';
function mmTopx(mm) {
//return mm * 3.7795275591;
//return mm * 3.7795275590551;
return (mm * 96) / 25.4;
}
function cmTopx(cm) {
return cm * 1; /* NEED TO FIX */
}
// Get mouse position
function getMousePos(c, evt) {
var rect = c.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top,
};
}
class sticker {
#width;
#height;
#fullheight;
constructor(width = 1, height = 1, fullheight = 1) {
if (!sticker.isNumber(width) || !sticker.isNumber(height) || !sticker.isNumber(fullheight)) throw 'Only accept number';
this.#width = width;
this.#height = height;
this.#fullheight = fullheight;
}
static isNumber(n) {
return !isNaN(parseFloat(n)) && !isNaN(n - 0) && typeof n != 'string';
}
get size() {
return { width: this.#width, height: this.#height, fullheight: this.#fullheight };
}
get width() {
return this.#width;
}
get height() {
return this.#height;
}
get fullheight() {
return this.#fullheight;
}
set width(n) {
if (!sticker.isNumber(n)) throw 'Only accept number';
this.#width = n;
}
set height(n) {
if (!sticker.isNumber(n)) throw 'Only accept number';
this.#height = n;
}
set fullheight(n) {
if (!sticker.isNumber(n)) throw 'Only accept number';
this.#fullheight = n;
}
}
window.onload = () => {
const controls = document.querySelectorAll('.control');
const canvas = document.querySelector('.A4');
const canvasPadding = {
left:
window
.getComputedStyle(canvas, null)
.getPropertyValue('padding-left')
.match(/\d+.\d+|\d+/)[0] - 0,
top:
window
.getComputedStyle(canvas, null)
.getPropertyValue('padding-top')
.match(/\d+.\d+|\d+/)[0] - 0,
};
/* ///////////////////////////////////////////////////// */
/* ///////////////////////////////////////////////////// */
/* ///// CANVAS ///// */
/* ///////////////////////////////////////////////////// */
/* canvas */
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
const ctx = canvas.getContext('2d');
const _sticker = new sticker(mmTopx(25), mmTopx(12), mmTopx(37));
let Stickers = [];
const drawSticker = (x, y, width, height, fullheight) => {
Stickers.push({
color: '#d8d8d8',
width: width,
height: height,
x: x,
y: y,
clicked: function () {
console.log(`you clicked me, ${this.x} ${this.y}`);
},
});
/* full area */
ctx.fillStyle = '#e8f2f8';
ctx.fillRect(x, y, width, fullheight); // x y width height
/* print area */
ctx.fillStyle = '#d8d8d8';
ctx.fillRect(x, y, width, height, fullheight); // x y width height
};
const render = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
Stickers = [];
const size = _sticker.size;
for (let y = 0; y < canvas.height - size.fullheight; y += size.fullheight + 10) {
for (let x = 10; x < canvas.width - size.width; x += size.width + 1) {
drawSticker(x, y, size.width, size.height, size.fullheight);
}
}
};
/* ///////////////////////////////////////////////////// */
/* ///////////////////////////////////////////////////// */
/* ///// Event Listeners ///// */
/* ///////////////////////////////////////////////////// */
canvas.addEventListener('updateRender', (event) => {
const lengthType = event.detail.dimensionValue.match(/mm|cm/)[0];
let value = event.detail.dimensionValue.match(/\d+/)[0] - 0;
switch (lengthType) {
case 'cm':
//value = cmTopx(value);
break;
case 'mm':
default:
value = mmTopx(value);
break;
}
/* Set the new sticker size */
switch (event.detail.dimension) {
case 'width':
_sticker.width = value;
break;
case 'height':
_sticker.height = value;
break;
case 'fullheight':
_sticker.fullheight = value;
break;
}
render();
});
const event = new Event('change');
controls.forEach((_control) => {
_control.addEventListener(
'change',
(event) => {
const value = event.target.value;
const name = event.target.name;
const dimension = event.target.getAttribute('data-sticker-dim');
const detail = {};
detail['dimension'] = dimension;
detail['dimensionValue'] = value;
detail['name'] = name;
const updateSticker = new CustomEvent('updateRender', { detail: detail });
canvas.dispatchEvent(updateSticker);
},
true
);
const input = _control.querySelector('input');
input.dispatchEvent(event);
});
/* click event for text input */
canvas.addEventListener('click', function (e) {
e.preventDefault();
let x = parseInt(e.clientX - canvas.offsetLeft - canvasPadding.left);
let y = parseInt(e.clientY - canvas.offsetTop - canvasPadding.top);
if (x >= 0 && y >= 0) {
for (let index = 0; index < Stickers.length; index++) {
const _sticker = Stickers[index];
if (
x >= _sticker.x &&
x <= Math.floor(_sticker.x + _sticker.width) &&
y >= _sticker.y &&
y <= Math.floor(_sticker.y + _sticker.height)
) {
_sticker.clicked();
break;
}
}
}
});
/* ///////////////////////////////////////////////////// */
};
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
background-color: #fafafa;
}
/* //////////////////////////////////////////////////////////// */
.container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-self: flex-start;
overflow: auto;
}
/* //////////////////////////////////////////////////////////// */
.container .controllers {
position: sticky;
top: 50%;
transform: translateY(-50%);
width: 250px;
height: 500px;
flex: 0 0 100px;
border-radius: 20px;
box-sizing: border-box;
padding: 20px;
margin-left: 5px;
border: 1px solid #9e9e9e;
}
.container .controllers .control {
position: relative;
flex: 0 0 100%;
height: 50px;
padding: 5px;
}
.container .controllers .control:first-child {
margin-top: 5px;
}
.container .controllers .control:not(:first-child) {
margin-top: 20px;
}
.container .controllers .control label::before {
content: attr(data-placeholder);
position: absolute;
left: 20px;
top: 20px;
color: #65657b;
font-family: sans-serif;
line-height: 14px;
pointer-events: none;
transform-origin: 0 50%;
transition: transform 200ms, color 200ms;
}
.container .controllers .control .cut {
position: absolute;
border-radius: 10px;
height: 20px;
width: fit-content;
left: 20px;
top: -20px;
transform: translateY(0);
transition: transform 200ms;
}
.container .controllers .control input {
height: 100%;
padding: 4px 20px 0;
width: 100%;
background-color: #eeeeee;
border-radius: 12px;
border: 0;
outline: 0;
box-sizing: border-box;
color: #eee;
font-size: 18px;
color: black;
}
.container .controllers .control input:focus ~ .cut,
.container .controllers .control input:not(:placeholder-shown) ~ .cut {
transform: translateY(8px);
}
.container .controllers .control input:focus ~ label::before,
.container .controllers .control input:not(:placeholder-shown) ~ label::before {
content: attr(data-focus-placeholder);
transform: translateY(-30px) translateX(10px) scale(0.75);
}
.container .controllers .control input:not(:placeholder-shown) ~ label::before {
content: attr(data-focus-placeholder);
color: #808097;
}
.container .controllers .control input:focus ~ label::before {
color: #dc2f55;
}
/* //////////////////////////////////////////////////////////// */
.container textarea {
background: none;
outline: none;
}
.container .A4 {
align-self: center;
width: 21cm;
flex: 0 0 29.7cm;
padding: 1cm;
border: 1px #d3d3d3 solid;
border-radius: 5px;
background: #f5f5f5;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
}
/* //////////////////////////////////////////////////////////// */
#page {
size: A4;
margin: 0;
}
#media print {
html,
body {
height: 99%;
}
.page {
margin: 0;
border: initial;
border-radius: initial;
width: initial;
min-height: initial;
box-shadow: initial;
background: initial;
page-break-after: always;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>A4 Render Stickers</title>
</head>
<body>
<div class="container">
<div class="controllers">
<button>print</button>
<div class="control">
<input type="text" value="25mm" data-sticker-dim="width" name="printWidth" placeholder=" " />
<div class="cut"></div>
<label for="printWidth" data-focus-placeholder="Width print" data-placeholder="Width size of print area"></label>
</div>
<div class="control">
<input type="text" value="12mm" data-sticker-dim="height" name="printHeight" placeholder=" " />
<div class="cut"></div>
<label for="printHeight" data-focus-placeholder="Height print" data-placeholder="Height size of print area"></label>
</div>
<div class="control">
<input type="text" value="37mm" data-sticker-dim="fullheight" name="stikcerHeight" placeholder=" " />
<div class="cut"></div>
<label for="stikcerHeight" data-focus-placeholder="Full height sticker" data-placeholder="Height size of full sticker"></label>
</div>
</div>
<canvas width="800" height="800" class="A4"></canvas>
</div>
</body>
</html>

How to have a list of rows which are drag/droppable?

I am working on a prototype that can enable drag & drop actions between containers. However, I have a conflict case in this :
when I use position: absolute for the draggable items, they are draggable but they are on top of each other :/
when I use position: relative, they are distributed evenly in their container but they are not draggable :/
I collected the code in the below chuck to be testable; please let me know what you think how I can fix this.
<!DOCTYPE html>
<html>
<head>
<style>
html,
body {
width: 100%;
height: 100%;
overflow: hidden;
}
.lang1Container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.meaningContainer {
border: 1px solid rgba(15, 15, 15, 0.2);
box-shadow: 0px 0px 5px 10px rgba(138, 57, 57, 0.2);
}
.lang1Item {
width: 300px;
height: 20px;
position: absolute;
background-color: #f44336;
color: #ffffff;
font-family: Oxygen, sans-serif;
font-size: px;
padding: 7px;
box-shadow: inset 1px 1px 10px 0 rgba(138, 57, 57, 0.2);
border-radius: 3px;
cursor: pointer;
}
.snap {
width: 301px;
height: 30px;
margin-top: 25px;
margin-bottom: 25px;
}
.over {
background-color: rgb(158, 89, 87, 0.2);
border: 1px solid #7c5b59;
border-radius: 3px;
border-style: dashed;
}
</style>
</head>
<body>
<div style="display: flex; flex-direction: column">
<div
id="lang1MeaningContainer"
style="
background-color: bisque;
min-width: 300px;
min-height: 400px;
border-color: red;
border-style: dashed;
margin: 10px;
"
>
<div class="header">lang1MeaningContainer - Meaning Container</div>
<div class="snap"></div>
<div class="snap"></div>
<div class="snap"></div>
<div class="snap"></div>
<div class="snap"></div>
<div class="snap"></div>
</div>
<div
id="lang1Container"
style="
background-color: bisque;
min-width: 300px;
min-height: 400px;
border-color: red;
border-style: dashed;
margin: 10px;
display: flex;
flex-direction: column;
"
>
<div class="header">lang1Container - English Words</div>
<div id="item1" class="lang1Item">Lang Item 1</div>
<div id="item2" class="lang1Item">Lang Item 2</div>
<span id="item3" class="lang1Item">Lang Item 3</span>
<span id="item4" class="lang1Item">Lang Item 4</span>
</div>
</div>
<script>
let lang1Container = document.getElementById("lang1Container");
let meaningContainer = document.getElementById("lang1MeaningContainer");
let lang1Items = document.getElementsByClassName("lang1Item");
console.log("length:" + lang1Items.length);
for (let i = 0; i < lang1Items.length; i++) {
let item = lang1Items[i];
item.addEventListener("mousedown", (e) => {
onMouseDown(e, item);
});
document.body.addEventListener("mousemove", (e) => {
onMouseMove(e, item);
});
item.addEventListener("mouseup", (e) => {
onMouseUp(e, item);
});
}
let mouseOffset = { x: 0, y: 0 };
let isMouseDown = false;
// for (let i = 0; i < 6; i++) {
// let snap = document.createElement("div");
// snap.className = "snap";
// meaningContainer.appendChild(snap);
// }
let doElsCollide = function (el1, el2) {
el1.offsetBottom = el1.offsetTop + el1.offsetHeight;
el1.offsetRight = el1.offsetLeft + el1.offsetWidth;
el2.offsetBottom = el2.offsetTop + el2.offsetHeight;
el2.offsetRight = el2.offsetLeft + el2.offsetWidth;
return !(
el1.offsetBottom < el2.offsetTop ||
el1.offsetTop > el2.offsetBottom ||
el1.offsetRight < el2.offsetLeft ||
el1.offsetLeft > el2.offsetRight
);
};
function snapMeaning(item, container) {
let box = container.getBoundingClientRect();
item.style.left = box.x + "px";
item.style.top = box.y + "px";
}
function onMouseDown(e, item) {
isMouseDown = true;
mouseOffset = {
x: item.offsetLeft - e.clientX,
y: item.offsetTop - e.clientY,
};
item.style.backgroundColor = "red";
}
function onMouseMove(e, item) {
e.preventDefault();
if (isMouseDown) {
item.style.left = e.clientX + mouseOffset.x + "px";
let tmpStyle = getComputedStyle(item);
let strMarginTop = tmpStyle.marginTop;
let tmpMarginTop = strMarginTop.replace("px", "");
item.style.top = e.clientY - tmpMarginTop + mouseOffset.y + "px"; // zero margin is used in CSS definition, otherwise margin amount need to be subtracted from the item.style.top value to have a proper event reaction. UU
// checking collusions with snaps
let snaps = document.getElementsByClassName("snap");
//console.log("Started checking");
for (let i = 0; i < snaps.length; i++) {
if (doElsCollide(item, snaps[i])) {
snaps[i].className = "snap over";
//console.log("[" + Date.now() + "]", "Collides with " + i);
} else {
snaps[i].className = "snap";
}
}
//console.log("Ended checking");
}
}
function onMouseUp(e, item) {
isMouseDown = false;
let snaps = document.getElementsByClassName("snap");
for (let i = 0; i < snaps.length; i++) {
if (doElsCollide(item, snaps[i])) {
snapMeaning(item, snaps[i]);
}
}
item.className = "lang1Item";
}
</script>
</body>
</html>

Issues with div size and position updates on mouse move event

I'm implementing a simple horizontal split pane using html, css, and javascript, adapted from this post.
While everything works smoothly for the vertical split pane, the horizontal implementation is jerky. The div sizes and positions jump to an unexpected value, but I am unable to spot when.
The issue is very subtle on below snippet, but some jumpiness and lagging can be observed.
Snippet below:
function dragElement(element, direction) {
var md;
const first = document.getElementById("map");
const second = document.getElementById("table");
element.onmousedown = onMouseDown;
function onMouseDown(e) {
md = {
e,
offsetLeft: element.offsetLeft,
offsetTop: element.offsetTop,
offsetBottom: element.offsetBottom,
firstWidth: first.offsetWidth,
secondWidth: second.offsetWidth,
firstHeight: first.offsetHeight,
secondHeight: first.offsetHeight
};
document.onmousemove = onMouseMove;
document.onmouseup = () => {
document.onmousemove = document.onmouseup = null;
}
}
function onMouseMove(e) {
var delta = {
x: e.clientX - md.e.x,
y: e.clientY - md.e.y
};
if (direction === "H") {
delta.x = Math.min(Math.max(delta.x, - md.firstWidth),
md.secondWidth);
element.style.left = md.offsetLeft + delta.x + "px";
first.style.width = (md.firstWidth + delta.x) + "px";
second.style.width = (md.secondWidth - delta.x) + "px";
}
if (direction === "V") {
delta.y = Math.min(Math.max(delta.y, - md.firstHeight), md.secondHeight);
element.style.top = md.offsetTop + delta.y + "px";
first.style.height = (md.firstHeight + delta.y) + "px";
second.style.height = (md.secondHeight - delta.y) + "px";
}
}
}
dragElement(document.getElementById("separator"), "V");
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.splitter {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
/*for horizontal*/
#separator {
cursor: row-resize;
background-color: #aaa;
background-repeat: no-repeat;
background-position: center;
width: 100%;
height: 10px;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#map {
width: 100%;
height: 20%;
min-height: 10px;
padding: 0;
margin: 0;
}
#table {
width: 100%;
height: 20%;
min-height: 10px;
}
<div class="splitter">
<div id="map"></div>
<div id="separator"></div>
<div id="table"></div>
</div>
The reason is the secondHeight is getting 0 when we move up and down.
So I get splitter class height and subtract the firstHeight form that and value equals to secondHeight.
secondHeight: (splitter.offsetHeight - first.offsetHeight)
function dragElement(element, direction) {
var md;
const first = document.getElementById("map");
const second = document.getElementById("table");
const splitter = document.getElementById("container");
element.onmousedown = onMouseDown;
function onMouseDown(e) {
md = {
e,
offsetLeft: element.offsetLeft,
offsetTop: element.offsetTop,
offsetBottom: element.offsetBottom,
firstWidth: first.offsetWidth,
secondWidth: second.offsetWidth,
firstHeight: first.offsetHeight,
secondHeight: (splitter.offsetHeight - first.offsetHeight)
};
document.onmousemove = onMouseMove;
document.onmouseup = () => {
document.onmousemove = document.onmouseup = null;
}
}
function onMouseMove(e) {
var delta = {
x: e.clientX - md.e.x,
y: e.clientY - md.e.y
};
if (direction === "H") {
delta.x = Math.min(Math.max(delta.x, -md.firstWidth),
md.secondWidth);
element.style.left = md.offsetLeft + delta.x + "px";
first.style.width = (md.firstWidth + delta.x) + "px";
second.style.width = (md.secondWidth - delta.x) + "px";
}
if (direction === "V") {
delta.y = Math.min(Math.max(delta.y, -md.firstHeight), md.secondHeight);
element.style.top = md.offsetTop + delta.y + "px";
first.style.height = (md.firstHeight + delta.y) + "px";
second.style.height = (md.secondHeight - delta.y) + "px";
}
}
}
dragElement(document.getElementById("separator"), "V");
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.splitter {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
}
/*for horizontal*/
#separator {
cursor: row-resize;
background-color: #aaa;
background-repeat: no-repeat;
background-position: center;
width: 100%;
height: 10px;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#map {
width: 100%;
height: 20%;
min-height: 10px;
padding: 0;
margin: 0;
}
#table {
width: 100%;
height: 20%;
min-height: 10px;
}
<div id="container" class="splitter">
<div id="map"></div>
<div id="separator"></div>
<div id="table"></div>
</div>

Javascript element translate and lifecycle

I'm doing an js assignment and the goals as mentioned in the fiddle below is to drag the main div, show when the div is centered and to translate it to it starting position.
goals:
After releasing ".draggable" it should animate back to its original position.
Currently I'm stuck with translating the div and understanding why the flow only works once then gets "frozen" and doesn't remove listeners.
I would love some clarifications on how should I approach the translate portion and can replicate this behavior as long as the client runs.
<html>
<head>
<style>
html,
body {
height: 100%;
margin: 0;
}
.draggable {
position: absolute;
left: 0;
top: 0;
z-index: 1;
width: 150px;
height: 150px;
cursor: grab;
}
.draggable-inner {
width: 100%;
height: 50%;
}
.draggable-inner.top {
background: #1ADECB;
}
.draggable-inner.bottom {
background: #1A8FDE;
}
.draggable.animate {
transition: 500ms transform ease;
}
.markers {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 0;
}
.marker {
background: yellow;
opacity: 0.2;
transition: 500ms opacity ease;
position: absolute;
}
.marker.visible {
background: #1ADE91;
opacity: 1;
}
.center-x-marker {
width: 10px;
height: 100%;
}
.center-y-marker {
width: 100%;
height: 10px;
}
</style>
</head>
<body>
<div class="draggable">
<div class="draggable-inner top"></div>
<div class="draggable-inner bottom"></div>
</div>
<div class="markers">
<div class="marker center-x-marker"></div>
<div class="marker center-y-marker"></div>
</div>
<script>
let isDragging = false;
const deltaToCenter = {
x: 0,
y: 0
};
const dragPosition = {
x: 0,
y: 0
};
const screenCenterPosition = {
x: 0,
y: 0
};
const draggable = document.querySelector('.draggable');
const markerX = document.querySelector('.center-x-marker');
const markerY = document.querySelector('.center-y-marker');
let {
width,
height
} = draggable.getBoundingClientRect();
const draggableCenter = {
x: width / 2,
y: height / 2
};
draggable.addEventListener('mousedown', initDrag);
function onTransitionEnd() {
draggable.classList.remove('animate');
hideMarkers();
}
function hideMarkers() {
markerX.classList.remove('visible');
markerY.classList.remove('visible');
}
function handleDrag(event) {
isDragging = true;
let pX = event.pageX;
let pY = event.pageY;
draggable.style.left = pX + "px";
draggable.style.top = pY + "px";
let dragObj = draggable.getBoundingClientRect();
if (dragObj.top === 145 && dragObj.bottom === 295){
markerX.classList.add('visible');
markerY.classList.add('visible');
}
}
function stopDrag() {
isDragging = false;
var bodyRect = document.body.getBoundingClientRect(),
elemRect = draggable.getBoundingClientRect();
draggable.classList.add('animate');
document.removeEventListener('mousemove', handleDrag);
draggable.addEventListener('webkitTransitionEnd', onTransitionEnd);
draggable.addEventListener('transitionend', onTransitionEnd);
draggable.style.transform = (`translate(0px, 0px)`);
}
function initDrag(event) {
isDragging = true;
dragPosition.x = event.pageX;
dragPosition.y = event.pageY;
screenCenterPosition.x = parseInt(document.body.offsetWidth / 2);
screenCenterPosition.y = parseInt(document.body.offsetHeight / 2);
document.addEventListener('mouseup', stopDrag);
document.addEventListener('mousemove', handleDrag);
}
</script>
</body>
</html>
You main problem is that you are trying to reset the top left with transform.
// use left and top:
draggable.style.left = pX + "px";
draggable.style.top = pY + "px";
// reset:
draggable.style.left = 0 + "px";
draggable.style.top= 0 + "px";
// or use transform:
draggable.style.transform = "translate("+pX+"px, "+pY+"px)";
// reset:
draggable.style.transform = "translate(0px, 0px)";
Also I moved your event listeners out of your functions since you don't really need to remove them.
let isDragging = false;
const deltaToCenter = {
x: 0,
y: 0
};
const dragPosition = {
x: 0,
y: 0
};
const screenCenterPosition = {
x: 0,
y: 0
};
const draggable = document.querySelector('.draggable');
const markerX = document.querySelector('.center-x-marker');
const markerY = document.querySelector('.center-y-marker');
let {
width,
height
} = draggable.getBoundingClientRect();
const draggableCenter = {
x: width / 2,
y: height / 2
};
draggable.addEventListener('mousedown', initDrag);
document.addEventListener('mousemove', handleDrag);
document.addEventListener('mouseup', stopDrag);
draggable.addEventListener('transitionend', onTransitionEnd);
function initDrag(event) {
isDragging = true;
dragPosition.x = event.pageX;
dragPosition.y = event.pageY;
screenCenterPosition.x = parseInt(document.body.offsetWidth / 2);
screenCenterPosition.y = parseInt(document.body.offsetHeight / 2);
}
function handleDrag(event) {
if (isDragging) {
let pX = event.pageX;
let pY = event.pageY;
draggable.style.transform = 'translate('+pX+'px, '+pY+'px)';
let dragObj = draggable.getBoundingClientRect();
if (dragObj.top === 145 && dragObj.bottom === 295) {
markerX.classList.add('visible');
markerY.classList.add('visible');
}
}
}
function stopDrag() {
isDragging = false;
draggable.classList.add('animate');
draggable.style.transform = 'translate(0px, 0px)';
}
function onTransitionEnd() {
draggable.classList.remove('animate');
hideMarkers();
}
function hideMarkers() {
markerX.classList.remove('visible');
markerY.classList.remove('visible');
}
draggable.addEventListener('mousedown', initDrag);
draggable.addEventListener('transitionend', onTransitionEnd);
document.addEventListener('mouseup', stopDrag);
document.addEventListener('mousemove', handleDrag);
html,
body {
height: 100%;
margin: 0;
overflow: hidden;
}
.draggable {
position: absolute;
left: 0;
top: 0;
z-index: 1;
width: 150px;
height: 150px;
cursor: grab;
}
.draggable-inner {
width: 100%;
height: 50%;
}
.draggable-inner.top {
background: #1ADECB;
}
.draggable-inner.bottom {
background: #1A8FDE;
}
.draggable.animate {
transition: 500ms transform ease;
}
.markers {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 0;
}
.marker {
background: yellow;
opacity: 0.2;
transition: 500ms opacity ease;
position: absolute;
}
.marker.visible {
background: #1ADE91;
opacity: 1;
}
.center-x-marker {
width: 10px;
height: 100%;
}
.center-y-marker {
width: 100%;
height: 10px;
}
<div class="draggable">
<div class="draggable-inner top"></div>
<div class="draggable-inner bottom"></div>
</div>
<div class="markers">
<div class="marker center-x-marker"></div>
<div class="marker center-y-marker"></div>
</div>

How do I make my jquery game's gameboard responsive?

I'm trying to create a game using Jquery however I have an issue when trying to make the webpage responsive. The issue is that I'm unable to make my game-board "spelplan" responsive, I know the reason this happens is because parts of my script is connected to the "width" of the game-board so when I remove the "width" and replace it with <div id="spelplan" class="col-6 col-m-6"> these parts get completely messed up. So what I need help with is how I make my game-board "spelplan" responsive, I would really appreciate any help I can get with this.
function updateClock() {
var currentTime = new Date();
var currentHours = currentTime.getHours();
var currentMinutes = currentTime.getMinutes();
var currentSeconds = currentTime.getSeconds();
currentMinutes = (currentMinutes < 10 ? "0" : "") + currentMinutes;
currentSeconds = (currentSeconds < 10 ? "0" : "") + currentSeconds;
var timeOfDay = (currentHours < 12) ? "AM" : "PM";
currentHours = (currentHours > 12) ? currentHours - 12 : currentHours;
currentHours = (currentHours == 0) ? 12 : currentHours;
var currentTimeString = currentHours + ":" + currentMinutes + ":" + currentSeconds + " " + timeOfDay;
document.getElementById("clock").firstChild.nodeValue = currentTimeString;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: black;
}
header {
position: absolute;
top: 50px;
color: white;
text-align: center;
}
#clock {
font-size: 25px;
position: absolute;
color: white;
}
#rand_pos {
position: absolute;
top: 20%;
left: 30%;
z-index: 10;
}
.player {
background-color: red;
height: 50px;
width: 50px;
position: absolute;
top: 100px;
left: 100px;
z-index: 100;
}
p {
position: relative;
left: 10px;
color: white;
}
#spelplan {
position: relative;
left: 35%;
top: 200px;
height: 600px;
width: 600px;
background-color: blue;
border-style: double;
border-radius: 40px;
}
.rand {
background-color: green;
height: 15px;
width: 15px;
position: absolute;
left: 30%;
top: 150px;
z-index: 3;
}
.new_pos {
background: #ccc;
border: 1px solid #000;
padding: 5px;
box-shadow: 0 0 20px #555;
-webkit-transition: all .2s ease-in;
transition: all .2s ease-in;
}
.new_pos:hover {
background: #bbb;
box-shadow: 0 0 20px #222;
}
.new_pos:active {
box-shadow: 0 0 20px #000;
background: #aaa;
}
*:focus {
outline: none;
}
.new_pos {
position: fixed;
left: 0;
bottom: 0;
cursor: pointer;
}
#footer {
position: absolute;
top: 80vh;
color: white;
text-align: center;
}
/* For mobile phones: */
[class*="col-"] {
width: 100%;
}
#media only screen and (min-width: 600px) {
/* For tablets: */
.col-m-1 {
width: 8.33%;
}
.col-m-2 {
width: 16.66%;
}
.col-m-3 {
width: 25%;
}
.col-m-4 {
width: 33.33%;
}
.col-m-5 {
width: 41.66%;
}
.col-m-6 {
width: 50%;
}
.col-m-7 {
width: 58.33%;
}
.col-m-8 {
width: 66.66%;
}
.col-m-9 {
width: 75%;
}
.col-m-10 {
width: 83.33%;
}
.col-m-11 {
width: 91.66%;
}
.col-m-12 {
width: 100%;
}
img {
width: 80%;
height: auto;
}
}
#media only screen and (min-width: 768px) {
/* For desktop: */
.col-1 {
width: 8.33%;
}
.col-2 {
width: 16.66%;
}
.col-3 {
width: 25%;
}
.col-4 {
width: 33.33%;
}
.col-5 {
width: 41.66%;
}
.col-6 {
width: 50%;
}
.col-7 {
width: 58.33%;
}
.col-8 {
width: 66.66%;
}
.col-9 {
width: 75%;
}
.col-10 {
width: 83.33%;
}
.col-11 {
width: 91.66%;
}
.col-12 {
width: 100%;
}
img {
width: 100%;
height: auto;
}
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style20.css">
<script type='text/javascript' src='eventjavascript6.js'></script>
<title>Jquery spel</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
var sprites = [];
var enemies = [];
var game = $("#spelplan");
var score = 0;
var el_score = $("#score")
function SCORE(pts) {
score += pts
el_score.text(score);
}
function RND(min, max) {
return parseInt(Math.random() * (max - min) + min);
}
var sprite = function(id, x, y, w, h, _class, view, collisionDetect, options) {
this.view = view;
this.id = id
this.x = x + "px";
this.y = y + "px";
this.width = w;
this.height = h;
this.options = options;
this.el = $("<div id='" + this.id + "' class='" + _class + "'></div>").css('left', this.x).css('top', this.y);
view.append(this.el);
this.x = function() {
return this.el.position().left;
}
this.y = function() {
return this.el.position().top;
}
this.up = function() {
if (this.y() > 0) {
this.el.animate({
top: '-=25px'
}, 0);
if (collisionDetect) collisionDetect(this);
}
};
this.down = function() {
if (this.y() < this.view.height() - this.height) {
this.el.animate({
top: '+=25px'
}, 0);
if (collisionDetect) collisionDetect(this);
}
};
this.left = function() {
if (this.x() > 0) {
this.el.animate({
left: '-=25px'
}, 0);
if (collisionDetect) collisionDetect(this);
}
};
this.right = function() {
if (this.x() + this.width < this.view.width()) {
this.el.animate({
left: '+=25px'
}, 0);
if (collisionDetect) collisionDetect(this);
}
this.destroy = function() {
this.el.remove();
for (var i = 0; i < sprites.length; i++) {
if (data[i].id == this.id) {
sprites.splice(i, 1);
break;
}
}
}
};
this.getPos = function() {
var pos, width, height;
pos = this.el.position();
width = this.el.width();
height = this.el.height();
return [
[pos.left, pos.left + width],
[pos.top, pos.top + height]
];
};
this.comparePos = function(p1, p2) {
var r1, r2;
r1 = p1[0] < p2[0] ? p1 : p2;
r2 = p1[0] < p2[0] ? p2 : p1;
return r1[1] > r2[0] || r1[0] === r2[0];
};
this.collidesWith = function(sprite) {
if (sprite.destroyed === true) return;
var pos1 = this.getPos(),
pos2 = sprite.getPos();
return this.comparePos(pos1[0], pos2[0]) && this.comparePos(pos1[1], pos2[1]);
};
if (this.options && this.options.init) this.options.init(this);
sprites.push(this);
};
function spawnrand() {
if (sprites.length > 100) return
var points = [50, 100, 200, 300, 400, 500];
var spelplanWidth = game.width();
var spelplanHeight = game.height();
var randPosY = Math.floor((Math.random() * spelplanHeight));
var randPosX = Math.floor((Math.random() * spelplanWidth));
var enemy = new sprite("enemy" + sprites.length + 1, randPosY, randPosX, 15, 15, "rand", game, null, {
PTS: points[RND(0, 5)],
init: function(sprite) {
sprite.selfDestruct = setTimeout(function() {
sprite.el.fadeOut(1000, function() {});
}, 5000);
}
});
enemies.push(enemy);
}
SCORE(0);
var player = new sprite("box1", 200, 200, 50, 50, "player", game,
function(sprite) {
sprites.forEach(function(sprite) {
if (sprite.id !== "box1" && player.collidesWith(sprite)) {
sprite.destroyed = true;
clearTimeout(sprite.selfDestruct);
sprite.el.fadeOut(100, function() {});
SCORE(sprite.options.PTS);
}
})
});
setInterval(spawnrand, 250);
$(document).keydown(function(e) {
if (e.keyCode == 37) {
player.left();
} else if (e.keyCode == 39) {
player.right();
} else if (e.keyCode == 38) {
player.up();
} else if (e.keyCode == 40) {
player.down();
}
});
});
</script>
</head>
<body onload="updateClock(); setInterval('updateClock()', 1000 )">
<span id="clock"> </span>
<header class="col-12 col-m-12">
<h1>Samla så mycket poäng du hinner genom att ta de gröna bollarna innan de försvinner</h1>
</header>
<div id="spelplan" class="col-6 col-m-6">
<div>
<p>Score:<span id="score"></span></p>
</div>
</div>
<section id="footer" class="col-12 col-m-12">
<h1>Använd piltangenterna för att styra den röda kuben </h1>
</section>
</body>
</html>
So again what I need help with is making the game-board "spelplan" responsive, any help is appreciated!

Categories

Resources