requestAnimationFrame & Smooth Animations
requestAnimationFrame (rAF) schedules code to run before the next screen repaint — giving you smooth 60fps animations. It's more efficient than setInterval for visual updates and pauses when the tab is hidden.
requestAnimationFrame
- requestAnimationFrame(callback) — Schedule before next repaint. Runs at display refresh rate (60fps)
- cancelAnimationFrame(id) — Cancel a scheduled frame
- Why not setInterval? — setInterval doesn't sync with display. Can cause jank, runs in background tab
- Timestamp — Callback receives timestamp (ms): function animate(timestamp) { }
- Animation loop — Recursive calls: function loop() { update(); requestAnimationFrame(loop); }
- Performance — Auto-pauses in background tabs. Batches with browser rendering cycle
rAF Code
// Basic animation with rAF
function animate() {
let startTime = null;
const duration = 2000; // 2 seconds
function step(timestamp) {
if (!startTime) startTime = timestamp;
const elapsed = timestamp - startTime;
const progress = Math.min(elapsed / duration, 1); // 0 to 1
// Move element from 0 to 300px
const position = progress * 300;
console.log(`Position: ${Math.round(position)}px (${Math.round(progress * 100)}%)`);
// element.style.transform = `translateX(${position}px)`;
if (progress < 1) {
requestAnimationFrame(step);
} else {
console.log("Animation complete! ✅");
}
}
requestAnimationFrame(step);
}
// animate();
// Easing functions for natural motion
function easeOut(t) { return 1 - Math.pow(1 - t, 3); }
function easeInOut(t) { return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; }
// FPS counter
let lastTime = 0;
let frameCount = 0;
function fpsCounter(timestamp) {
frameCount++;
if (timestamp - lastTime >= 1000) {
console.log(`FPS: ${frameCount}`);
frameCount = 0;
lastTime = timestamp;
}
// requestAnimationFrame(fpsCounter);
}
// requestAnimationFrame(fpsCounter);
console.log("requestAnimationFrame: 60fps smooth animations!");Tip
Tip
Use transform and opacity for animations instead of top/left/width/height. Transform and opacity don't trigger layout recalculation — they use GPU compositing, running at 60fps even on mobile devices.
Use performance.mark/measure for custom timing. PerformanceObserver for Core Web Vitals. sendBeacon for analytics.
Common Mistake
Warning
Using setInterval for animations. It doesn't sync with the browser's repaint cycle, causing janky animations and wasted CPU cycles. requestAnimationFrame syncs perfectly with the display refresh rate.
Practice Task
Note
Animation performance: (1) Create a smooth animation using requestAnimationFrame. (2) Compare it with setInterval-based animation. (3) Use CSS transform instead of changing top/left position.
Quick Quiz
Key Takeaways
- requestAnimationFrame (rAF) schedules code to run before the next screen repaint — giving you smooth 60fps animations.
- requestAnimationFrame(callback) — Schedule before next repaint. Runs at display refresh rate (60fps)
- cancelAnimationFrame(id) — Cancel a scheduled frame
- Why not setInterval? — setInterval doesn't sync with display. Can cause jank, runs in background tab