Detect collision (video game js) - javascript

I am building a video game where fireballs drop from the top screen. The spaceship, moved by controllers, must avoid those fireballs in order win. My issue is that I do not know how to detect when the spaceship collides into fireballs. However, I found this link: Detect if animated object touched another object in DOM. I analysed this code and it seems it only works for his issue particularly. Do you guys know how to do this?
Code for image spaceship and fireball:
<img src="Photo/fireball.png" id="fireball">
<img src="Photo/Spaceship1.png" id="icon-p">
Code for spaceship:
let rect = icon
let pos = {top: 1000, left: 570}
const keys = {}
window.addEventListener("keydown", function(e) {keys[e.keyCode] = true})
window.addEventListener("keyup", function(e) {keys[e.keyCode] = false})
const loop = function() {
if (keys[37] || keys[81]) {pos.left -= 10}
if (keys[39] || keys[68]) {pos.left += 10}
if (keys[38] || keys[90]) {pos.top -= 10}
if (keys[40] || keys[83]) {pos.top += 10}
var owidth = display.offsetWidth
var oheight = display.offsetHeight
var iwidth = rect.offsetWidth
var iheight = rect.offsetHeight
if (pos.left < 0) pos.left = -10
if (pos.top < 0) pos.top = -10
if (pos.left + iwidth >= owidth) pos.left = owidth-iwidth
if (pos.top + iheight >= oheight) pos.top= oheight-iheight
rect.setAttribute("data", owidth + ":" + oheight)
rect.style.left = pos.left + "px"; rect.style.top = pos.top + "px"}
let sens = setInterval(loop, 1000 / 60)
Code for fireball:
function fFireball(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))}
let fireballElement = document.querySelector("#fireball");
let fireball = {x: fFireball(fireballElement.offsetWidth), y: 0}
const fireLoop = function() {
fireball.y += 2
fireballElement.style.top = fireball.y + 'px'
if (fireball.y > window.innerHeight) {
fireball.x = fFireball(fireballElement.offsetWidth)
fireballElement.style.left = fireball.x + 'px'; fireball.y = 0}}
fireballElement.style.left = fireball.x + 'px'
let fireInterval = setInterval(fireLoop, 1000 / 100)
Thanks!

here I've integrated collision detection for your game. The most notable thing is in this function:
function checkCollision() {
var elem = document.getElementById("icon");
var elem2 = document.getElementById("fireball");
if ( detectOverlap(elem, elem2) && elem2.getAttribute('hit')=='false' ){
hits++; // detect hit and increase
elem2.setAttribute('hit', true); // set attribute to not have flooding
console.log( hits ); // console it just to see it
}
setTimeout( checkCollision, 20);
}
In lower window you can test demo that I've built for you without images but some random boxes as images :)
Good luck with game man, cool
//////////
"use strict"
//Stay on focus
function stayOnFocus() {
setTimeout(function() {
alert("Do not exit window or game progression will be lost!")
}, 1000)
}
let hits = 0
//"A" keypress plays Music
document.addEventListener('keydown', function(e) {
if (e.keyCode !== 173) {
//document.getElementById('audio').play()
}
})
//Input Validation
let icon = document.getElementById("icon")
let fireballElement = document.querySelector("#fireball")
var input = document.getElementById("input")
input.addEventListener("keydown", function(event) {
if (event.keyCode === 13) {
event.preventDefault();
document.getElementById("begin-timer").click()
}
})
//CountDown (3...2...1)
var count = 3
function countDown() {
function preventCountFast() {
document.getElementById("count").innerHTML = count
if (count > 0) {
count--
} else {
clearInterval(ncount);
document.getElementById("count").style.display = "none"
}
}
var ncount = setInterval(preventCountFast, 1000)
}
//Name displayed + space(switch between images) + parameter icon displayed
function Username(field) {
field = input.value
if (field == "") {
alert("Complete blanks");
return false
}
document.getElementById("askName").style.display = "none"
setTimeout(function() {
document.getElementById("name").innerHTML = "Player: " + field
icon.style.display = 'block';
fireballElement.style.display = "block"
const images = ["https://placehold.it/30x30", "https://placehold.it/90x90",
"https://placehold.it/120x40", "https://placehold.it/100x100"
]
document.body.onkeyup = function(e) {
if (e.keyCode === 32) {
hits++;
icon.src = images[hits % 5]
}
}
checkCollision();
}, 4000)
}
//Spaceship moves into space + prevent going out borders
let display = document.getElementById("body");
let rect = icon
let pos = {
top: 1000,
left: 570
}
const keys = {}
window.addEventListener("keydown", function(e) {
keys[e.keyCode] = true
})
window.addEventListener("keyup", function(e) {
keys[e.keyCode] = false
})
const loop = function() {
if (keys[37] || keys[81]) {
pos.left -= 10
}
if (keys[39] || keys[68]) {
pos.left += 10
}
if (keys[38] || keys[90]) {
pos.top -= 10
}
if (keys[40] || keys[83]) {
pos.top += 10
}
var owidth = display.offsetWidth
var oheight = display.offsetHeight
var iwidth = rect.offsetWidth
var iheight = rect.offsetHeight
if (pos.left < 0) pos.left = -10
if (pos.top < 0) pos.top = -10
if (pos.left + iwidth >= owidth) pos.left = owidth - iwidth
if (pos.top + iheight >= oheight) pos.top = oheight - iheight
rect.setAttribute("data", owidth + ":" + oheight)
rect.style.left = pos.left + "px";
rect.style.top = pos.top + "px"
}
let sens = setInterval(loop, 1000 / 60)
//Parameter Sensibility
let param = document.getElementById("parameters")
let b2 = document.getElementById("body2")
document.getElementById("general").addEventListener("click", function() {
param.style.display = "block";
b2.style.display = "none"
})
function validateSens() {
b2.style.display = "block";
param.style.display = "none";
clearInterval(sens)
let sensibilty = parseFloat(document.getElementById("sensibilty").value)
switch (sensibilty) {
case 1:
sens = setInterval(loop, 1000 / 40);
break;
case 2:
sens = setInterval(loop, 1000 / 60);
break;
case 3:
sens = setInterval(loop, 1000 / 80);
break;
default:
alert("Sorry, a bug occured")
}
}
//Fireball script
function fFireball(offset) {
return Math.floor(Math.random() * (window.innerWidth - offset))
}
let fireball = {
x: fFireball(fireballElement.offsetWidth),
y: 0
}
const fireLoop = function() {
fireball.y += 2;
fireballElement.style.top = fireball.y + 'px'
if (fireball.y > window.innerHeight) {
fireball.x = fFireball(fireballElement.offsetWidth)
fireballElement.style.left = fireball.x + 'px';
fireball.y = 0;
fireballElement.setAttribute('hit', false );
}
}
fireballElement.style.left = fireball.x + 'px'
let fireInterval = setInterval(fireLoop, 1000 / 100)
function checkCollision() {
var elem = document.getElementById("icon");
var elem2 = document.getElementById("fireball");
if (detectOverlap(elem, elem2) && elem2.getAttribute('hit')=='false' ){
hits++; // detect hit
elem2.setAttribute('hit', true);
aler("hi")
}
setTimeout( checkCollision, 20);
}
// detect fn
var detectOverlap = (function() {
function getPositions(elem) {
var pos = elem.getBoundingClientRect();
return [
[pos.left, pos.right],
[pos.top, pos.bottom]
];
}
function comparePositions(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];
}
return function(a, b) {
var pos1 = getPositions(a),
pos2 = getPositions(b);
return comparePositions(pos1[0], pos2[0]) && comparePositions(pos1[1], pos2[1]);
};
})();
body {
user-select: none;
margin: 0px;
height: 100vh;
padding: 0;
width: 100%;
background-image: url(Photo/bg.jpg);
background-repeat: no-repeat;
background-attachment: fixed;
animation: intro-fade 3s;
background-size: cover;
overflow: hidden;
}
#askName {
display: block;
z-index: 1;
margin-left: auto;
margin-top: 12%;
margin-right: auto;
width: 400px;
text-align: center;
background-color: #737373;
opacity: 0.8;
border-radius: 15px;
padding: 40px 50px 40px 50px;
}
#askName:hover {
opacity: 0.9
}
#askName>label {
text-align: center;
font-size: 150%;
font-weight: lighter;
text-align: center;
}
#askName>input {
display: block;
font-size: 100%;
margin: 30px auto 20px auto;
border: none;
border-radius: 10px;
padding: 10px 20px 10px 20px;
}
#askName>button {
background-color: #e6e6e6;
cursor: pointer;
padding: 10px 20px 10px 20px;
border: none;
border-radius: 10px;
}
#count {
margin-top: 13%;
animation: count-down 16s;
font-weight: lighter;
font-family: cursive;
text-align: center;
color: black;
font-size: 200px;
}
h6 {
margin-right: 20px;
padding-top: 5px;
font-weight: normal;
font-family: sans-serif;
margin-top: 0px;
color: white;
text-align: center;
font-size: 190%;
cursor: default;
}
h6:hover {
font-size: 210%
}
#icon {
position: absolute;
top: 0;
left: 0;
cursor: none;
width: 9%;
}
#general {
position: absolute;
cursor: pointer;
min-width: 4%;
top: 10px;
width: 4%;
right: 10px;
}
#general:hover {
transform: rotate(-100deg)
}
#parameters {
text-align: center;
display: none;
animation: intro-fade 3s;
height: auto;
border: none;
background-color: #d9d9d9;
color: black;
position: absolute;
padding: 0px 60px 20px 60px;
left: 50%;
width: auto;
min-width: 200px;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 13px;
}
h3 {
color: black;
font-weight: normal;
font-size: 150%;
}
#sensibilty {
display: block;
margin-right: auto;
margin-left: auto;
}
#validateSens {
margin-top: 20px;
border: none;
padding: 10px;
border-radius: 5px;
cursor: pointer;
}
#keyframes intro-fade {
from {
opacity: 0
}
to {
opacity: 1
}
}
#keyframes count-down {
from {
transform: scale(0)
}
to {
transform: scale(1)
}
}
<body id="body" onload="stayOnFocus()">
<img src="https://placehold.it/350x350" id="general">
<section id="parameters">
<h3> Choose your sensibility </h3>
<input type="range" id="sensibilty" min="1" max="3" value="2">
<button id="validateSens" onclick="validateSens()"> Submit </button>
</section>
<main id="body2">
<form id="askName" title="Write your name"> <label> Enter your username: </label>
<input id="input" type="text" maxlength="10" autofocus>
<button type="button" onclick="countDown(); return Username()" id="begin-timer"> Submit </button>
</form>
<h6 id="name"></h6>
<h2 id="count"></h2>
<img src="https://placehold.it/50x52" id="fireball" style="display:none; width:3%; position:absolute; cursor:none">
<img src="https://placehold.it/80x40" id="icon" style="display:none">
</main>

