requestAnimationFrame()
Schedules a function to run right before the next screen repaint. Used for smooth animations driven by JavaScript.
- Returns
- A number, the identifier of the scheduled call.
Example
#const bar = document.querySelector('#bar')
const duration = 600
let start
function step(timestamp) {
if (start === undefined) start = timestamp
const progress = Math.min((timestamp - start) / duration, 1)
bar.style.width = `${progress * 100}%`
if (progress < 1) {
requestAnimationFrame(step)
} else {
console.log('Bar filled:', bar.style.width)
}
}
requestAnimationFrame(step)
console.log('Animation scheduled')Progress is computed from elapsed time, so the bar fills in 0.6 s no matter the frame rate. Click “Run again” to see the animation once more.
Definition and usage
#The requestAnimationFrame() function asks the browser to call the given function right before it draws the next frame. That usually happens 60 times a second, and more often on higher-refresh-rate screens. Visual changes then land on screen exactly when the browser is already redrawing the page anyway, so the animation looks smoother than with setInterval().
The callback runs only once. To build an animation, schedule the next call by calling requestAnimationFrame() again at the end of each frame. The callback receives a timestamp in milliseconds (on the same scale as performance.now()), which lets you compute progress from elapsed time rather than frame count. That way the animation takes the same amount of time on every screen.
The call returns an identifier that, passed to cancelAnimationFrame(), cancels the scheduled frame. In background tabs and hidden frames, the browser pauses calls to save battery. Simple transitions are better described in CSS (transition, @keyframes), and requestAnimationFrame() is best left for motion that depends on calculations, such as in games and charts.
Syntax
#requestAnimationFrame(callback)
cancelAnimationFrame(requestId)Parameters
#callback
function (timestamp)
The function called before the next frame. It receives a timestamp in milliseconds.requestId
number
The identifier returned byrequestAnimationFrame(), passed tocancelAnimationFrame().
More examples
#const points = document.querySelector('#points')
const target = 150
let value = 0
function tick() {
value = Math.min(value + 30, target)
points.textContent = value
console.log(value)
if (value < target) requestAnimationFrame(tick)
}
requestAnimationFrame(tick)Each frame increases the counter by 30, so the animation finishes after five frames, around 80 ms on a 60 Hz screen. On a 120 Hz screen the same code runs twice as fast, which is why longer animations should compute their step from elapsed time instead, as in the first example.
const tile = document.querySelector('#tile')
const moveId = requestAnimationFrame(() => {
tile.style.transform = 'translateX(200px)'
console.log('Moved the tile')
})
requestAnimationFrame(() => {
console.log('Frame ready, offset:', tile.style.transform || 'none')
})
cancelAnimationFrame(moveId)
console.log('Frame id is', typeof moveId)The first scheduled function was cancelled before the next frame, so the tile didn’t move. The second one ran normally.
const header = document.querySelector('header')
let scheduled = false
window.addEventListener('scroll', () => {
if (scheduled) return
scheduled = true
requestAnimationFrame(() => {
header.classList.toggle('with-shadow', window.scrollY > 0)
scheduled = false
})
})The scroll event can fire several times within a single frame. The scheduled flag makes sure the header’s appearance updates at most once per frame, right before painting.
Browser support
#Widely available
Works in all modern browsers, phones included. You can use it without worry.
- Chrome
- Edge
- Firefox
- Safari
Good practices
#- Compute animation progress from elapsed time (
timestamp), not from frame count. A 120 Hz screen has twice as many frames as a 60 Hz one. - Each call schedules only one frame. If the animation should keep going, call
requestAnimationFrame()again inside the function. - Describe simple transitions in CSS with
transitionor@keyframes. The browser animates them more efficiently and without JavaScript code.
Related entries
#- setTimeout()Runs a function once, after the given number of milliseconds has passed.
- setInterval()Calls a function repeatedly, at the given interval, until it’s stopped with clearInterval().
- HTMLElement.styleAn object of styles set directly on an element (inline), for reading and changing individual CSS properties.
- ResizeObserverCalls a function every time an observed element’s size changes, no matter the cause of the change.
- Element.getBoundingClientRect()Returns an element’s size and its position relative to the viewport, as a DOMRect object.
Spotted a mistake or missing an example? Write to us.