Window: requestAnimationFrame() method
BaselineWidely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
Thewindow.requestAnimationFrame()
method tells thebrowser you wish to perform an animation. It requests the browser to call auser-supplied callback function before the next repaint.
The frequency of calls to the callback function will generally match the displayrefresh rate. The most common refresh rate is 60hz,(60 cycles/frames per second), though 75hz, 120hz, and 144hz are also widely used.requestAnimationFrame()
calls are paused in most browsers when running inbackground tabs or hidden<iframe>
s, in order to improveperformance and battery life.
Note:Your callback function must callrequestAnimationFrame()
again ifyou want to animate another frame.requestAnimationFrame()
is one-shot.
Warning:Be sure always to use the first argument (or some other method forgetting the current time) to calculate how much the animation will progress ina frame —otherwise, the animation will run faster on high refresh-rate screens.For ways to do that, see the examples below.
Syntax
requestAnimationFrame(callback)
Parameters
callback
The function to call when it's time to update your animation for the next repaint. This callback function is passed a single argument:
timestamp
A
DOMHighResTimeStamp
indicating the end time of the previous frame's rendering (based on the number of milliseconds sincetime origin). The timestamp is a decimal number, in milliseconds, but with a minimal precision of 1 millisecond. ForWindow
objects (notWorkers
), it is equal todocument.timeline.currentTime
. This timestamp is shared between all windows that run on the same agent (all same-origin windows and, more importantly, same-origin iframes) — which allows synchronizing animations across multiplerequestAnimationFrame
callbacks. The timestamp value is also similar to callingperformance.now()
at the start of the callback function, but it is never the same value.When multiple callbacks queued by
requestAnimationFrame()
begin to fire in a single frame, each receives the same timestamp even though time has passed during the computation of every previous callback's workload.
Return value
Anunsigned long
integer value, the request ID, that uniquely identifies the entryin the callback list. You should not make any assumptions about its value. You can pass this value towindow.cancelAnimationFrame()
to cancel the refresh callback request.
Warning:The request ID is typically implemented as a per-window incrementing counter. Therefore, even when it starts counting at 1, it may overflow and end up reaching 0.While unlikely to cause issues for short-lived applications, you should avoid0
as a sentinel value for invalid request identifier IDs and instead prefer unattainable values such asnull
.The spec doesn't specify the overflowing behavior, so browsers have divergent behaviors. When overflowing, the value would either wrap around to 0, to a negative value, or fail with an error.Unless overflow throws, request IDs are also not truly unique because there are only finitely many 32-bit integers for possibly infinitely many callbacks.Note that it would however take ~500 days to reach the issue when rendering at 60Hz with 100 calls torequestAnimationFrame()
per frame.
Examples
In this example, an element is animated for 2 seconds (2000 milliseconds). The elementmoves at a speed of 0.1px/ms to the right, so its relative position (in CSS pixels) canbe calculated in function of the time elapsed since the start of the animation (inmilliseconds) with0.1 * elapsed
. The element's final position is 200px(0.1 * 2000
) to the right of its initial position.
const element = document.getElementById("some-element-you-want-to-animate");let start;function step(timestamp) { if (start === undefined) { start = timestamp; } const elapsed = timestamp - start; // Math.min() is used here to make sure the element stops at exactly 200px const shift = Math.min(0.1 * elapsed, 200); element.style.transform = `translateX(${shift}px)`; if (shift < 200) { requestAnimationFrame(step); }}requestAnimationFrame(step);
The following three examples illustrate different approaches to setting the zero point in time,the baseline for calculating the progress of your animation in each frame. If youwant to synchronize to an external clock, such asBaseAudioContext.currentTime
,the highest precision available is the duration of a single frame, 16.67ms @60hz. Thecallback's timestamp argument represents the end of the previous frame, so the soonestyour newly calculated value(s) will be rendered is in the next frame.
This example waits until the first callback executes to setzero
. If your animationjumps to a new value when it starts, you must structure it this way. If you do not need tosynchronize to anything external, such as audio, then this approach is recommended becausesome browsers have a multi-frame delay between the initial call torequestAnimationFrame()
and the first call to the callback function.
let zero;requestAnimationFrame(firstFrame);function firstFrame(timestamp) { zero = timestamp; animate(timestamp);}function animate(timestamp) { const value = (timestamp - zero) / duration; if (value < 1) { element.style.opacity = value; requestAnimationFrame((t) => animate(t)); } else element.style.opacity = 1;}
This example usesdocument.timeline.currentTime
to set a zero valuebefore the first call torequestAnimationFrame
.document.timeline.currentTime
aligns with thetimestamp
argument, so the zero value is equivalent to the0th frame's timestamp.
const zero = document.timeline.currentTime;requestAnimationFrame(animate);function animate(timestamp) { const value = (timestamp - zero) / duration; // animation-timing-function: linear if (value < 1) { element.style.opacity = value; requestAnimationFrame((t) => animate(t)); } else element.style.opacity = 1;}
This example animates usingperformance.now()
instead of the callback'stimestamp value. You might use this to achieve slightly higher synchronizationprecision, though the extra degree of precision is variable and not much of an increase.Note: This example does not allow you to synchronize animation callbacks reliably.
const zero = performance.now();requestAnimationFrame(animate);function animate() { const value = (performance.now() - zero) / duration; if (value < 1) { element.style.opacity = value; requestAnimationFrame((t) => animate(t)); } else element.style.opacity = 1;}
Specifications
Specification |
---|
HTML # dom-animationframeprovider-requestanimationframe |