Related

Detecting the collision of 2 divs with 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>

Vue - Convert html audio player into component

I'm converting a player in html into a Vue component.
Half of the component is already created, only the time control slider is missing.
Here is the html player code (Lines with multiple tabs are already implemented in the Vue component):
var audioPlayer = document.querySelector('.green-audio-player');
var playPause = audioPlayer.querySelector('#playPause');
var playpauseBtn = audioPlayer.querySelector('.play-pause-btn');
var loading = audioPlayer.querySelector('.loading');
var progress = audioPlayer.querySelector('.progress');
var sliders = audioPlayer.querySelectorAll('.slider');
var player = audioPlayer.querySelector('audio');
var currentTime = audioPlayer.querySelector('.current-time');
var totalTime = audioPlayer.querySelector('.total-time');
var speaker = audioPlayer.querySelector('#speaker');
var draggableClasses = ['pin'];
var currentlyDragged = null;
window.addEventListener('mousedown', function(event) {
if(!isDraggable(event.target)) return false;
currentlyDragged = event.target;
let handleMethod = currentlyDragged.dataset.method;
this.addEventListener('mousemove', window[handleMethod], false);
window.addEventListener('mouseup', () => {
currentlyDragged = false;
window.removeEventListener('mousemove', window[handleMethod], false);
}, false);
});
playpauseBtn.addEventListener('click', togglePlay);
player.addEventListener('timeupdate', updateProgress);
player.addEventListener('loadedmetadata', () => {
totalTime.textContent = formatTime(player.duration);
});
player.addEventListener('canplay', makePlay);
player.addEventListener('ended', function(){
playPause.attributes.d.value = "M18 12L0 24V0";
player.currentTime = 0;
});
sliders.forEach(slider => {
let pin = slider.querySelector('.pin');
slider.addEventListener('click', window[pin.dataset.method]);
});
function isDraggable(el) {
let canDrag = false;
let classes = Array.from(el.classList);
draggableClasses.forEach(draggable => {
if(classes.indexOf(draggable) !== -1)
canDrag = true;
})
return canDrag;
}
function inRange(event) {
let rangeBox = getRangeBox(event);
let rect = rangeBox.getBoundingClientRect();
let direction = rangeBox.dataset.direction;
if(direction == 'horizontal') {
var min = rangeBox.offsetLeft;
var max = min + rangeBox.offsetWidth;
if(event.clientX < min || event.clientX > max) return false;
} else {
var min = rect.top;
var max = min + rangeBox.offsetHeight;
if(event.clientY < min || event.clientY > max) return false;
}
return true;
}
function updateProgress() {
var current = player.currentTime;
var percent = (current / player.duration) * 100;
progress.style.width = percent + '%';
currentTime.textContent = formatTime(current);
}
function getRangeBox(event) {
let rangeBox = event.target;
let el = currentlyDragged;
if(event.type == 'click' && isDraggable(event.target)) {
rangeBox = event.target.parentElement.parentElement;
}
if(event.type == 'mousemove') {
rangeBox = el.parentElement.parentElement;
}
return rangeBox;
}
function getCoefficient(event) {
let slider = getRangeBox(event);
let rect = slider.getBoundingClientRect();
let K = 0;
if(slider.dataset.direction == 'horizontal') {
let offsetX = event.clientX - slider.offsetLeft;
let width = slider.clientWidth;
K = offsetX / width;
} else if(slider.dataset.direction == 'vertical') {
let height = slider.clientHeight;
var offsetY = event.clientY - rect.top;
K = 1 - offsetY / height;
}
return K;
}
function rewind(event) {
if(inRange(event)) {
player.currentTime = player.duration * getCoefficient(event);
}
}
function formatTime(time) {
var min = Math.floor(time / 60);
var sec = Math.floor(time % 60);
return min + ':' + ((sec<10) ? ('0' + sec) : sec);
}
function togglePlay() {
if(player.paused) {
playPause.attributes.d.value = "M0 0h6v24H0zM12 0h6v24h-6z";
player.play();
} else {
playPause.attributes.d.value = "M18 12L0 24V0";
player.pause();
}
}
function makePlay() {
playpauseBtn.style.display = 'block';
loading.style.display = 'none';
}
.audio.green-audio-player {
width: 400px;
min-width: 300px;
height: 56px;
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.07);
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 24px;
padding-right: 24px;
border-radius: 4px;
user-select: none;
-webkit-user-select: none;
background-color: #fff;
}
.audio.green-audio-player .play-pause-btn {
display: none;
cursor: pointer;
}
.audio.green-audio-player .spinner {
width: 18px;
height: 18px;
background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/355309/loading.png);
background-size: cover;
background-repeat: no-repeat;
animation: spin 0.4s linear infinite;
}
.audio.green-audio-player .slider {
flex-grow: 1;
background-color: #D8D8D8;
cursor: pointer;
position: relative;
}
.audio.green-audio-player .slider .progress {
background-color: #44BFA3;
border-radius: inherit;
position: absolute;
pointer-events: none;
}
.audio.green-audio-player .slider .progress .pin {
height: 16px;
width: 16px;
border-radius: 8px;
background-color: #44BFA3;
position: absolute;
pointer-events: all;
box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.32);
}
.audio.green-audio-player .controls {
font-family: 'Roboto', sans-serif;
font-size: 16px;
line-height: 18px;
color: #55606E;
display: flex;
flex-grow: 1;
justify-content: space-between;
align-items: center;
margin-left: 24px;
}
.audio.green-audio-player .controls .slider {
margin-left: 16px;
margin-right: 16px;
border-radius: 2px;
height: 4px;
}
.audio.green-audio-player .controls .slider .progress {
width: 0;
height: 100%;
}
.audio.green-audio-player .controls .slider .progress .pin {
right: -8px;
top: -6px;
}
.audio.green-audio-player .controls span {
cursor: default;
}
svg, img {
display: block;
}
#keyframes spin {
from {
transform: rotateZ(0);
}
to {
transform: rotateZ(1turn);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div class="audio green-audio-player">
<div class="loading">
<div class="spinner"></div>
</div>
<div class="play-pause-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="24" viewBox="0 0 18 24">
<path fill="#566574" fill-rule="evenodd" d="M18 12L0 24V0" class="play-pause-icon" id="playPause"/>
</svg>
</div>
<div class="controls">
<span class="current-time">0:00</span>
<div class="slider" data-direction="horizontal">
<div class="progress">
<div class="pin" id="progress-pin" data-method="rewind"></div>
</div>
</div>
<span class="total-time">0:00</span>
</div>
<audio>
<source src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/355309/Swing_Jazz_Drum.mp3" type="audio/mpeg">
</audio>
</div>
Html Codepen: https://codepen.io/caiokawasaki/pen/JwVwry
Here is the Vue component:
Vue.component('audio-player', {
props: ['message'],
data: () => ({
audio: undefined,
loaded: false,
playing: false,
currentTime: '00:00',
totalTime: '00:00',
percent: '0%',
draggableClasses: ['pin'],
currentlyDragged: null
}),
computed: {},
methods: {
formatTime(time) {
var min = Math.floor(time / 60);
var sec = Math.floor(time % 60);
return min + ':' + ((sec < 10) ? ('0' + sec) : sec);
},
loadedMetaData() {
this.totalTime = this.formatTime(this.audio.duration)
},
canPlay() {
this.loaded = true
},
timeUpdate(){
var current = this.audio.currentTime;
var percent = (current / this.audio.duration) * 100;
this.percent = percent + '%';
this.currentTime = this.formatTime(current);
},
ended(){
this.playing = false
this.audio.currentTime = 0
},
isDraggable(el) {
let canDrag = false;
let classes = Array.from(el.classList);
this.draggableClasses.forEach(draggable => {
if (classes.indexOf(draggable) !== -1)
canDrag = true;
})
return canDrag;
},
inRange(event) {
let rangeBox = getRangeBox(event);
let rect = rangeBox.getBoundingClientRect();
let direction = rangeBox.dataset.direction;
if (direction == 'horizontal') {
var min = rangeBox.offsetLeft;
var max = min + rangeBox.offsetWidth;
if (event.clientX < min || event.clientX > max) return false;
} else {
var min = rect.top;
var max = min + rangeBox.offsetHeight;
if (event.clientY < min || event.clientY > max) return false;
}
return true;
},
togglePlay() {
if (this.audio.paused) {
this.audio.play();
this.playing = true;
} else {
this.audio.pause();
this.playing = false;
}
},
makePlay() {
playpauseBtn.style.display = 'block';
loading.style.display = 'none';
},
getRangeBox(event) {
let rangeBox = event.target;
let el = currentlyDragged;
if (event.type == 'click' && isDraggable(event.target)) {
rangeBox = event.target.parentElement.parentElement;
}
if (event.type == 'mousemove') {
rangeBox = el.parentElement.parentElement;
}
return rangeBox;
},
getCoefficient(event) {
let slider = getRangeBox(event);
let rect = slider.getBoundingClientRect();
let K = 0;
if (slider.dataset.direction == 'horizontal') {
let offsetX = event.clientX - slider.offsetLeft;
let width = slider.clientWidth;
K = offsetX / width;
} else if (slider.dataset.direction == 'vertical') {
let height = slider.clientHeight;
var offsetY = event.clientY - rect.top;
K = 1 - offsetY / height;
}
return K;
},
rewind(event) {
if (this.inRange(event)) {
this.audio.currentTime = this.audio.duration * getCoefficient(event);
}
}
},
mounted() {
this.audio = this.$refs.audio
},
template: `<div class="audio-message-content">
<a v-if="loaded" class="play-pause-btn" href="#" :title="playing ? 'Clique aqui para pausar o audio' : 'Clique aqui ouvir o audio'" #click.prevent="togglePlay">
<svg key="pause" v-if="playing" x="0px" y="0px" viewBox="0 0 18 20" style="width: 18px; height: 20px; margin-top: -10px">
<path d="M17.1,20c0.49,0,0.9-0.43,0.9-0.96V0.96C18,0.43,17.6,0,17.1,0h-5.39c-0.49,0-0.9,0.43-0.9,0.96v18.07c0,0.53,0.4,0.96,0.9,0.96H17.1z M17.1,20"/>
<path d="M6.29,20c0.49,0,0.9-0.43,0.9-0.96V0.96C7.19,0.43,6.78,0,6.29,0H0.9C0.4,0,0,0.43,0,0.96v18.07C0,19.57,0.4,20,0.9,20H6.29z M6.29,20"/>
</svg>
<svg key="play" v-else x="0px" y="0px" viewBox="0 0 18 22" style="width: 18px; height: 22px; margin-top: -11px">
<path d="M17.45,10.01L1.61,0.14c-0.65-0.4-1.46,0.11-1.46,0.91V20.8c0,0.81,0.81,1.32,1.46,0.91l15.84-9.87C18.1,11.43,18.1,10.41,17.45,10.01L17.45,10.01z M17.45,10.01"/>
</svg>
</a>
<div v-else class="loading">
<div class="spinner"></div>
</div>
<div class="controls">
<span class="current-time">{{ currentTime }}</span>
<div class="slider" data-direction="horizontal" #click="">
<div class="progress" :style="{width: percent}">
<div class="pin" id="progress-pin" data-method="rewind"></div>
</div>
</div>
<span class="total-time">{{ totalTime }}</span>
</div>
<audio ref="audio" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/355309/Swing_Jazz_Drum.mp3" #loadedmetadata="loadedMetaData" #canplay="canPlay" #timeupdate="timeUpdate" #ended="ended"></audio>
</div>`
})
var app = new Vue({
el: '#app'
})
.audio-message-content {
width: 400px;
min-width: 300px;
height: 56px;
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.07);
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 24px;
padding-right: 24px;
border-radius: 4px;
user-select: none;
-webkit-user-select: none;
background-color: #fff;
}
.audio-message-content .play-pause-btn {
position: relative;
width: 18px;
height: 22px;
cursor: pointer;
}
.audio-message-content .play-pause-btn svg {
display: block;
position: absolute;
top: 50%;
left: 50%;
margin-left: -9px;
}
.audio-message-content .spinner {
width: 18px;
height: 18px;
background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/355309/loading.png);
background-size: cover;
background-repeat: no-repeat;
animation: spin 0.4s linear infinite;
}
.audio-message-content .slider {
flex-grow: 1;
background-color: #D8D8D8;
cursor: pointer;
position: relative;
}
.audio-message-content .slider .progress {
background-color: #44BFA3;
border-radius: inherit;
position: absolute;
pointer-events: none;
}
.audio-message-content .slider .progress .pin {
height: 16px;
width: 16px;
border-radius: 8px;
background-color: #44BFA3;
position: absolute;
pointer-events: all;
box-shadow: 0px 1px 1px 0px rgba(0, 0, 0, 0.32);
}
.audio-message-content .controls {
font-family: 'Roboto', sans-serif;
font-size: 16px;
line-height: 18px;
color: #55606E;
display: flex;
flex-grow: 1;
justify-content: space-between;
align-items: center;
margin-left: 24px;
}
.audio-message-content .controls .slider {
margin-left: 16px;
margin-right: 16px;
border-radius: 2px;
height: 4px;
}
.audio-message-content .controls .slider .progress {
width: 0;
height: 100%;
}
.audio-message-content .controls .slider .progress .pin {
right: -8px;
top: -6px;
}
.audio-message-content .controls span {
cursor: default;
}
svg, img {
display: block;
}
#keyframes spin {
from {
transform: rotateZ(0);
}
to {
transform: rotateZ(1turn);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<audio-player></audio-player>
</div>
Vue Component Codepen: https://codepen.io/caiokawasaki/pen/QzRMwz
Functions like the following I could not understand nor find anything on the internet:
window[handleMethod]
window[pin.dataset.method]
Can anyone help me finalize this component?
Edit
I've converted all of the html and javascript into a Vue component but anyway it still is not working properly.
The only thing that is not working properly is the progress bar. It needs to perform two functions:
Clicking it should go to the desired time.
When clicking on the pin and drag it should go to the desired time.
I use Vue Cli, neither of the above two work in the form of .vue files, but in Codepen normally written only function 2 works.
Codepen: https://codepen.io/caiokawasaki/pen/VqOqBQ
The function: window[handleMethod] is executed by deriving the name of the method off of the data- property from the pin element:
<div class="pin" id="progress-pin" data-method="rewind"></div>
So window[handleMethod] is equivalent to window.rewind()
The same is true for window[pin.dataset.method].
So in your case:
this[handleMethod](event)
and:
this[pin.dataset.method](event)
Should be suitable replacements.

Problem with arrow position - range input

I found an input range script on the Internet that meets my expectations.
I began to process it under myself. And everything works ok except the cloud position under the dot. I tried different ways but still the script does not work as it should. I would like the triangle from the cloud to be always under the dot regardless of the position.
$.fn.WBslider = function () {
return this.each(function () {
var $_this = $(this),
$_date = $('input', $_this),
$_title = $('.setyear', $_this),
thumbwidth = 85, // set this to the pixel width of the thumb
yrnow = 200;
$_date.on('input change keyup', function () {
var $_this = $(this),
val = parseInt($_date.val(), 10);
if (val < 70) {
val = '< 70';
}
if (val === '') { // Stop IE8 displaying NaN
val = 0;
}
$_title.text(val);
var pos = (val - $_date.attr('min')) / ($_date.attr('max') - $_date.attr('min'));
// position the title with the thumb
var thumbCorrect = thumbwidth * (pos - 0.5) * -1,
titlepos = Math.round((pos * $_date.width()) - thumbwidth / 4 + thumbCorrect);
$_title.css({
'left': titlepos
});
// show "progress" on the track
pos = Math.round(pos * 99); // to hide stuff behide the thumb
var grad = 'linear-gradient(90deg, #fb824f ' + pos + '%,#e2e5ec ' + (pos + 1) + '%)';
$_date.css({
'background': grad
});
}).on('focus', function () {
if (isNaN($(this).val())) {
$(this).val(0);
}
}).trigger('change');
$(window).on('resize', function () {
$_date.trigger('change');
});
});
};
$(function () {
$('.slider').WBslider();
});
.startyear,
.endyear {
float: left;
color: #333;
text-align: right;
font-weight: bold;
}
.endyear {
text-align: left;
}
.setyear {
position: absolute;
bottom: -65px;
left: 50%;
text-align: center;
font-weight: bold;
white-space: nowrap;
min-width: 85px;
box-shadow: 1px 3px 5px #d5d7db;
border-radius: 5px;
border: 1px solid #e4e7eb;
background-color: #ffffff;
padding: 10px 25px;
color: #494e53;
font-size: 12px;
font-weight: 600;
}
.setyear:before {
position: absolute;
margin-top: -21px;
content: URL("https://image.ibb.co/eSib69/range_arrow.png");
background-color: transparent;
}
.range {
position: relative;
float: left;
padding: 0 0.9375rem;
}
input[type=range] {
-webkit-appearance: none;
display: block;
height: 4px;
padding: 0;
background: #FFE014;
box-sizing: content-box;
border-right: 0 !important;
}
input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border: 7px solid #fff;
border-radius: 25px;
background: #fb824f;
box-shadow: 0 2px 9px rgba(185, 185, 185, 0.75);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<div class="slider">
<span class="startyear">max. 250 kg</span>
<div class="range">
<input type="range" name="date" id="date1" min="70" max="200" step="1" value="Please enter the year of build" required>
<span class="setyear"></span>
</div>
<span class="endyear">max. 250 kg</span>
</div>
</form>
I tried to find out the position of type range via linear gradient. It might not be accurate but to an extent, it does the work.
$.fn.WBslider = function() {
return this.each(function() {
var $_this = $(this),
$_date = $('input', $_this),
$_title = $('.setyear', $_this),
thumbwidth = 85, // set this to the pixel width of the thumb
yrnow = 200;
$_date.on('input change keyup', function() {
var $_this = $(this),
val = parseInt($_date.val(), 10);
if (val < 70) {
val = '< 70';
}
if (val === '') { // Stop IE8 displaying NaN
val = 0;
}
$_title.text(val);
var pos = (val - $_date.attr('min')) / ($_date.attr('max') - $_date.attr('min'));
// position the title with the thumb
var thumbCorrect = thumbwidth * (pos - 0.5) * -1,
titlepos = Math.round((pos * $_date.width()) - thumbwidth / 4 + thumbCorrect);
if ($("#date1").attr("style") != undefined) {
titlepos = 23 + parseFloat($("#date1").attr("style").split(" ").pop().split("%")[0]);
} else {
titlepos = 70;
}
$_title.css({
'left': titlepos
});
// show "progress" on the track
pos = Math.round(pos * 99); // to hide stuff behide the thumb
var grad = 'linear-gradient(90deg, #fb824f ' + pos + '%,#e2e5ec ' + (pos + 1) + '%)';
$_date.css({
'background': grad
});
}).on('focus', function() {
if (isNaN($(this).val())) {
$(this).val(0);
}
}).trigger('change');
$(window).on('resize', function() {
$_date.trigger('change');
});
});
};
$(function() {
$('.slider').WBslider();
});
https://jsfiddle.net/4Lqmw1sz/4/

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!

HTML5 page with a drag magnifier but it does not work in mobile device

It works fine in desktop browsers but if I use in iPad/Android/any mobile device, then it will not drag. I already use jQuery touch punch.
var currentSlide = 8;
var onMouseDown = function(event) {
var elements = (allElementsFromPoint(event.pageX, event.pageY));
for (var i = 0; i < elements.length; i++) {
if ($(elements[i]).hasClass("slide-canvas-layer")) {
if (tool == "pencil" || tool == "brush") {
canvas.addEventListener('mousemove', onPaint, false);
}
}
}
};
var onMouseUp = function(event) {
var elements = (allElementsFromPoint(event.pageX, event.pageY));
for (var i = 0; i < elements.length; i++) {
if ($(elements[i]).hasClass("slide-canvas-layer")) {
canvas.removeEventListener('mousemove', onPaint, false);
}
}
};
var onTouchStart = function(event) {
var elements = (allElementsFromPoint(event.touches[0].pageX, event.touches[0].pageY));
for (var i = 0; i < elements.length; i++) {
if ($(elements[i]).hasClass("slide-canvas-layer")) {
if (tool == "pencil" || tool == "brush") {
canvas.addEventListener('touchmove', onPaint, false);
}
}
}
};
var onTouchEnd = function(event) {
var elements = (allElementsFromPoint(event.changedTouches[0].pageX, event.changedTouches[0].pageY));
for (var i = 0; i < elements.length; i++) {
if ($(elements[i]).hasClass("slide-canvas-layer")) {
canvas.removeEventListener('mousemove', onPaint, false);
canvas.removeEventListener('touchmove', onPaint, false);
}
}
};
window.onload = function() {
$(".menu-left-tool-box-button").bind('click', function(e) {
tool = $(this).val();
setTool(this);
});
$(".coversheet").draggable({
containment: "parent",
scroll: false
});
$(".coversheet").find(".coversheet-plus-minus-button").bind('click', function(e) {
switch ($(this).val()) {
case "+":
$(this).parent().parent().css({
width: ($(this).parent().parent().width() + 50) + "px",
height: ($(this).parent().parent().height() + 50) + "px"
});
break;
case "-":
if ($(this).parent().parent().width() > 100) {
$(this).parent().parent().css({
width: ($(this).parent().parent().width() - 50) + "px",
height: ($(this).parent().parent().height() - 50) + "px"
});
}
break;
}
});
$(".menu-left-tool-box-button-child-pencil").bind('click', function(e) {
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(this).addClass("menu-left-tool-box-button-child-pencil-active");
ctx.lineWidth = parseInt($(this).attr("value"));
ctx.strokeStyle = "#000";
ctx.globalAlpha = 1;
octx.lineWidth = parseInt($(this).attr("value"));
octx.strokeStyle = "#000";
octx.globalAlpha = 1;
tool = "pencil";
setTool($(".menu-left-tool-box-button[value=pencil]"));
});
$(".menu-left-tool-box-button-child-brush").bind('click', function(e) {
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
$(this).addClass("menu-left-tool-box-button-child-brush-active");
$(".menu-left-tool-box-button[value=brush]").css({
backgroundImage: 'url(./images/' + $(this).attr("color") + '_paint.png)'
});
ctx.strokeStyle = $(this).attr("value");
ctx.globalAlpha = 1;
ctx.lineWidth = 20;
octx.strokeStyle = $(this).attr("value");
octx.globalAlpha = 1;
octx.lineWidth = 20;
tool = "brush";
setTool($(".menu-left-tool-box-button[value=brush]"));
});
$(".menu-right-button").bind('click', function(e) {
switch ($(this).val()) {
case "checkAnswer":
var total = $(".cell-clickable").length;
var right = 0;
$(".cell-clickable").each(function() {
switch ($(this).attr("value")) {
case "cell-clickable-dot1":
if ($(this).hasClass("cell-clickable-dot1")) {
right++;
$(this).find(".cell-clickable-button").removeClass("true-image").removeClass("false-image").addClass("true-image").removeClass("hide");
} else {
if ($(this).hasClass("cell-clickable-dot2")) {
$(this).find(".cell-clickable-button").removeClass("true-image").removeClass("false-image").addClass("false-image").removeClass("hide");
}
}
break;
case "cell-clickable-dot2":
if ($(this).hasClass("cell-clickable-dot2")) {
right++;
$(this).find(".cell-clickable-button").removeClass("true-image").removeClass("false-image").addClass("true-image").removeClass("hide");
} else {
if ($(this).hasClass("cell-clickable-dot1")) {
$(this).find(".cell-clickable-button").removeClass("true-image").removeClass("false-image").addClass("false-image").removeClass("hide");
}
}
break;
}
if (total == right) {
$(".cell-clickable-dot1").css({
backgroundColor: "rgba(61, 203, 27,0.5)",
boxShadow: "0px 0px 0px 5px rgb(61, 203, 27)"
});
$(".cell-clickable").find(".cell-clickable-button").addClass("hide");
}
});
}
});
$(".menu-left-preview-box-preview").bind('click', function(e) {
window.location = "page" + ($(this).index() + 1) + ".html";
});
var native_width = 0;
var native_height = 0;
var magnifyIsMouseDown = false;
$(".magnify").parent().mousedown(function(e) {
magnifyIsMouseDown = true;
});
$(".magnify").mousemove(function(e) {
if (isDragging) {
if (!native_width && !native_height) {
var image_object = new Image();
image_object.src = $(".small").attr("src");
native_width = image_object.width;
native_height = image_object.height;
} else {
var magnify_offset = $(this).offset();
var mx = e.pageX - magnify_offset.left;
var my = e.pageY - magnify_offset.top;
if ($(".large").is(":visible")) {
var rx = Math.round(mx / $(".small").width() * native_width - $(".large").width() / 2) * -1;
var ry = Math.round(my / $(".small").height() * native_height - $(".large").height() / 2) * -1;
var bgp = rx + "px " + ry + "px";
var px = mx - $(".large").width() / 2;
var py = my - $(".large").height() / 2;
$(".large").css({
left: px,
top: py,
backgroundPosition: bgp
});
}
}
}
});
var isDragging = false;
$(".magnify").parent().mouseup(function(e) {
isDragging = false;
});
$(".magnify").parent().mousedown(function(e) {
// $(".large").fadeOut(100);
isDragging = true;
});
$(".large").fadeIn(100);
manageSlide();
}
function allElementsFromPoint(x, y) {
var element, elements = [];
var old_visibility = [];
while (true) {
element = document.elementFromPoint(x, y);
if (!element || element === document.documentElement) {
break;
}
elements.push(element);
old_visibility.push(element.style.visibility);
element.style.visibility = 'hidden';
}
for (var k = 0; k < elements.length; k++) {
elements[k].style.visibility = old_visibility[k];
}
elements.reverse();
return elements;
}
function manageSlide() {
$("#slide-number").text(currentSlide);
$(".slide").addClass("hide");
$(".slide").removeClass("hide");
$(".menu-left-preview-box-preview").removeClass("menu-left-preview-box-preview-active");
$(".menu-left-preview-box-preview").eq(currentSlide - 1).addClass("menu-left-preview-box-preview-active");
$(".slide-canvas-layer").addClass("hide");
$(".slide-canvas-layer-opacity").addClass("hide");
tool = null;
setTool();
initCanvas();
}
var zoomScale = 1;
function setTool(sender) {
$(".menu-left-tool-box-button").removeClass("tool-box-button-active");
switch (tool) {
case "pencil":
$(".slide-canvas-layer").removeClass("hide");
$(".slide-canvas-layer-opacity").removeClass("hide");
$(sender).addClass("tool-box-button-active");
var flag = false;
$(".menu-left-tool-box-button-child-pencil").each(function() {
if ($(this).hasClass("menu-left-tool-box-button-child-pencil-active")) {
flag = true;
}
});
if (!flag) {
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-pencil").eq(0).addClass("menu-left-tool-box-button-child-pencil-active");
ctx.lineWidth = parseInt($(".menu-left-tool-box-button-child-pencil").eq(0).attr("value"));
ctx.strokeStyle = "#000";
ctx.globalAlpha = 1;
octx.lineWidth = parseInt($(".menu-left-tool-box-button-child-pencil").eq(0).attr("value"));
octx.strokeStyle = "#000";
octx.globalAlpha = 1;
}
break;
case "brush":
$(".slide-canvas-layer").removeClass("hide");
$(".slide-canvas-layer-opacity").removeClass("hide");
$(sender).addClass("tool-box-button-active");
var flag = false;
$(".menu-left-tool-box-button-child-brush").each(function() {
if ($(this).hasClass("menu-left-tool-box-button-child-brush-active")) {
flag = true;
}
});
if (!flag) {
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
$(".menu-left-tool-box-button-child-brush").eq(0).addClass("menu-left-tool-box-button-child-brush-active");
ctx.strokeStyle = $(".menu-left-tool-box-button-child-brush").eq(0).attr("value");
ctx.globalAlpha = 1;
ctx.lineWidth = 20;
octx.strokeStyle = $(".menu-left-tool-box-button-child-brush").eq(0).attr("value");
octx.globalAlpha = 1;
octx.lineWidth = 20;
}
break;
case "coversheet":
if ($(".slide").find(".coversheet").hasClass("hide")) {
$(".slide").find(".coversheet").removeClass("hide");
} else {
$(".slide").find(".coversheet").addClass("hide");
}
break;
case "back":
if (currentSlide > 1) {
currentSlide--;
window.location = "page" + currentSlide + ".html";
}
break;
case "next":
if (currentSlide < $(".menu-left-preview-box-preview").length) {
currentSlide++;
window.location = "page" + currentSlide + ".html";
}
break;
case "restart":
location.reload();
break;
case "zoom":
zoomScale += 0.10;
$("#slide-content").css({
transform: "scale(" + zoomScale + ")",
webkitTransform: "scale(" + zoomScale + ")",
mozTransform: "scale(" + zoomScale + ")"
});
$(".slide-canvas-layer").addClass("hide");
$(".slide-canvas-layer-opacity").addClass("hide");
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
tool = null;
break;
case "reduse":
if (zoomScale > .20) {
zoomScale -= 0.10;
}
$("#slide-content").css({
transform: "scale(" + zoomScale + ")",
webkitTransform: "scale(" + zoomScale + ")",
mozTransform: "scale(" + zoomScale + ")"
});
$(".slide-canvas-layer").addClass("hide");
$(".slide-canvas-layer-opacity").addClass("hide");
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
tool = null;
break;
default:
$(".slide-canvas-layer").addClass("hide");
$(".slide-canvas-layer-opacity").addClass("hide");
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
tool = null;
break;
}
}
var prevTool = null;
var tool = null;
var cumulativeOffset = function(element) {
var top = 0,
left = 0;
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return {
top: top,
left: left
};
};
var canvas = null,
ctx = null;
var ocanvas = null,
octx = null;
var firstTimeDocumentEvent = false;
var mouse = {
x: 0,
y: 0
};
var last_mouse = {
x: 0,
y: 0
};
function initCanvas() {
document.body.addEventListener('mousedown', onMouseDown, false);
document.body.addEventListener('mouseup', onMouseUp, false);
document.body.addEventListener('touchstart', onTouchStart, false);
document.body.addEventListener('touchend', onTouchEnd, false);
document.body.addEventListener('touchleave', onTouchEnd, false);
document.body.addEventListener('touchcancel', onTouchEnd, false);
canvas = document.querySelector('.slide-canvas-layer');
ctx = canvas.getContext('2d');
ocanvas = document.querySelector('.slide-canvas-layer-opacity');
octx = ocanvas.getContext('2d');
var sketch = document.querySelector('#slide-content');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width')) - 15;
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
ocanvas.width = parseInt(sketch_style.getPropertyValue('width')) - 15;
ocanvas.height = parseInt(sketch_style.getPropertyValue('height'));
$(canvas).css({
marginTop: (-1 * parseInt(sketch_style.getPropertyValue('height'))) + "px"
});
$(ocanvas).css({
marginTop: (-1 * parseInt(sketch_style.getPropertyValue('height'))) + "px"
});
canvas.addEventListener('mousemove', function(e) {
e.preventDefault();
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
var offSet = cumulativeOffset(this);
mouse.x = e.pageX - offSet.left;
mouse.y = e.pageY - offSet.top;
}, false);
ocanvas.addEventListener('mousemove', function(e) {
e.preventDefault();
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
var offSet = cumulativeOffset(this);
mouse.x = e.pageX - offSet.left;
mouse.y = e.pageY - offSet.top;
}, false);
canvas.addEventListener('touchmove', function(e) {
e.preventDefault();
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
var offSet = cumulativeOffset(this);
mouse.x = e.touches[0].pageX - offSet.left;
mouse.y = e.touches[0].pageY - offSet.top;
}, false);
ocanvas.addEventListener('touchmove', function(e) {
e.preventDefault();
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
var offSet = cumulativeOffset(this);
mouse.x = e.touches[0].pageX - offSet.left;
mouse.y = e.touches[0].pageY - offSet.top;
}, false);
ctx.lineWidth = 3;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
octx.lineWidth = 3;
octx.lineJoin = 'round';
octx.lineCap = 'round';
}
var mouseDownOn = null;
var onPaint = function() {
if (tool == "pencil") {
ctx.beginPath();
ctx.moveTo(last_mouse.x, last_mouse.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.closePath();
ctx.stroke();
} else if (tool == "brush") {
octx.beginPath();
octx.moveTo(last_mouse.x, last_mouse.y);
octx.lineTo(mouse.x, mouse.y);
octx.closePath();
octx.stroke();
}
};
.hide {
display: none!important;
}
body {
font-family: Verdana;
}
body > * {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#wrapper {
width: 1000px;
height: 550px;
background-color: #cecbcb;
margin: auto;
}
#menu-left {
height: 100%;
width: 100px;
float: left;
}
#menu-left-tool-box {
height: 60%;
width: 100%;
box-shadow: 0px 1px 0px 0px #b2b0b0;
text-align: center;
}
.menu-left-tool-box-button {
width: 35px;
height: 35px;
background-color: transparent;
margin: 4px;
padding: 0;
outline: none;
border-width: 1px;
background-position: center;
background-repeat: no-repeat;
}
.menu-left-tool-box-button:hover {
transform: scale(1.05);
-webkit-transform: scale(1.05);
-moz-transform: scale(1.05);
cursor: pointer;
}
.tool-box-button-active {
background-color: rgba(30, 182, 18, 0.3);
}
.menu-left-tool-box-button-child-pencil {
width: 10px;
height: 10px;
margin: 0.5px;
padding: 0;
border-width: 1px;
background-size: contain;
float: left;
}
.menu-left-tool-box-button-child-pencil-active {
box-shadow: 0px 0px 0px 1px #000;
}
.menu-left-tool-box-button-child-brush {
width: 10px;
height: 10px;
margin: 0.5px;
padding: 0;
border-width: 1px;
background-size: contain;
float: left;
}
.menu-left-tool-box-button-child-brush-active {
box-shadow: 0px 0px 0px 1px #000;
}
#menu-left-preview-box {
height: 40%;
width: 100%;
overflow-x: hidden;
text-align: center;
overflow-y: auto;
}
.menu-left-preview-box-preview {
width: 38px;
height: 31px;
background-color: transparent;
box-shadow: 0px 0px 1px 0px #b2b0b0;
border: none;
outline: none;
}
.menu-left-preview-box-preview-active {
box-shadow: 0px 0px 1px 1px #13bb0c;
}
#content {
height: 100%;
width: 850px;
background-color: #d4a1a5;
box-shadow: -1px 0px 0px 0px #b2b0b0;
overflow: auto;
float: left;
}
.slide {
height: 100%;
width: 100%;
background-color: #d4a1a5;
}
.slide-title {
height: 35px;
width: 820px;
padding: 15px;
}
.slide-title-content {
height: 35px;
background-color: red;
border-radius: 20px;
}
.slide-title-content-eye {
height: 25px;
width: 25px;
border-radius: 100px;
margin: 5px;
border-width: 1px;
outline: none;
background-position: center;
background-repeat: no-repeat;
float: left;
}
.slide-title-content-text {
height: 25px;
margin: 5px;
float: left;
color: white;
}
.slide-content {
height: 470px;
width: 100%;
overflow: auto;
}
.false-image {
background-image: url('./images/false.jpg');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.true-image {
background-image: url('./images/true.jpg');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.slide-canvas-layer {
position: fixed;
z-index: 999;
}
.slide-canvas-layer-opacity {
position: fixed;
z-index: 999;
opacity: 0.5;
}
.coversheet {
height: 100px;
width: 100px;
background-color: white;
position: absolute;
z-index: 1000;
}
.coversheet-plus-minus {
background-color: #000;
width: 71px;
height: 50px;
line-height: 50px;
/*position: absolute;*/
right: 0;
}
.coversheet-plus-minus-button {
background-color: transparent;
color: white;
border: none;
margin: 0;
font-size: 18px;
margin: 2px;
padding: 7px;
}
#menu-right {
height: 100%;
width: 50px;
box-shadow: -1px 0px 0px 0px #b2b0b0;
float: right;
}
.menu-right-button {
height: 30px;
width: 30px;
border-radius: 100px;
margin: 10px;
border-width: 1px;
outline: none;
background-position: center;
background-repeat: no-repeat;
background-color: transparent;
}
#menu-right-image {
height: 160px;
width: 100%;
position: relative;
top: 240px;
}
/**********OVERRIDE CSS*/
.ui-state-hover,
.ui-state-active {
background-color: rgba(57, 187, 31, 0.5);
border: none;
}
/***********/
#slide-content {
height: 100%;
width: 100%;
transform-origin: left top;
-webkit-transform-origin: left top;
-moz-transform-origin: left top;
}
.magnify {
position: relative;
background-color: #d4a1a5;
height: 100%;
margin: auto;
}
.large {
width: 133px;
height: 133px;
position: absolute;
border-radius: 100%;
box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.85), 0 0 7px 7px rgba(0, 0, 0, 0.25), inset 0 0 40px 2px rgba(0, 0, 0, 0.25);
background: url('https://lh3.googleusercontent.com/t789bbPUzJzrVs6cDPZNtiDxX9NCEWggBIW1IDCbeJllWjMOXy5ILBBFCYu9UjKJLsmCyubMREZjTZnelGwMyup8nVLP-VeMUg8oF0mCXN7gkcOweuJYVYTuj3Cx4rhuoOy8jvM-v83vrmT0-bn1tJx2YdLraQ9p7X82jL8rz9iLSNUgVv36Lxs9rfKpBGTyM7_8rzBsZonWXmcMTgInDdhL5mWx-J97bPOyJ9XA1bltKNRposrL69pdQW1WOAUWKj-uTT5K5z6GjaLeT-vN7gJSCb0NFUauszdao_Z5GFSyKOHWWjChXY3Q5CKBtg7Ir-uXoi4UFArTlHNPvuXCnJQptWv-L0TtZ2RrKpMgjimPoBKsGFjUxzYcY95kfZjGlm5-7V5Uut_Nw58Rk0bCz1EWUrW8nzDfawdilyJkDMn3dIhqlzJ8NGGZ5-2hwJTUOIyfrIQat7273nAPCkid6553CiLF57RGptiCxonWn5CZbpU4O7E8nudVMLPiYNY-nuupA-GOhh8-v6heBza6ijkGIpyyTFLieNBO9IjFTw=s220-no') no-repeat;
background-color: #d4a1a5;
display: none;
margin: auto;
}
/*To solve overlap bug at the edges during magnification*/
.small {
display: block;
width: 100%;
}
.large-image {
background: url('https://photos-1.dropbox.com/t/2/AAAnwmx1QvCzmWFYW5i8a3u_gOPGCt19gs_lBJOqwCS93A/12/134709958/png/32x32/1/_/1/2/icon_magnify_glass.png/EK_Up2cYgQQgBygH/R1kBQnLrN-RrPOYcREvqvMMrOMI-rfEOs5ZVgV-3Ayo?size=1024x768&size_mode=2') no-repeat;
height: 112%;
width: 114%;
position: absolute;
top: 73%;
left: 70%;
}
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
<div class="slide-content">
<div id="slide-content">
<div class="magnify" style="width: 266px;">
<div class="large" style="display: none; left: 195.615px; top: 79.0139px; background-position: -150px -54px;">
<div class="large-image"></div>
</div>
<img class="small" src="https://lh3.googleusercontent.com/gzHg89Pc4MtSrIeH__viNCTdBDDemmYf5VDG7vnZzRiaqWBKOKEzHH0PtlpLu8vyA27dg0q7-pmxAggQEF3p5JsDCPWjbux1uQJGLDNTSLsB7MVJG_cBWDDZqkdcshbeCfXCC4UCQMe1ogugc1R8zduLOfeONFnbhGObEFmBzBF60E6ff8lN8BBoZSxPGW_p3LvTdC4k49ULEVZR9QlJDo_JuMUIM-kSBtrN6WEtQ0RRJPUnqIrwmWj3ec_37hdSSzdPHmYLaNPTbJ1wF3dY8wYn5sJG6tdxk7qp-gI-q7xcFEK2huR6YwJtKTpx5U6mn2tr8Vs00vuvHf1fzi-_40ZWA3XNKWh8syUbTs-WYob7Xb4mSKCr5P4QTfUtHFwHuREv45j24LRdSe5_8B_YWCnIB6wG5x4PbNUEn70OlYSvVgS_rYEkeVqMcY0hQCJuIivfkBacTYFrTF61obq1ixtYSmkyhp8CV3atCsHFm2JwKIlJBZ5fwwnZICfKDITDt1qYsb64TrhNL7Wg7BHSazQ6Tkr3exVtQZ6yp57PsQ=s220-no">
</div>
</div>
<canvas class="slide-canvas-layer-opacity hide" width="835" height="470" style="margin-top: -470px;"></canvas>
<canvas class="slide-canvas-layer hide" width="835" height="470" style="margin-top: -470px;"></canvas>
</div>
I updated your Jsfiddle (new one here), there were some issues with your code when running on mobile.
1. mousedown/mouseup/mousemove versus touchstart/touchend/touchmove
Changed your mouseup and mousedown functions to be triggered also by touchstart and touchend
Old:
$(".magnify").parent().mouseup(function(e) {
isDragging = false;
});
$(".magnify").parent().mousedown(function(e) {
isDragging = true;
});
New:
$(".magnify").parent().on("mouseup touchend",function(e) {
isDragging = false;
});
$(".magnify").parent().on("mousedown touchstart",function(e) {
isDragging = true;
});
Similarly for mousemove
Old:
$(".magnify").mousemove(function(e) {
New:
$(".magnify").on("mousemove touchmove",function(e) {
2. event's pageX and pageY undefined in mobile
In your code
var mx = e.pageX - magnify_offset.left;
var my = e.pageY - magnify_offset.top;
are NaN on mobile because e.pageX and e.pageY are undefined, so I adapted your code for touch events following this answer to Is there an equivalent to e.PageX position for 'touchstart' event as there is for click event? :
if(e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel'){
var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
mx = touch.pageX - magnify_offset.left;
my = touch.pageY - magnify_offset.top;
}
var currentSlide = 8;
var onMouseDown = function(event) {
var elements = (allElementsFromPoint(event.pageX, event.pageY));
for (var i = 0; i < elements.length; i++) {
if ($(elements[i]).hasClass("slide-canvas-layer")) {
if (tool == "pencil" || tool == "brush") {
canvas.addEventListener('mousemove', onPaint, false);
}
}
}
};
var onMouseUp = function(event) {
var elements = (allElementsFromPoint(event.pageX, event.pageY));
for (var i = 0; i < elements.length; i++) {
if ($(elements[i]).hasClass("slide-canvas-layer")) {
canvas.removeEventListener('mousemove', onPaint, false);
}
}
};
var onTouchStart = function(event) {
var elements = (allElementsFromPoint(event.touches[0].pageX, event.touches[0].pageY));
for (var i = 0; i < elements.length; i++) {
if ($(elements[i]).hasClass("slide-canvas-layer")) {
if (tool == "pencil" || tool == "brush") {
canvas.addEventListener('touchmove', onPaint, false);
}
}
}
};
var onTouchEnd = function(event) {
var elements = (allElementsFromPoint(event.changedTouches[0].pageX, event.changedTouches[0].pageY));
for (var i = 0; i < elements.length; i++) {
if ($(elements[i]).hasClass("slide-canvas-layer")) {
canvas.removeEventListener('mousemove', onPaint, false);
canvas.removeEventListener('touchmove', onPaint, false);
}
}
};
window.onload = function() {
$(".menu-left-tool-box-button").bind('click', function(e) {
tool = $(this).val();
setTool(this);
});
$(".coversheet").draggable({
containment: "parent",
scroll: false
});
$(".coversheet").find(".coversheet-plus-minus-button").bind('click', function(e) {
switch ($(this).val()) {
case "+":
$(this).parent().parent().css({
width: ($(this).parent().parent().width() + 50) + "px",
height: ($(this).parent().parent().height() + 50) + "px"
});
break;
case "-":
if ($(this).parent().parent().width() > 100) {
$(this).parent().parent().css({
width: ($(this).parent().parent().width() - 50) + "px",
height: ($(this).parent().parent().height() - 50) + "px"
});
}
break;
}
});
$(".menu-left-tool-box-button-child-pencil").bind('click', function(e) {
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(this).addClass("menu-left-tool-box-button-child-pencil-active");
ctx.lineWidth = parseInt($(this).attr("value"));
ctx.strokeStyle = "#000";
ctx.globalAlpha = 1;
octx.lineWidth = parseInt($(this).attr("value"));
octx.strokeStyle = "#000";
octx.globalAlpha = 1;
tool = "pencil";
setTool($(".menu-left-tool-box-button[value=pencil]"));
});
$(".menu-left-tool-box-button-child-brush").bind('click', function(e) {
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
$(this).addClass("menu-left-tool-box-button-child-brush-active");
$(".menu-left-tool-box-button[value=brush]").css({
backgroundImage: 'url(./images/' + $(this).attr("color") + '_paint.png)'
});
ctx.strokeStyle = $(this).attr("value");
ctx.globalAlpha = 1;
ctx.lineWidth = 20;
octx.strokeStyle = $(this).attr("value");
octx.globalAlpha = 1;
octx.lineWidth = 20;
tool = "brush";
setTool($(".menu-left-tool-box-button[value=brush]"));
});
$(".menu-right-button").bind('click', function(e) {
switch ($(this).val()) {
case "checkAnswer":
var total = $(".cell-clickable").length;
var right = 0;
$(".cell-clickable").each(function() {
switch ($(this).attr("value")) {
case "cell-clickable-dot1":
if ($(this).hasClass("cell-clickable-dot1")) {
right++;
$(this).find(".cell-clickable-button").removeClass("true-image").removeClass("false-image").addClass("true-image").removeClass("hide");
} else {
if ($(this).hasClass("cell-clickable-dot2")) {
$(this).find(".cell-clickable-button").removeClass("true-image").removeClass("false-image").addClass("false-image").removeClass("hide");
}
}
break;
case "cell-clickable-dot2":
if ($(this).hasClass("cell-clickable-dot2")) {
right++;
$(this).find(".cell-clickable-button").removeClass("true-image").removeClass("false-image").addClass("true-image").removeClass("hide");
} else {
if ($(this).hasClass("cell-clickable-dot1")) {
$(this).find(".cell-clickable-button").removeClass("true-image").removeClass("false-image").addClass("false-image").removeClass("hide");
}
}
break;
}
if (total == right) {
$(".cell-clickable-dot1").css({
backgroundColor: "rgba(61, 203, 27,0.5)",
boxShadow: "0px 0px 0px 5px rgb(61, 203, 27)"
});
$(".cell-clickable").find(".cell-clickable-button").addClass("hide");
}
});
}
});
$(".menu-left-preview-box-preview").bind('click', function(e) {
window.location = "page" + ($(this).index() + 1) + ".html";
});
var native_width = 0;
var native_height = 0;
var magnifyIsMouseDown = false;
$(".magnify").parent().mousedown(function(e) {
magnifyIsMouseDown = true;
});
$(".magnify").on("mousemove touchmove",function(e) {
if (isDragging) {
if (!native_width && !native_height) {
var image_object = new Image();
image_object.src = $(".small").attr("src");
native_width = image_object.width;
native_height = image_object.height;
} else {
var magnify_offset = $(this).offset();
var mx = e.pageX - magnify_offset.left;
var my = e.pageY - magnify_offset.top;
if (e.type == 'touchstart' || e.type == 'touchmove' || e.type == 'touchend' || e.type == 'touchcancel') {
var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
mx = touch.pageX - magnify_offset.left;
my = touch.pageY - magnify_offset.top;
}
if ($(".large").is(":visible")) {
var rx = Math.round(mx / $(".small").width() * native_width - $(".large").width() / 2) * -1;
var ry = Math.round(my / $(".small").height() * native_height - $(".large").height() / 2) * -1;
var bgp = rx + "px " + ry + "px";
var px = mx - $(".large").width() / 2;
var py = my - $(".large").height() / 2;
$(".large").css({
left: px,
top: py,
backgroundPosition: bgp
});
}
}
}
});
var isDragging = false;
$(".magnify").parent().on("mouseup touchend", function(e) {
isDragging = false;
});
$(".magnify").parent().on("mousedown touchstart", function(e) {
console.log("mousedown");
// $(".large").fadeOut(100);
isDragging = true;
});
$(".large").fadeIn(100);
manageSlide();
}
function allElementsFromPoint(x, y) {
var element, elements = [];
var old_visibility = [];
while (true) {
element = document.elementFromPoint(x, y);
if (!element || element === document.documentElement) {
break;
}
elements.push(element);
old_visibility.push(element.style.visibility);
element.style.visibility = 'hidden';
}
for (var k = 0; k < elements.length; k++) {
elements[k].style.visibility = old_visibility[k];
}
elements.reverse();
return elements;
}
function manageSlide() {
$("#slide-number").text(currentSlide);
$(".slide").addClass("hide");
$(".slide").removeClass("hide");
$(".menu-left-preview-box-preview").removeClass("menu-left-preview-box-preview-active");
$(".menu-left-preview-box-preview").eq(currentSlide - 1).addClass("menu-left-preview-box-preview-active");
$(".slide-canvas-layer").addClass("hide");
$(".slide-canvas-layer-opacity").addClass("hide");
tool = null;
setTool();
initCanvas();
}
var zoomScale = 1;
function setTool(sender) {
$(".menu-left-tool-box-button").removeClass("tool-box-button-active");
switch (tool) {
case "pencil":
$(".slide-canvas-layer").removeClass("hide");
$(".slide-canvas-layer-opacity").removeClass("hide");
$(sender).addClass("tool-box-button-active");
var flag = false;
$(".menu-left-tool-box-button-child-pencil").each(function() {
if ($(this).hasClass("menu-left-tool-box-button-child-pencil-active")) {
flag = true;
}
});
if (!flag) {
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-pencil").eq(0).addClass("menu-left-tool-box-button-child-pencil-active");
ctx.lineWidth = parseInt($(".menu-left-tool-box-button-child-pencil").eq(0).attr("value"));
ctx.strokeStyle = "#000";
ctx.globalAlpha = 1;
octx.lineWidth = parseInt($(".menu-left-tool-box-button-child-pencil").eq(0).attr("value"));
octx.strokeStyle = "#000";
octx.globalAlpha = 1;
}
break;
case "brush":
$(".slide-canvas-layer").removeClass("hide");
$(".slide-canvas-layer-opacity").removeClass("hide");
$(sender).addClass("tool-box-button-active");
var flag = false;
$(".menu-left-tool-box-button-child-brush").each(function() {
if ($(this).hasClass("menu-left-tool-box-button-child-brush-active")) {
flag = true;
}
});
if (!flag) {
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
$(".menu-left-tool-box-button-child-brush").eq(0).addClass("menu-left-tool-box-button-child-brush-active");
ctx.strokeStyle = $(".menu-left-tool-box-button-child-brush").eq(0).attr("value");
ctx.globalAlpha = 1;
ctx.lineWidth = 20;
octx.strokeStyle = $(".menu-left-tool-box-button-child-brush").eq(0).attr("value");
octx.globalAlpha = 1;
octx.lineWidth = 20;
}
break;
case "coversheet":
if ($(".slide").find(".coversheet").hasClass("hide")) {
$(".slide").find(".coversheet").removeClass("hide");
} else {
$(".slide").find(".coversheet").addClass("hide");
}
break;
case "back":
if (currentSlide > 1) {
currentSlide--;
window.location = "page" + currentSlide + ".html";
}
break;
case "next":
if (currentSlide < $(".menu-left-preview-box-preview").length) {
currentSlide++;
window.location = "page" + currentSlide + ".html";
}
break;
case "restart":
location.reload();
break;
case "zoom":
zoomScale += 0.10;
$("#slide-content").css({
transform: "scale(" + zoomScale + ")",
webkitTransform: "scale(" + zoomScale + ")",
mozTransform: "scale(" + zoomScale + ")"
});
$(".slide-canvas-layer").addClass("hide");
$(".slide-canvas-layer-opacity").addClass("hide");
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
tool = null;
break;
case "reduse":
if (zoomScale > .20) {
zoomScale -= 0.10;
}
$("#slide-content").css({
transform: "scale(" + zoomScale + ")",
webkitTransform: "scale(" + zoomScale + ")",
mozTransform: "scale(" + zoomScale + ")"
});
$(".slide-canvas-layer").addClass("hide");
$(".slide-canvas-layer-opacity").addClass("hide");
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
tool = null;
break;
default:
$(".slide-canvas-layer").addClass("hide");
$(".slide-canvas-layer-opacity").addClass("hide");
$(".menu-left-tool-box-button-child-pencil").removeClass("menu-left-tool-box-button-child-pencil-active");
$(".menu-left-tool-box-button-child-brush").removeClass("menu-left-tool-box-button-child-brush-active");
tool = null;
break;
}
}
var prevTool = null;
var tool = null;
var cumulativeOffset = function(element) {
var top = 0,
left = 0;
do {
top += element.offsetTop || 0;
left += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return {
top: top,
left: left
};
};
var canvas = null,
ctx = null;
var ocanvas = null,
octx = null;
var firstTimeDocumentEvent = false;
var mouse = {
x: 0,
y: 0
};
var last_mouse = {
x: 0,
y: 0
};
function initCanvas() {
document.body.addEventListener('mousedown', onMouseDown, false);
document.body.addEventListener('mouseup', onMouseUp, false);
document.body.addEventListener('touchstart', onTouchStart, false);
document.body.addEventListener('touchend', onTouchEnd, false);
document.body.addEventListener('touchleave', onTouchEnd, false);
document.body.addEventListener('touchcancel', onTouchEnd, false);
canvas = document.querySelector('.slide-canvas-layer');
ctx = canvas.getContext('2d');
ocanvas = document.querySelector('.slide-canvas-layer-opacity');
octx = ocanvas.getContext('2d');
var sketch = document.querySelector('#slide-content');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width')) - 15;
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
ocanvas.width = parseInt(sketch_style.getPropertyValue('width')) - 15;
ocanvas.height = parseInt(sketch_style.getPropertyValue('height'));
$(canvas).css({
marginTop: (-1 * parseInt(sketch_style.getPropertyValue('height'))) + "px"
});
$(ocanvas).css({
marginTop: (-1 * parseInt(sketch_style.getPropertyValue('height'))) + "px"
});
canvas.addEventListener('mousemove', function(e) {
e.preventDefault();
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
var offSet = cumulativeOffset(this);
mouse.x = e.pageX - offSet.left;
mouse.y = e.pageY - offSet.top;
}, false);
ocanvas.addEventListener('mousemove', function(e) {
e.preventDefault();
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
var offSet = cumulativeOffset(this);
mouse.x = e.pageX - offSet.left;
mouse.y = e.pageY - offSet.top;
}, false);
canvas.addEventListener('touchmove', function(e) {
e.preventDefault();
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
var offSet = cumulativeOffset(this);
mouse.x = e.touches[0].pageX - offSet.left;
mouse.y = e.touches[0].pageY - offSet.top;
}, false);
ocanvas.addEventListener('touchmove', function(e) {
e.preventDefault();
last_mouse.x = mouse.x;
last_mouse.y = mouse.y;
var offSet = cumulativeOffset(this);
mouse.x = e.touches[0].pageX - offSet.left;
mouse.y = e.touches[0].pageY - offSet.top;
}, false);
ctx.lineWidth = 3;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
octx.lineWidth = 3;
octx.lineJoin = 'round';
octx.lineCap = 'round';
}
var mouseDownOn = null;
var onPaint = function() {
if (tool == "pencil") {
ctx.beginPath();
ctx.moveTo(last_mouse.x, last_mouse.y);
ctx.lineTo(mouse.x, mouse.y);
ctx.closePath();
ctx.stroke();
} else if (tool == "brush") {
octx.beginPath();
octx.moveTo(last_mouse.x, last_mouse.y);
octx.lineTo(mouse.x, mouse.y);
octx.closePath();
octx.stroke();
}
};
.hide {
display: none!important;
}
body {
font-family: Verdana;
}
body > * {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#wrapper {
width: 1000px;
height: 550px;
background-color: #cecbcb;
margin: auto;
}
#menu-left {
height: 100%;
width: 100px;
float: left;
}
#menu-left-tool-box {
height: 60%;
width: 100%;
box-shadow: 0px 1px 0px 0px #b2b0b0;
text-align: center;
}
.menu-left-tool-box-button {
width: 35px;
height: 35px;
background-color: transparent;
margin: 4px;
padding: 0;
outline: none;
border-width: 1px;
background-position: center;
background-repeat: no-repeat;
}
.menu-left-tool-box-button:hover {
transform: scale(1.05);
-webkit-transform: scale(1.05);
-moz-transform: scale(1.05);
cursor: pointer;
}
.tool-box-button-active {
background-color: rgba(30, 182, 18, 0.3);
}
.menu-left-tool-box-button-child-pencil {
width: 10px;
height: 10px;
margin: 0.5px;
padding: 0;
border-width: 1px;
background-size: contain;
float: left;
}
.menu-left-tool-box-button-child-pencil-active {
box-shadow: 0px 0px 0px 1px #000;
}
.menu-left-tool-box-button-child-brush {
width: 10px;
height: 10px;
margin: 0.5px;
padding: 0;
border-width: 1px;
background-size: contain;
float: left;
}
.menu-left-tool-box-button-child-brush-active {
box-shadow: 0px 0px 0px 1px #000;
}
#menu-left-preview-box {
height: 40%;
width: 100%;
overflow-x: hidden;
text-align: center;
overflow-y: auto;
}
.menu-left-preview-box-preview {
width: 38px;
height: 31px;
background-color: transparent;
box-shadow: 0px 0px 1px 0px #b2b0b0;
border: none;
outline: none;
}
.menu-left-preview-box-preview-active {
box-shadow: 0px 0px 1px 1px #13bb0c;
}
#content {
height: 100%;
width: 850px;
background-color: #d4a1a5;
box-shadow: -1px 0px 0px 0px #b2b0b0;
overflow: auto;
float: left;
}
.slide {
height: 100%;
width: 100%;
background-color: #d4a1a5;
}
.slide-title {
height: 35px;
width: 820px;
padding: 15px;
}
.slide-title-content {
height: 35px;
background-color: red;
border-radius: 20px;
}
.slide-title-content-eye {
height: 25px;
width: 25px;
border-radius: 100px;
margin: 5px;
border-width: 1px;
outline: none;
background-position: center;
background-repeat: no-repeat;
float: left;
}
.slide-title-content-text {
height: 25px;
margin: 5px;
float: left;
color: white;
}
.slide-content {
height: 470px;
width: 100%;
overflow: auto;
}
.false-image {
background-image: url('./images/false.jpg');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.true-image {
background-image: url('./images/true.jpg');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.slide-canvas-layer {
position: fixed;
z-index: 999;
}
.slide-canvas-layer-opacity {
position: fixed;
z-index: 999;
opacity: 0.5;
}
.coversheet {
height: 100px;
width: 100px;
background-color: white;
position: absolute;
z-index: 1000;
}
.coversheet-plus-minus {
background-color: #000;
width: 71px;
height: 50px;
line-height: 50px;
/*position: absolute;*/
right: 0;
}
.coversheet-plus-minus-button {
background-color: transparent;
color: white;
border: none;
margin: 0;
font-size: 18px;
margin: 2px;
padding: 7px;
}
#menu-right {
height: 100%;
width: 50px;
box-shadow: -1px 0px 0px 0px #b2b0b0;
float: right;
}
.menu-right-button {
height: 30px;
width: 30px;
border-radius: 100px;
margin: 10px;
border-width: 1px;
outline: none;
background-position: center;
background-repeat: no-repeat;
background-color: transparent;
}
#menu-right-image {
height: 160px;
width: 100%;
position: relative;
top: 240px;
}
/**********OVERRIDE CSS*/
.ui-state-hover,
.ui-state-active {
background-color: rgba(57, 187, 31, 0.5);
border: none;
}
/***********/
#slide-content {
height: 100%;
width: 100%;
transform-origin: left top;
-webkit-transform-origin: left top;
-moz-transform-origin: left top;
}
.magnify {
position: relative;
background-color: #d4a1a5;
height: 100%;
margin: auto;
}
.large {
width: 133px;
height: 133px;
position: absolute;
border-radius: 100%;
box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.85), 0 0 7px 7px rgba(0, 0, 0, 0.25), inset 0 0 40px 2px rgba(0, 0, 0, 0.25);
background: url('https://lh3.googleusercontent.com/t789bbPUzJzrVs6cDPZNtiDxX9NCEWggBIW1IDCbeJllWjMOXy5ILBBFCYu9UjKJLsmCyubMREZjTZnelGwMyup8nVLP-VeMUg8oF0mCXN7gkcOweuJYVYTuj3Cx4rhuoOy8jvM-v83vrmT0-bn1tJx2YdLraQ9p7X82jL8rz9iLSNUgVv36Lxs9rfKpBGTyM7_8rzBsZonWXmcMTgInDdhL5mWx-J97bPOyJ9XA1bltKNRposrL69pdQW1WOAUWKj-uTT5K5z6GjaLeT-vN7gJSCb0NFUauszdao_Z5GFSyKOHWWjChXY3Q5CKBtg7Ir-uXoi4UFArTlHNPvuXCnJQptWv-L0TtZ2RrKpMgjimPoBKsGFjUxzYcY95kfZjGlm5-7V5Uut_Nw58Rk0bCz1EWUrW8nzDfawdilyJkDMn3dIhqlzJ8NGGZ5-2hwJTUOIyfrIQat7273nAPCkid6553CiLF57RGptiCxonWn5CZbpU4O7E8nudVMLPiYNY-nuupA-GOhh8-v6heBza6ijkGIpyyTFLieNBO9IjFTw=s220-no') no-repeat;
background-color: #d4a1a5;
display: none;
margin: auto;
}
/*To solve overlap bug at the edges during magnification*/
.small {
display: block;
width: 100%;
}
.large-image {
background: url('https://photos-1.dropbox.com/t/2/AAAnwmx1QvCzmWFYW5i8a3u_gOPGCt19gs_lBJOqwCS93A/12/134709958/png/32x32/1/_/1/2/icon_magnify_glass.png/EK_Up2cYgQQgBygH/R1kBQnLrN-RrPOYcREvqvMMrOMI-rfEOs5ZVgV-3Ayo?size=1024x768&size_mode=2') no-repeat;
height: 112%;
width: 114%;
position: absolute;
top: 73%;
left: 70%;
}
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js"></script>
<div class="slide-content">
<div id="slide-content">
<div class="magnify" style="width: 266px;">
<div class="large" style="display: none; left: 195.615px; top: 79.0139px; background-position: -150px -54px;">
<div class="large-image"></div>
</div>
<img class="small" src="https://lh3.googleusercontent.com/gzHg89Pc4MtSrIeH__viNCTdBDDemmYf5VDG7vnZzRiaqWBKOKEzHH0PtlpLu8vyA27dg0q7-pmxAggQEF3p5JsDCPWjbux1uQJGLDNTSLsB7MVJG_cBWDDZqkdcshbeCfXCC4UCQMe1ogugc1R8zduLOfeONFnbhGObEFmBzBF60E6ff8lN8BBoZSxPGW_p3LvTdC4k49ULEVZR9QlJDo_JuMUIM-kSBtrN6WEtQ0RRJPUnqIrwmWj3ec_37hdSSzdPHmYLaNPTbJ1wF3dY8wYn5sJG6tdxk7qp-gI-q7xcFEK2huR6YwJtKTpx5U6mn2tr8Vs00vuvHf1fzi-_40ZWA3XNKWh8syUbTs-WYob7Xb4mSKCr5P4QTfUtHFwHuREv45j24LRdSe5_8B_YWCnIB6wG5x4PbNUEn70OlYSvVgS_rYEkeVqMcY0hQCJuIivfkBacTYFrTF61obq1ixtYSmkyhp8CV3atCsHFm2JwKIlJBZ5fwwnZICfKDITDt1qYsb64TrhNL7Wg7BHSazQ6Tkr3exVtQZ6yp57PsQ=s220-no">
</div>
</div>
<canvas class="slide-canvas-layer-opacity hide" width="835" height="470" style="margin-top: -470px;"></canvas>
<canvas class="slide-canvas-layer hide" width="835" height="470" style="margin-top: -470px;"></canvas>
</div>

Categories

Resources