4

PROBLEM

I am trying to get an image on a canvas to move from left to right smoothly. It's not too bad on Chrome/Safari (still stutters a little), but there is noticeable stutter in Firefox on multiple machines (tried on Windows and Mac). I'm a bit stumped as to how to solve this.

WHAT I TRIED

I am using requestAnimationFrame instead of setTimeout. I am using clearRect instead of setting the canvas width, though I am clearing the entire canvas instead of the minimum bounding box as I wanted to test it in the worst case scenario. I did close extra tabs. I even disabled Firebug. I am using drawImage instead of the image data functions. Since I'm not doing anything else but clearRect and drawImage, I avoided using an offscreen canvas.

EXAMPLE 1: http://jsfiddle.net/QkvYs/

This one has a set framerate where it ensures that the position is correct regardless of how often the game loop runs. It displays the number of frames skipped. This example is closer to what I'm aiming for. Note that it looks choppy even when no frames are being skipped. I have also played around with the framerate with no success, though I'd like to aim for about 30 fps (var frameRate = 33;).

EXAMPLE 2: http://jsfiddle.net/R8sEV/

Here is a simple one where all it does is move the image. This stutters for me on multiple machines.

//loop
function loop() {
    //request anim frame
    requestAnimationFrame(loop);

    //set canvas once able
    if (!canvas) {
        var temp = document.getElementById("testCanvas");
        if (temp) {
            canvas = temp;
            ctx = canvas.getContext('2d');
        }
    }

    //update and render if able
    if (canvas) {
        //increase x
        x += 5;

        //start from beginning
        if (x > canvas.width) {
            x = 0;
        }

        //clear
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        //image
        ctx.drawImage(image, x, 200);
    }
}

//start
loop();

WHAT I LOOKED AT

I realize that this question has been asked before and I did look before asking. However, the answers given have unfortunately not helped.

Thanks for the help! I appreciate it.

Community
  • 1
  • 1
n a
  • 301
  • 2
  • 10
  • 1
    Do you have to use a canvas? You could do this animations with CSS Animations. If you have to use a canvas, can you use a tween library like TweenJS: http://www.createjs.com/#!/TweenJS – rogchap Jan 18 '13 at 00:47
  • I would prefer canvas over CSS as I see canvas only getting better and faster. Coding for the present and the future! I will look into TweenJS. I'm unfamiliar with tweening but that might be what I need. – n a Jan 18 '13 at 01:01
  • I looked at TweenJS and I don't think that it solves my issue. It animates the frames inbetween frames to make things smoother. However, in my case, I could do the same thing by decreasing how much the x is moving and increasing the framerate to get the same speed. Even with this, it's still choppy on Firefox. – n a Jan 18 '13 at 01:42
  • I would seriously consider CSS Animations, unless your application is specifically needing the canvas. CSS Animation is just as future proof as the canvas. Ps. I would also look at EaselJS to handle the frame loop and adding assets to the canvas/stage. – rogchap Jan 18 '13 at 01:49
  • I've been looking at CSS animations since you mentioned them. They seem to be animated based on time, which I would need to convert to frames. I'd just have to keep interrupting CSS animations every render loop as the X/Y positions could have changed after a collision, which could cause choppiness as well. I'm also a bit unsure as to how fast rapidly creating and removing DOM objects are (like a player shooting a large number of fireballs). It's doable, but definitely more difficult than canvas. At the very least, I'll try a small example first and see from there. – n a Jan 18 '13 at 02:07
  • As for EaselJS, thanks for the suggestion but I actually started with that before I gave up and began building my own from scratch. :P I needed more than what EaselJS provided and modifying EaselJS to do what I needed was harder than just building my own. That and EaselJS doesn't seem to be garbage collector friendly. I was reading through the Ticker code which controls the game loop and every tick they use slice and unshift on Arrays for their listeners. That means every frame there are 3 additional Arrays that need to be garbage collected. – n a Jan 18 '13 at 02:17

2 Answers2

1

For instance use time delta in your position calculations. This will ensure that object is moved by certain value in given time regardless of FPS and delay between frames.

Edited your fiddle: http://jsfiddle.net/R8sEV/2/

wrong approach:

x += 5

good approach:

x += speed * delta / 1000

where delta is time in [ms] which passed from last frame - and speed is measured in [pixels/second]

rezoner
  • 1,907
  • 13
  • 16
  • I already have something similar built into the other Example (uses fixed timestep) but it is still choppy. I just tried it and the fiddle you posted is also choppy for me. If it helps, here's the link again. http://jsfiddle.net/QkvYs/ I'll edit the original post and swap Example 1 and 2. – n a Jan 18 '13 at 00:53
  • Unrelated, but I just saw that you used Date.now() in your fiddle instead of new Date(). Didn't realize that existed and it's apparently faster! I'll use that instead now. – n a Jan 18 '13 at 01:12
0

You are getting the element every frame?! That can cause a lot of lag.

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;

function loop() {
  requestAnimationFrame(loop);
  x += 5;
  ctx.drawImage(IMAGE, x, 0);
}

loop();
cs6413110
  • 69
  • 6