Originally posted by xoggoth
View Post
DOM manipulation can be a complex thing, depending on how you do it. If you move something about then it's possible that the browser has to reflow the entire page in order to determine which parts it needs to repaint. If the element is absolutely positioned relative to the document element it greatly reduces the amount of calculation the browser has to do for each frame.
The usual cause of jerky animation, though, is the assumption that an interval timer set to fire every 20ms will fire every 20ms, regular as clockwork. This isn't a valid assumption as the timer could be delayed for any number of reasons, whether it be an external process hogging CPU so the browser itself doesn't get the chance to trigger the timer event on time, to the browser itself being occupied with some long-running task like garbage collection. I did a lot of research into this at ClientOrg last year, although I was only testing on Chrome, Firefox, and Opera (I was working with SVG so IE wasn't relevant). This showed that Chrome was the most reliable, being bang on the millisecond about 99.5% of the time, whereas Firefox (3.6) was the worst, sometimes introducing delays of up to 300ms or so. As the interval timer won't try to fire all delayed events, this means that a 20ms interval timer running for 1 second might not even fire 50 times, but only 48; also, there might be several times when the spacing of the events is more like 30ms - 18ms - 12ms, giving three events in 60ms with the third being exactly 60ms from the start time, but none of the two intervening events firing on schedule. Opera is particularly prone to this, giving a kind of sawtooth effect to my charts of the results.
One way to fix this is to, on each tick, use the time since start time to determine the actual current proportion of the overall duration (as opposed to what it would be in an ideal world) and calculate the current offset based on that. So to move an absolutely-positioned element right by 200 pixels in two seconds, you'd do something like:
Code:
function moveThing(thingToMove) {
var startX = 0,
endX = 200,
deltaX = endX - startX,
duration = 2000, /* milliseconds */
interval = 20, /* milliseconds */
startTime = new Date().getTime(),
timer;
timer = setInterval(function() {
var currentTime = new Date().getTime(),
/* make sure we don't overshoot, even if the last tick is late */
deltaT = Math.min(currentTime - startTime, duration),
deltaD = deltaT / duration,
deltaX = Math.floor(deltaD * deltaX);
thingToMove.style.left = (startX + deltaX) + "px";
if (deltaT == duration) {
clearInterval(timer);
}
}, interval);
}


)

Comment