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.

Basic requestAnimationFrame Loop
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.

setInterval Animation
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 fps

CSS 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.

Triggering CSS Transitions
// 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.

Animated Transforms
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.

Time-Based 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);
TechniqueBest ForPerformance
requestAnimationFrame()Frame-by-frame JS animationsExcellent
CSS transitions + JS toggleSimple state changes (hover, etc.)Excellent
CSS @keyframes + JS toggleComplex multi-step animationsExcellent
setInterval / setTimeoutLegacy code, non-visual timersModerate
Web Animations APIProgrammatic keyframe controlExcellent
📝 Note: Always prefer CSS transitions/animations or requestAnimationFrame over setInterval. They are better optimized by the browser and automatically pause in background tabs, saving CPU and battery.
Exercise:
What is the main advantage of requestAnimationFrame over setInterval for animations?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.