Ulrik Karlsson Ulrik Karlsson - 6 months ago 10
HTML Question

Trying to make a canvas "jump"

I'm trying to make a canvas jump. By jumping I mean moving up for a kinda sort time and then going down to the floor again. Something is obviously wrong because nothing happen when I press space. Can't think of what's wrong with my code, any ideas?

var spaceKey = false;
var player = {
side: 20,
x: 0,
y: HEIGHT - 20,
color: "red"

};

context.fillStyle = player.color;
context.fillRect(player.x, player.y, player.side, player.side);

setInterval(update, 1000/60);

function update(){
clearCanvas();

if (spaceKey){
jump();
}

draw();
}

function jump(){
// Moving the player 5 px up 60 times every 10ms.
for (var i = 0; i < 60; i++){
function jumpUp(){
player.y -= 5;
}
setTimeout(10, jumpUp);

}
// Moving the player 5 px down 60 times every 10ms.
for (var i = 0; i < 60; i++){
function jumpDown(){
player.y += 5;
}
setTimeout(10, jumpDown);
}

}


function onKeyDown(evt) {
//Making spaceKey true onclick and calling the jump function.
if (evt.keyCode == 32) spaceKey = true;

}

function onKeyUp(evt) {

if (evt.keyCode == 32) spaceKey = false;

}

window.addEventListener("keydown", onKeyDown, false);

window.addEventListener("keyup", onKeyUp, false);


(This is not all of my code, and if you want me to add any more code please let me know.)Thanks

I changed my jump function to this:

function jump(){
// Moving the player 5 px up 60 times every 10ms.
function jumpUp(){
player.y -= 5;
}
function jumpDown(){
player.y += 5;
}
for (var i = 0; i < 60; i++){

requestAnimationFrame(jumpUp);
}
// Moving the player 5 px down 60 times every 10ms.
for (var i = 0; i < 60; i++){

requestAnimationFrame(jumpDown);
}
}


still not working though.

Answer

Timeouts are not chained nor summed. The browser does not want for the previous Timeout to have run before executing the next. It tries to run a timeout timeoutArg ms after the moment it is registered. All your callbacks will be executed more or less at the same time here: 10 ms after being registered.

This should work:

var player = { y: 0 };

function jump(){
  function jumpUp(){
    player.y -= 5;
    render();
  }
  function jumpDown(){
    player.y += 5;
    render();
  }
  // Moving the player 5px up every 10ms for 600ms (i.e. 5px up 60 times).
  for (var i = 0; i < 60; i++){
    setTimeout(jumpUp , i * 10);
  }
  // Same but down.
  for (var i = 0; i < 60; i++){
    setTimeout(jumpDown, i * 10 + 600);
  }
}

jump();

// This is just an example.
var repr = document.getElementById("player");
function render(){
  repr.style.top = 150 + player.y / 3 + "px";
}
#player {
  position: absolute;
  width: 10px;
  height: 10px;
  left: 50%;
  top: 150px;
  background-color: black;
  border-radius: 100%;
}
<div id="player"></div>

However, be aware that using timeouts for animation is bad practice. One should use requestAnimationFrame instead and base the animation progress on time, not on frames (frame rate is unreliable). Here is an example of the user of requestAnimationFrame.

var player = { y: 0 };

function jumpFunc(progress){
  return 1 - Math.abs(1 - progress * 2);
  // You may want to use a sinusoid instead of a linear function here.
  // It usually looks more realistic. E.g. :
  // return Math.sin(progress * Math.PI);
}

function jump(startTime, initY){
  var duration = 1200;
  var amplitude = 300;
  startTime = startTime || Date.now();
  initY = initY || player.y;
  var progress = Math.max(0, Math.min(1, (Date.now() - startTime) / duration));
  player.y = initY - jumpFunc(progress) * amplitude;
  render();
  if(progress < 1){
     requestAnimationFrame(jump.bind(null, startTime, initY));
  }
}

// This is just an example.
var repr = document.getElementById("player");
function render(){
  repr.style.top = 150 + player.y / 3 + "px";
}

jump();
#player {
  position: absolute;
  width: 10px;
  height: 10px;
  left: 50%;
  top: 150px;
  background-color: black;
  border-radius: 100%;
}
<div id="player"></div>

Comments