Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animating multiple DIV-Elements with JS and the DOM results in a low Framerate

Preface

I just started programming with javascript and I am currently working on this hobby web-site project of mine. The site is supposed to display pages filled with product images than can be "panned" to either the left or right. Each "page" containing about 24 medium sized pictures, one page almost completely fills out an entire screen. When the user chooses to look at the next page he needs to click'n'drag to the left (for example) to let a new page (dynamically loaded through an AJAX script) slides into the view.

The Issue

This requires for my javascript to "slide" two of these mentioned pages synchronously by the width of a screen. This results in a really low framerate. Firefox and Opera lag a bit, Chrome has it especially bad: 1 frame of animation takes approx. 100 milliseconds, thus making the animation look very "laggy".

I do not use jQuery, nor do I want to use it or any other library to "do the work for me". At least not until I know for sure that what I am trying to do can not be done with a couple of lines of self-written code.

So far I have figured out that the specific way I manipulate the DOM is causing the performance-drop. The routine looks like this:

function slide() {
  this.t = new Date().getTime() - this.msBase;

  if( this.t > this.msDura ) {
    this.callB.call(this.ref,this.nFrames);
    return false;
  }

  //calculating the displacement of both elements

  this.pxProg = this.tRatio * this.t;

  this.eA.style.left = ( this.pxBaseA + this.pxProg ) + 'px';
  this.eB.style.left = (this.pxBaseB + this.pxProg) + 'px';

  if ( bRequestAnimationStatus )
    requestAnimationFrame( slide.bind(this) );
  else
    window.setTimeout( slide.bind(this), 16 );

  this.nFrames++;

}

//starting an animation

slide.call({
  eA:      theMiddlePage,
  eB:      neighboorPage, 
  callB:   theCallback,
  msBase:  new Date().getTime(),
  msDura:  400,
  tRatio:  ((0-pxScreenWidth)/400),
  nFrames: 0,
  ref:     myObject,
  pxBaseA: theMiddlePage.offsetLeft,
  pxBaseB: neighboorPage.offsetLeft
});

Question

I noticed that when I let the AJAX script load less images into each page, the animation becomes much faster. The separate images seem to create more overhead than I have expected.

Is there another way to do this?

like image 681
John Smith Avatar asked Mar 07 '13 23:03

John Smith


1 Answers

THE JAVASCRIPT SOLUTION

OK, there are two possible things for you to try to speed this up.

First of all, when you modify the style of an element, you force the browser to rerender the page. This is called a repaint. Certain changes also force a recalculation of the page's geometry. This is called a reflow. A reflow always triggers a repaint immediately after it. I think why you're experiencing worse performance with more elements is that each update to each one triggers at least a repaint. What is normally recommended in the case of modifying multiple styles on a single element is to either do them all at once by adding or removing a class, or hide the element, do your manipulations, and then show it, which means only two repaints (and possibly reflows).

It appears that in this particular case, you're probably already doing just fine, as you're only manipulating each item once per iteration.

Second of all, requestAnimationFrame() is good for doing animations on a canvas element, but seems to have somewhat dodgy performance on DOM elements. Open your page in Chrome's profiler to see exactly where it hangs up. Try JUST using setTimeout() to see if that ends up being the case. Again, the profiler will tell you where the hang-ups are. requestAnimationFrame() should be better, but validate this. I've had it backfire on me, before.

THE REAL SOLUTION

Don't do this in JavaScript, if you can at all avoid it. Use CSS transitions and translation to do the animation with a JavaScript function registered as the onTransitionEnd event handler for each animated element. Letting the browser do this stuff natively is almost always faster than any JS code anyone can write.

The only catch is that CSS3 animations are only supported by the newer browers. For your own edification, do it this way. For real, practical applications, delegate to a library. A good library will do it natively, if possible, and fall back to the best way of doing it in JS for the older browsers.

Good link to read regarding this stuff: http://www.html5rocks.com/en/tutorials/speed/html5/

like image 143
Michael Cordingley Avatar answered Nov 15 '22 01:11

Michael Cordingley