Frontend Development 9 min read

Why requestAnimationFrame Beats setTimeout for Smooth Animations – A Deep Dive

An in‑depth investigation compares requestAnimationFrame and setTimeout for web animations, revealing how the former syncs with browser repaint cycles, why it outperforms setTimeout, the limits of manually adjusting frame rates, compatibility issues across browsers and platforms, and practical fallback implementations.

Qudian (formerly Qufenqi) Technology Team
Qudian (formerly Qufenqi) Technology Team
Qudian (formerly Qufenqi) Technology Team
Why requestAnimationFrame Beats setTimeout for Smooth Animations – A Deep Dive

When developing a WeChat mini‑program I wanted a gravity‑sensor parallax effect, but the platform does not support the HTML5

DeviceOrientationEvent

. Instead it provides

wx.onAccelerometerChange

at only 5 Hz, which makes the animation appear choppy because smooth motion requires at least 60 fps.

Differences from setTimeout

MDN defines

requestAnimationFrame

as a method that notifies the browser to call a specified function before the next repaint. This raises the question: does the callback run automatically before the repaint, or is it simply called after a fixed interval?

window.requestAnimationFrame() is used to tell the browser to invoke a callback before the next repaint, allowing developers to synchronize animations with the display refresh.

Animations should avoid layout‑changing properties like

left

or

margin

and instead use

transform: translate

. Even better is

translate3d

, which triggers full GPU acceleration.

To verify the behavior, I ran the following code:

<code>var laststart;
function test(){
  laststart && console.log(Date.now() - laststart);
  laststart = Date.now();
  requestAnimationFrame(test);
}
requestAnimationFrame(test);
</code>

In Chrome on macOS the repaint frequency was about 60 times per second, as shown below:

I then asked three questions:

Can I artificially increase the repaint frequency?

Does calling

requestAnimationFrame

guarantee a certain repaint rate?

If I never call it, will the page ever repaint?

By varying the

setTimeout

interval (16 ms, 10 ms, 8 ms, 5 ms) and applying a simple translate animation, I observed that the visible flicker speed followed the order

i16 > i10,i5 > i8

. The browser always limited repaint to its native ~60 Hz, regardless of how frequently the callback attempted to change styles.

Conclusion: the browser’s repaint frequency cannot be manually overridden; it matches the display’s refresh rate (typically 60 Hz). The only way to influence animation smoothness is to let the browser schedule frames via

requestAnimationFrame

.

Compatibility

MDN’s compatibility table shows that

requestAnimationFrame

is not supported in older browsers and in the iOS version of WeChat mini‑programs.

Therefore a polyfill is required. Below is a fallback implementation that uses

setTimeout

to approximate a 60 fps schedule when the native API is unavailable.

<code>;(function(){
  var lastTime = 0;
  var vendors = ['ms','moz','webkit','o'];
  for(var x=0; x<vendors.length && !window.requestAnimationFrame; ++x){
    window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] ||
                                 window[vendors[x]+'CancelRequestAnimationFrame'];
  }
  if(!window.requestAnimationFrame){
    window.requestAnimationFrame = function(callback){
      var currTime = Date.now();
      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
      var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }
  if(!window.cancelAnimationFrame){
    window.cancelAnimationFrame = function(id){ clearTimeout(id); };
  }
})();
</code>

requestAnimationFrame in WeChat Mini‑Programs

iOS version of WeChat mini‑programs does not support

requestAnimationFrame

at all.

Conclusion

requestAnimationFrame

and

setTimeout

serve different purposes; the former synchronizes with the browser’s repaint cycle.

Using

translate3d

yields better GPU acceleration than plain

translate

.

The browser’s maximum repaint frequency is fixed (typically 60 Hz) and cannot be manually increased.

Calling

requestAnimationFrame

guarantees a repaint at the browser’s native rate, even if no visual changes occur.

If

requestAnimationFrame

is never called and no other code triggers repaints, the page will not repaint.

frontendPerformanceanimationwebrequestAnimationFramesetTimeout
Qudian (formerly Qufenqi) Technology Team
Written by

Qudian (formerly Qufenqi) Technology Team

Technology team focusing on architecture, service-oriented design, top-tier tools, automation platforms, end-to-end development solutions, talent cultivation, and engineer career growth.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.