A timer does not know when the browser is about to paint. requestAnimationFrame schedules a callback before a future frame, making it a better fit for visual updates such as moving an element or drawing on a canvas.
let frameId;
let start;
function animate(now) {
start ??= now;
const progress = Math.min((now - start) / 400, 1);
element.style.transform = `translateX(${progress * 120}px)`;
if (progress < 1) frameId = requestAnimationFrame(animate);
}
frameId = requestAnimationFrame(animate);Use the timestamp, not a fixed step
- Base progress on the callback timestamp so motion is not tied to a particular refresh rate.
- Keep layout reads and writes organized to avoid repeated forced layout.
- Cancel a scheduled frame with cancelAnimationFrame when the animation should stop.
Respect the user's motion preference
Offer a reduced-motion path for non-essential movement and avoid animating content that can distract or make the page uncomfortable.
Test at different refresh rates and when a tab is in the background. Browsers may pause or throttle animation callbacks when a page is not visible.
