1

The following is an example code I wrote just to show I handle certain things on my game:

https://jsfiddle.net/qk7ayx7n/25/

<canvas id = "canvas"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>

JS:

var canvas = document.getElementById("canvas");
var ctx=canvas.getContext("2d");
canvas.width = 750; //keeping ratio
canvas.height = 587; //keeping ratio

$('#canvas').css("height", window.innerHeight);
$('#canvas').css("width",  window.innerHeight * 1.277); //keeping the ratio
//and also resizing according to the window(not to overflow)

var board = new Image();
board.src = "https://s21.postimg.org/ko999yaaf/circ.png";
var circle = new Image();
circle.src = "https://s21.postimg.org/4zigxdh7r/circ.png";
ctx.drawImage(board, 0, 0);

var x = 10, y = 10;
ctx.drawImage(circle, x, y);
startMoving();

function startMoving(){
      if(y > 310) return;
    y+=3;
    ctx.clearRect(0,0,750,587);
    ctx.drawImage(board, 0, 0);
      ctx.drawImage(circle, x, y);
    setTimeout(function(){startMoving()}, 30);
}

A little explanation: This is a simple board game. first the canvas is set to the board dimensions themselves in order to get the coordinates X Y correctly(this is not useful here but in my actual game yes).

then it is resized according to the window of the player, with regards to the actual ratio of the original board image. keeping the ratio is important for the quality of the image. Now the movement is done with a simple timer in a function, once it gets to a certain X and Y the movement is stopped.

I have trouble getting the movement of the circle to move without breaks/lags in some browsers and devices (like on an cordova app), though it works fine usually. I know that the lags are caused by the way I handle things, but why? also, I have trouble keeping the speed of the movement constant - +3 doesn't seem to move the same in every browser.

user1938653
  • 611
  • 1
  • 9
  • 21
  • 1
    Perhaps look into requestAnimationFrame(), insead of using setTimeout() – Jeff McCloud Nov 22 '16 at 00:39
  • Mind showing how can I integrate it using my example? – user1938653 Nov 22 '16 at 00:41
  • Look here for how to throttle (60 frames per second is good goal): http://stackoverflow.com/questions/19764018/controlling-fps-with-requestanimationframe and here's a random non-throttled jsFiddle I found that uses canvas, but use your code of course: https://jsfiddle.net/0q4yLyma/ – Jeff McCloud Nov 22 '16 at 00:59
  • Not related to your issue, but don't forget to preload your images before doing anything to it. – Kaiido Nov 22 '16 at 01:30

1 Answers1

1

In most cases, you should use requestAnimationFrame for JavaScript-based animations to avoid choppiness. With this technique, the position is a function of time not how many execution frames take place. This way, fast computers will have more animation frames than slow computers, but you'll still perceive the same animation velocity. For example:

var x = 10, y = 10;

var startPos = 10;
var destPos = 310;
var startTime = Date.now();
var velocity = 0.1; // pixels per millisecond
var distance = destPos - startPos;
var duration = Math.abs(distance) / velocity;

requestAnimationFrame(startMoving);

function startMoving(now) {
  var elapsedTime = Math.min(now - startTime, duration);
  y = startPos + (elapsedTime * velocity);
  ctx.clearRect(0,0,750,587);
  ctx.drawImage(board, 0, 0);
  ctx.drawImage(circle, x, y);

  if (elapsedTime < duration)
    requestAnimationFrame(startMoving);
}
Jacob
  • 77,566
  • 24
  • 149
  • 228
  • Maybe I am wrong, but if I need the image to travel a certain distance and the duration is over, because of slow browser, the image would simply teleport there instead of completing it's route, no? – user1938653 Nov 22 '16 at 01:13
  • Yep, that's right. In other words, you set how long or how fast you want the animation to be. If the computer is only fast enough to do 1 animation frame, that's all you'll see. Pretty unlikely, though. – Jacob Nov 22 '16 at 01:14
  • Well that might be a problem, as I need the player to actually see his movement to the new location. I don't mind him having bigger jumps though, each jump is an x pixels moved and slow browser will have less jumps. is it possible? – user1938653 Nov 22 '16 at 01:15
  • That's exactly what this code does. Teleporting would just be the case of an extremely slow browser, where it's just one jump. I wouldn't get hung up on that edge condition. – Jacob Nov 22 '16 at 01:16
  • As I am too tired to try to integrate it with my real code(it's huge) I will simply let you know tomorrow, thanks nonetheless! – user1938653 Nov 22 '16 at 01:23
  • Why do you use `Date.now()` when you've got an [DOMHighResTimeStamp](https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp) passed as only argument of your callback ? Also, your technique would be just applicable to a setTimeout loop, it's not really dependent on rAF, it's just that rAF is synchronised with the monitor refresh rate, whatever it is (usually 60FPS) no need to go faster since your monitor won't show it anyway. – Kaiido Nov 22 '16 at 01:24
  • Kaiido, is there a less complicated way to achieve the same thing without Date.now()? – user1938653 Nov 22 '16 at 01:33
  • @user1938653, the only argument passed in the callback to rAF (here `startMoving`) is a timestamp, that you can use instead of `Date.now()`, not sure if it makes anything easier, it just doesn't make an useless call. `function startMoving(time){ if(!startTime){startTime = time;} elapsedTime = Math.min( time - startTime , ...` – Kaiido Nov 22 '16 at 03:51
  • @Jacob, I had to modify some things (like: distance = Math.abs(destPos - startPos) in order to go up aswell) but overall your solution is perfect for old browsers that lag, but to be completely honest; I was looking for a way to partially prevent lags in old browsers and devices from happening in the first place. nevertheless, thanks. – user1938653 Nov 22 '16 at 22:53