user1184100
user1184100

Reputation: 6894

Image flickering in canvas

I've written a simple image slider using HTML5 canvas. After each image reaches end of screen it is removed from the array which is causing random image flickering. How can i fix this.

JSFiddle : http://jsfiddle.net/mbe5R/2/

this.animate = function() {

        this.y += this.speed;
        this.x = this.xrandom * albumWall.canvas.width - 250;

            if(this.y > innerHeight) {
                    albumWall.fbImages.splice(albumWall.fbImages.indexOf(this),1);

                    if(albumWall.count==(albumWall.imgArr.length-1)) {
                        albumWall.count=-1;
                    }else{
                        albumWall.count++;
                        var img = albumWall.imgArr[albumWall.count];
                        console.log(img)

                        albumWall.fbImages.push(new self.fbImage(albumWall.count, img, img.width, img.height));
                    }


                }

            };

When the image reaches the end of window i'm removing it

albumWall.fbImages.splice(albumWall.fbImages.indexOf(this),1);

I think this is causing the problem for the screen to flicker randomly.

Upvotes: 1

Views: 2665

Answers (2)

Philipp
Philipp

Reputation: 69673

While the answer by Evan is a workaround for older browsers, newer browsers support requestAnimationFrame which is a much cleaner way to prevent flickering.

By setting

 function yourDrawingFunction() {

      // your drawing code here

      window.requestAnimationFrame(yourDrawingFunction);
 };

 window.requestAnimationFrame( yourDrawingFunction );

the frame-drawing code will be executed whenever the browser is ready and will automatically use double-buffering (nothing will be shown to the user before the function has finished). This also has two positive side-effects for performance:

  • Drawing is faster on most browsers because it's easier for them to incorporate the drawing into the scheduling of their own page rendering pipeline
  • The code will not be executed when the current browser tab isn't visible, which saves resources on the users machine. But this means you have to make sure that you have no actual application logic in your frame-drawing code (but you shouldn't have that anyway).

Most browsers already support this, but some only with their vendor-specific prefix. This polyfiller snippet in your initialization routine provides compatibility and also provides a fallback for browsers which don't support it at all:

window.requestAnimFrame = (function(){
  return  window.requestAnimationFrame       ||
          window.webkitRequestAnimationFrame ||
          window.mozRequestAnimationFrame    ||
          window.msRequestAnimationFrame     ||
          window.oRequestAnimationFrame      ||
          function( callback ){
            window.setTimeout(callback, 1000 / 60);
          };
})();

Upvotes: 0

Evan
Evan

Reputation: 825

You were right, as far as I can tell. The issue was that by pulling the image out of the array in the middle of the animation, you produced a frame where another image (the one now in its place, most likely) was not rendered. This can be fixed by changing that above line to this:

var that = this;
setTimeout( function() {
  albumWall.fbImages.splice(albumWall.fbImages.indexOf(that),1);
}, 0);

The short explanation is that the timeout will make the splice wait until your current animation function is complete before triggering. More information can be found at this helpful answer about using setTimeout to send functions down the stack.

Here's your updated fiddle.

Upvotes: 2

Related Questions