JavaScript DOM Animations
JavaScript can animate HTML elements by changing their styles over time. The preferred modern approach is requestAnimationFrame(), but you can also trigger CSS transitions from JavaScript. This chapter covers the main techniques for smooth, performant animations.
requestAnimationFrame()
requestAnimationFrame() tells the browser to call a function before the next repaint (typically 60 times per second). It is the recommended way to run frame-based animations because the browser can optimize timing and pause animations in inactive tabs.
let position = 0;
function animate() {
position += 2;
const box = document.getElementById("box");
box.style.left = position + "px";
console.log("Position:", position);
if (position < 300) {
requestAnimationFrame(animate);
} else {
console.log("Animation complete!");
}
}
// Start the animation
requestAnimationFrame(animate);setInterval Animation (Legacy)
Before requestAnimationFrame, developers used setInterval to create animation loops. This approach still works but is less efficient — the browser cannot optimize it and it keeps running in background tabs.
let pos = 0;
const intervalId = setInterval(() => {
pos += 2;
const box = document.getElementById("box");
box.style.top = pos + "px";
console.log("Top:", pos);
if (pos >= 200) {
clearInterval(intervalId);
console.log("Interval animation done!");
}
}, 16); // roughly 60 fpsCSS Transitions via JavaScript
A very common approach is to define a CSS transition in your stylesheet, then use JavaScript to change a property value or toggle a class. The browser handles the smooth interpolation automatically.
// Assume CSS: #box { transition: transform 0.5s ease, opacity 0.5s ease; }
const box = document.getElementById("box");
// Change properties — CSS transition handles the animation
box.style.transform = "translateX(200px)";
box.style.opacity = "0.5";
console.log("Transition triggered");
// Listen for the transition to finish
box.addEventListener("transitionend", (e) => {
console.log("Transition ended for:", e.propertyName);
});Transform Manipulations
CSS transforms (translate, rotate, scale, skew) are GPU-accelerated and produce the smoothest animations. JavaScript sets them via element.style.transform.
let angle = 0;
const spinner = document.getElementById("spinner");
function rotate() {
angle += 3;
spinner.style.transform = `rotate(${angle}deg)`;
if (angle % 90 === 0) {
console.log(`Rotated to ${angle} degrees`);
}
if (angle < 360) {
requestAnimationFrame(rotate);
} else {
console.log("Full rotation complete!");
}
}
requestAnimationFrame(rotate);Animation Timing and cancelAnimationFrame()
requestAnimationFrame passes a high-resolution timestamp to your callback. Use it to create time-based (rather than frame-based) animations that run at consistent speed regardless of frame rate. Use cancelAnimationFrame() to stop a pending animation.
const duration = 2000; // 2 seconds
let start = null;
let rafId;
function step(timestamp) {
if (!start) start = timestamp;
const elapsed = timestamp - start;
const progress = Math.min(elapsed / duration, 1);
const box = document.getElementById("box");
box.style.left = (progress * 400) + "px";
console.log(`Progress: ${(progress * 100).toFixed(1)}%`);
if (progress < 1) {
rafId = requestAnimationFrame(step);
} else {
console.log("Animation finished!");
}
}
rafId = requestAnimationFrame(step);
// To stop early:
// cancelAnimationFrame(rafId);| Technique | Best For | Performance |
|---|---|---|
| requestAnimationFrame() | Frame-by-frame JS animations | Excellent |
| CSS transitions + JS toggle | Simple state changes (hover, etc.) | Excellent |
| CSS @keyframes + JS toggle | Complex multi-step animations | Excellent |
| setInterval / setTimeout | Legacy code, non-visual timers | Moderate |
| Web Animations API | Programmatic keyframe control | Excellent |