based on this this tutorial on w3 schools I wrote a function(move) that animates an element(div) using the setInterval() method, my problem is when I try to call the function move() several times the div does an unexpected behavior, I tried to use async/await but it didn't work.
async function moveArray(destination) {
//move through array of addresses
for (let i = 0; i < destination.length - 1; i++) {
await move(destination[i], destination[i + 1]);
}
}
async function move(pos, add) {
//moves element from position to address
var elem = document.getElementById("object");
var id = setInterval(frame, 15);
function frame() {
if (pos.x == add.x && pos.y == add.y) {
clearInterval(id);
return;
} else {
//if element haven't reached its target
// we add/substract 1 from the address and update styling
if (pos.x != add.x) {
pos.x += add.x > pos.x ? 1 : -1;
elem.style.left = pos.x + "px";
}
if (pos.y != add.y) {
pos.y += add.y > pos.y ? 1 : -1;
elem.style.top = pos.y + "px";
}
}
}
}
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#object {
width: 50px;
height: 50px;
position: absolute;
background: red;
}
<button onclick="moveArray( [{x:0,y:0}, {x:140, y:150}, {x:130, y:110}, {x:70, y:65}] )">
move
</button>
<div id="container">
<div id="object"></div>
//object to be animated
</div>
The weird behavior is caused because the move function is being called multiple times simultaneously in the for loop. This happens because your async functions do not know when they are done.
Instead of making move async, which it doesn't have to if it is not awaiting anything, return a Promise from the start and call you animation code inside of it. When the point is reached when animation has to end, resolve the promise.
Doing it like this will make your await statement in the for loop wait for the move function to reach the resolve call before continuing with the next animation.
async function moveArray(destination) {
//move through array of addresses
for (let i = 0; i < destination.length - 1; i++) {
await move(destination[i], destination[i + 1]);
}
}
function move(pos, add) {
return new Promise(resolve => {
var elem = document.getElementById("object");
var id = setInterval(frame, 15);
function frame() {
if (pos.x == add.x && pos.y == add.y) {
clearInterval(id);
// Fulfill promise when position is reached.
resolve();
} else {
if (pos.x != add.x) {
pos.x += add.x > pos.x ? 1 : -1;
elem.style.left = pos.x + "px";
}
if (pos.y != add.y) {
pos.y += add.y > pos.y ? 1 : -1;
elem.style.top = pos.y + "px";
}
}
}
});
}
<!DOCTYPE html>
<html>
<head>
<style>
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#object {
width: 50px;
height: 50px;
position: absolute;
background: red;
}
</style>
</head>
<body>
<button onclick="moveArray( [{x:0,y:0}, {x:140, y:150}, {x:130, y:110}, {x:70, y:65}] )">
move
</button>
<div id="container">
<div id="object"></div>
//object to be animated
</div>
<script src="animation.js"></script>
</body>
</html>
Related
Problem is simple but seems I can't get over it, div called ".player" shouldn't be going out from div ".play-ground", it should be moving only in its yellow space. Next thing which I'm trying to achieve is that I want a div same size of the red one, showing up for some time, so that the user can catch it with the red one and after that he gets points otherwise doesn't. Is there any way to do it? And how make div disapear after being catched?
var a = prompt("Provide nick");
while (a === "") {
a = prompt("Provide nick");
}
document.write("<p>Nick: " + a + "</p>");
/* -------- */
var ground = document.getElementsByClassName('play-ground')[0];
var player = document.getElementsByClassName('player')[0];
var points = document.getElementsByClassName('numba')[0];
var thing = document.getElementsByClassName('thing-tocatch')[0];
document.onkeydown = move;
var lefts = 0;
var tops = 0;
function move(e) {
console.log(e.keyCode);
if (e.keyCode == 68) {
lefts += 100;
player.style.left = lefts + "px";
points.innerHTML = lefts;
}
if (e.keyCode == 65) {
lefts -= 100;
player.style.left = lefts + "px";
}
if (e.keyCode == 83) {
tops += 100;
player.style.top = tops + "px";
}
if (e.keyCode == 87) {
tops -= 100;
player.style.top = tops + "px";
}
}
.play-ground {
position: relative;
background: yellow;
width: 800px;
height: 700px;
}
.player {
position: absolute;
background: red;
width: 100px;
height: 100px;
}
.thing-tocatch {
background: blue;
height: 100px;
width: 100px;
position: absolute;
margin: 200px 200px;
}
<div id="game">
<div class="points">
<h3>Points: <span class="numba">0</span></h3>
</div>
<div class="play-ground">
<div class="thing-tocatch">
</div>
<div class="player">
</div>
</div>
</div>
</div>
I cannot write whole the app here, but I just want to show you some things.
Try to write your code: one action - one function. You can see as I did. I've splitted up your move function on two: onKeyDown and movePlayer. It will help you to support your code when the app will become bigger.
You should store states of your subjects. For example I've created object Palyer where I store his coords (you used for that two vars: lefts and tops). It will be better if you will implement the same for all subjects in your app. For example, the Player has props: width, height, x, y, color. The ground has props: width, height, color. The thing also should have props as for player. You should store these states as global variables, because you will need to use them in different functions.
Your questions:
Next thing which I'm trying to achieve is that I want a div same size of the red one, showing up for some time, so that the user can catch it with the red one and after that he gets points otherwise doesn't. Is there any way to do it?. Your question contains answer. You need a function which will draw (document.createElement('div')) HTML element and after that you will be able to change its styles. OR you can create the thing before start (as you did with player) and keep it hidden (style display: none;)
And how make div disapear after being catched? You can remove thing from DOM or change style to display: none;
I advice you to find similar apps to check how other developers implement similar things:
Pong Clone In JavaScript
Snake Game in vanilla js
var a = prompt("Provide nick");
while (a === "") {
a = prompt("Provide nick");
}
document.write("<p>Nick: " + a + "</p>");
/* -------- */
var ground = document.getElementsByClassName('play-ground')[0];
var player = document.getElementsByClassName('player')[0];
var points = document.getElementsByClassName('numba')[0];
var thing = document.getElementsByClassName('thing-tocatch')[0];
document.onkeydown = onKeyDown;
var Player = {x: 0, y: 0};
var STEP = 100;
function onKeyDown (e) {
console.log(e.keyCode);
var x = Player.x;
var y = Player.y;
if (e.keyCode == 68) {
x += STEP;
}
if (e.keyCode == 65) {
x -= STEP;
}
if (e.keyCode == 83) {
y += STEP;
}
if (e.keyCode == 87) {
y -= STEP;
}
movePlayer(x, y);
}
function movePlayer (x, y) {
if (!isCoordsInsideGround(x, y)) return;
Player.x = x || 0;
Player.y = y || 0;
player.style.left = Player.x + "px";
player.style.top = Player.y + "px";
}
function isCoordsInsideGround (x, y) {
if (x < 0 || ground.offsetWidth - STEP < x) {
return false;
}
if (y < 0 || ground.offsetHeight - STEP < y) {
return false;
}
return true;
}
.play-ground {
position: relative;
background: yellow;
width: 800px;
height: 700px;
}
.player {
position: absolute;
background: red;
width: 100px;
height: 100px;
}
.thing-tocatch {
background: blue;
height: 100px;
width: 100px;
position: absolute;
margin: 200px 200px;
}
<div id="game">
<div class="points">
<h3>Points: <span class="numba">0</span></h3>
</div>
<div class="play-ground">
<div class="thing-tocatch">
</div>
<div class="player">
</div>
</div>
</div>
</div>
To see a working example simply copy code into notepad++ and run in chrome as a .html file, I have had trouble getting a working example in snippet or code pen, I would have given a link to those websites if I could get it working in them.
The QUESTION is; once I fire the laser once it behaves exactly the way I want it to. It increments with lzxR++; until it hits boarder of the game arena BUT if I hit the space bar WHILST the laser is moving the code iterates again and tries to display the laser in two places at once which looks bad and very choppy, so how can I get it to work so the if I hit the space bar a second time even whilst the laser was mid incrementation - it STOPS the incrementing and simply shoots a fresh new laser without trying to increment multiple lasers at once???
below is the Code:
<html>
<head>
<style>
#blueCanvas {
position: absolute;
background-color: black;
width: 932px;
height: 512px;
border: 1px solid black;
top: 20px;
left: 20px;
}
#blueBall {
position: relative;
background-color: white;
border: 1px solid blue;
width: 10px;
height: 10px;
border-radius: 100%;
top: 0px;
left: 0px;
}
#laser {
position: absolute;
background-color: white;
border: 1px solid blue;
width: 10px;
height: 1px;
top: 10px;
left: 10px;
}
#pixelTrackerTop {
position: absolute;
top: 530px;
left: 20px;
}
#pixelTrackerLeft {
position: absolute;
top: 550px;
left: 20px;
}
</style>
<title>Portfolio</title>
<script src="https://ajax.googleapis.com/
ajax/libs/jquery/1.12.4/jquery.min.js">
</script>
<SCRIPT LANGUAGE="JavaScript" type="text/javascript">
document.addEventListener("keydown", keyBoardInput);
var topY = 0;
var leftX = 0;
var lzrY = 0;
var lzrX = 0;
function moveUp() {
var Y = document.getElementById("blueBall");
topY = topY -= 1;
Y.style.top = topY;
masterTrack();
if (topY < 1) {
topY = 0;
Y.style.top = topY;
};
stopUp = setTimeout("moveUp()", 1)
/**allows for progression of speed with additional key strokes**/
topStop();
stopConflictYup();
console.log('moveUp');
};
function moveDown() {
var Y = document.getElementById("blueBall");
topY = topY += 1;
Y.style.top = topY;
masterTrack();
if (topY > 500) {
topY = 500;
Y.style.top = topY;
};
stopDown = setTimeout("moveDown()", 1)
/**allows for progression of speed with additional key strokes**/
topStop();
stopConflictYdown();
console.log('moveDown');
};
function moveLeft() {
var X = document.getElementById("blueBall");
leftX = leftX -= 1;
X.style.left = leftX;
masterTrack();
if (leftX < 1) {
leftX = 0;
Y.style.leftX = leftX;
};
stopLeft = setTimeout("moveLeft()", 1)
/**allows for progression of speed with additional key strokes**/
leftStop();
stopConflictXleft();
console.log('moveLeft');
};
function moveRight() {
var X = document.getElementById("blueBall");
leftX = leftX += 1;
X.style.left = leftX;
masterTrack();
if (leftX > 920) {
leftX = 920;
Y.style.leftX = leftX;
};
stopRight = setTimeout("moveRight()", 1)
/**allows for progression of speed with additional key strokes**/
leftStop();
stopConflictXright();
console.log('moveRight');
};
function masterTrack() {
var pxY = topY;
var pxX = leftX;
document.getElementById('pixelTrackerTop').innerHTML =
'Top position is ' + pxY;
document.getElementById('pixelTrackerLeft').innerHTML =
'Left position is ' + pxX;
};
function topStop() {
if (topY <= 0) {
clearTimeout(stopUp);
console.log('stopUp activated');
};
if (topY >= 500) {
clearTimeout(stopDown);
console.log('stopDown activated');
};
};
function leftStop() {
if (leftX <= 0) {
clearTimeout(stopLeft);
console.log('stopLeft activated');
};
if (leftX >= 920) {
clearTimeout(stopRight);
console.log('stopRight activated');
};
};
function stopConflictYup() {
clearTimeout(stopDown);
};
function stopConflictYdown() {
clearTimeout(stopUp);
};
function stopConflictXleft() {
clearTimeout(stopRight);
};
function stopConflictXright() {
clearTimeout(stopLeft);
};
function shootLaser() {
var l = document.getElementById("laser");
var lzrY = topY;
var lzrX = leftX;
fireLaser();
function fireLaser() {
l.style.left = lzrX; /**initial x pos **/
l.style.top = topY; /**initial y pos **/
var move = setInterval(moveLaser, 1);
/**continue to increment laser unless IF is met**/
function moveLaser() { /**CALL and start the interval**/
var bcrb = document.getElementById("blueCanvas").style.left;
if (lzrX > bcrb + 920) {
/**if the X axis of the laser goes beyond the
blueCanvas 0 point by 920 then stop incrementing the laser on its X
axis**/
clearInterval(move);
/**if statement was found true so stop increment of laser**/
} else {
lzrX++;
l.style.left = lzrX;
};
};
};
};
function keyBoardInput() {
var i = event.keyCode;
if (i == 32) {
shootLaser();
};
if (i == 38) {
if (topY > 0) {
moveUp();
};
};
if (i == 40) {
if (topY < 500) {
moveDown();
};
};
if (i == 37) {
if (leftX > 0) {
moveLeft();
};
};
if (i == 39) {
if (leftX < 920) {
moveRight();
};
};
};
/**
!! gradual progression of opacity is overall
!! being able to speed up element is best done with setTimout
!! setInterval is constant regards to visual speed
!! NEXT STEP IS ARRAYS OR CLASSES
IN ORDER TO SHOOT MULITPLE OF SAME ELEMENT? MAYBEE?
var l = document.getElementById("laser");
lzrX = lzrX += 1;
l.style.left = lzrX;
lzrY = topY += 1;
l.style.top = lzrY;
**/
</SCRIPT>
</head>
<div id="blueCanvas">
<div id="laser"></div>
<div id="blueBall">
</div>
</div>
<p id="pixelTrackerTop">Top position is 0</p>
<br>
<p id="pixelTrackerLeft">Left position is 0</p>
</body>
</html>
Solved the problem with using a variable called "g" and incrementing it once the laser is shot!
var g = 0;
function keyBoardInput() {
var i = event.keyCode;
if (i == 32) {
if (g < 1) {
shootLaser();
g++;
};
};
I am just starting to learn about Javascript for animation. I have created this super simple example, which when clicked moves 3 blocks from a central position, to top, top-left and top-right position (I am building up to a sort of Context Action button type thing). But I would now like to be able to click again and if the animation is "out", then it will execute in reverse. I tried adding an if else, using the elem1.style.bottom == 350 parameter to define whether pos would increment or decrement, but it didn't work. Similarly, I was unable to get a clicked boolean to persist between clicks.
JSFiddle
html:
<body>
<p>
<button onclick="myMove()">Click Me</button>
</p>
<div id ="container">
<div id ="animate1"></div>
<div id ="animate2"></div>
<div id ="animate3"></div>
</div>
</body>
css:
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate1 {
width: 50px;
height: 50px;
left: 175px;
bottom: 175px;
position: absolute;
background-color: red;
z-index:3;
}
#animate2 {
width: 50px;
height: 50px;
left: 175px;
bottom: 175px;
position: absolute;
background-color: blue;
}
#animate3 {
width: 50px;
height: 50px;
left: 175px;
bottom: 175px;
position: absolute;
background-color: green;
}
Javascript:
function myMove() {
var elem1 = document.getElementById("animate1");
var elem2 = document.getElementById("animate2");
var elem3 = document.getElementById("animate3");
var start = 350;
var pos = 175;
var id = setInterval(frame, 5);
var clicked = false;
function frame() {
if (pos == 350) {
clearInterval(id);
clicked = !clicked;
} else {
if (clicked == false){ pos++; } else { pos--;}
elem1.style.bottom = pos + 'px';
elem1.style.left = start - pos + 'px';
elem2.style.bottom = pos + 'px';
elem2.style.left = pos + 'px';
elem3.style.bottom = pos + 'px';
}
}
}
How would I achieve this?
I think this is what you want: http://jsfiddle.net/3pvwaa19/20/
It creates a variable to keep track of whether the animation has happened and it moves your pos variable to the same location - - outside of the main function, so that the values won't get lost after each click:
var moved = false;
var pos = 175;
Yes, you can do it with if(elem1.style.bottom =="350px") {...}else{...:
function myMove() {
var elem1 = document.getElementById("animate1");
var elem2 = document.getElementById("animate2");
var elem3 = document.getElementById("animate3");
if (elem1.style.bottom == "350px"){
var start = 175;
var pos = 350;
var id = setInterval(frame, 5);
function frame() {
if (pos == 175) {
clearInterval(id);
} else {
pos--;
elem1.style.bottom = pos + 'px';
elem1.style.left = 2*start - pos + 'px';
elem2.style.bottom = pos + 'px';
elem2.style.left = pos + 'px';
elem3.style.bottom = pos + 'px';
}
}
} else {
var start = 350;
var pos = 175;
var id = setInterval(frame, 5);
function frame() {
if (pos == 350) {
clearInterval(id);
} else {
pos++;
elem1.style.bottom = pos + 'px';
elem1.style.left = start - pos + 'px';
elem2.style.bottom = pos + 'px';
elem2.style.left = pos + 'px';
elem3.style.bottom = pos + 'px';
}
}
}
}
I've done this simple animation but i have a feeling i've writted to much lines of code for such a simple task. Is there a way to achieve the same result but with less code ?
https://jsfiddle.net/qw82Lwy0/1/
function myMove() {
var elem = document.getElementById("animate");
var pos = 0;
var id = setInterval(frame, 5);
var flag = true;
function frame() {
if (flag) {
pos++;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
if (pos == 350) {
flag = false;
}
} else if (!flag) {
pos--;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
if (pos == 0) {
flag = true;
}
}
}
}
P.S: No jquery or css animation, just javascript.
instead of using the flag and a seperate code for the 2 conditions you could just flip the incrementer like so:
function myMove() {
var elem = document.getElementById("animate");
var pos = 0;
var id = setInterval(frame, 5);
var changeVal = 1;
function frame() {
pos+=changeVal;
if ((pos >= 350) || (pos <= 0)) changeVal *= -1;
elem.style.top = elem.style.left = pos + 'px';
}
}
Animations can now be done in modern browsers using CSS. For example, your javascript animation can be condensed to just a few lines using a CSS transition.
See MDN for more details and examples of transitions and animations.
Run the code snippet below to try
window.ball.addEventListener('transitionend', toggle);
function toggle() {
window.ball.classList.toggle('move');
}
.animation {
position: relative;
background-color: lightyellow;
border: 1px solid gray;
height: 200px;
width: 200px;
}
#ball {
transition-property: left top background-color;
transition-duration: 2s;
position: absolute;
background-color: red;
border-radius: 12px;
height: 25px;
width: 25px;
left: 0;
top: 0;
}
#ball.move {
background-color: blue;
left: 175px;
top: 175px;
}
<button onclick="toggle()">Start</button>
<div class="animation"><div id="ball"></div></div>
Well, you could shorten your frame() function like so:
function myMove() {
var elem = document.getElementById("animate");
var pos = 0;
var id = setInterval(frame, 5);
var flag = true;
function frame() {
if (flag) {
pos++;
if (pos == 350) {
flag = false;
}
} else { // since you are checking if flag is true, the only other answer is false
pos--;
if (pos == 0) {
flag = true;
}
}
elem.style.top = pos + 'px'; // since this occurs in both cases
elem.style.left = pos + 'px'; // you can do it afterward
}
}
Use a function with 3 paraneters : posAdd, flagValue and posValue
function frame(posAdd,flagValue,posValue) {
if (flagValue) {
pos=pos+posAdd;
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
if (pos == posValue) {
flag = !flagValue;
}
}
}
And call it twice : frame(1,true,350)
And frame(-1,false,0)
I am trying to create a single button that controls the bouncing movement of my div's. In the default position, the space divs should form a static, single line.
Once the button is pressed, the divs should resume bouncing within the container.
JsFiddle
function hitLR(el, bounding) {
console.log($(el).data('vx'), $(el).data('vy'))
if (el.offsetLeft <= 0 && $(el).data('vx') < 0) {
console.log('LEFT');
$(el).data('vx', -1 * $(el).data('vx'))
}
if ((el.offsetLeft + el.offsetWidth) >= bounding.offsetWidth) {
console.log('RIGHT');
$(el).data('vx', -1 * $(el).data('vx'));
}
if (el.offsetTop <= 0 && $(el).data('vy') < 0) {
console.log('TOP');
$(el).data('vy', -1 * $(el).data('vy'));
}
if ((el.offsetTop + el.offsetHeight) >= bounding.offsetHeight) {
console.log('BOTTOM');
$(el).data('vy', -1 * $(el).data('vy'));
}
}
function mover(el, bounding) {
hitLR(el, bounding);
el.style.left = el.offsetLeft + $(el).data('vx') + 'px';
el.style.top = el.offsetTop + $(el).data('vy') + 'px';
}
setInterval(function() {
$('.bouncer').each(function(){
mover(this, $('.bouncyHouse')[0]);
});
}, 50);
You can use clearInterval() function to stop the movement.
See this fiddle example.
function hitLR(el, bounding) {
if (el.offsetLeft <= 0 && $(el).data('vx') < 0) {
//console.log('LEFT');
$(el).data('vx', -1 * $(el).data('vx'))
}
if ((el.offsetLeft + el.offsetWidth) >= bounding.offsetWidth) {
//console.log('RIGHT');
$(el).data('vx', -1 * $(el).data('vx'));
}
if (el.offsetTop <= 0 && $(el).data('vy') < 0) {
//console.log('TOP');
$(el).data('vy', -1 * $(el).data('vy'));
}
if ((el.offsetTop + el.offsetHeight) >= bounding.offsetHeight) {
//console.log('BOTTOM');
$(el).data('vy', -1 * $(el).data('vy'));
}
}
function mover(el, bounding) {
hitLR(el, bounding);
el.style.left = el.offsetLeft + $(el).data('vx') + 'px';
el.style.top = el.offsetTop + $(el).data('vy') + 'px';
}
function moveIt() {
$('.bouncer').each(function() {
mover(this, $('.bouncyHouse')[0]);
});
};
$htmlBack = $('.bouncer').clone();
moveInterval = setInterval(moveIt, 50);
$('button').on('click', function(){
if( moveInterval != 0){
clearInterval(moveInterval);
$('.bouncer').remove();
$('.bouncyHouse').eq(0).append($htmlBack);
$htmlBack = $('.bouncer').clone();
moveInterval = 0;
} else {
moveInterval = setInterval(moveIt, 50);
}
});
.bouncyHouse {
height: 200px;
width: 150%;
background-color: black;
position: relative;
}
.bouncer {
position: absolute;
width: 200px;
color: white;
}
.bouncer:nth-child(2) {
top: 30px;
left: 100px;
}
.bouncer:nth-child(3) {
top: 50px;
left: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="bouncyHouse">
<button type="button">Click Me!</button>
<div class="bouncer" data-vx='2' data-vy='-3'>
<span>space</span>
</div>
<div class="bouncer" data-vx='-2' data-vy='2'>
<span>space</span>
</div>
<div class="bouncer" data-vx='5' data-vy='2'>
<span>space</span>
</div>
</div>