Skip to content

D3.js - Animation

Animation in D3.js is primarily achieved through its powerful transition system. Transitions smoothly interpolate element properties (attributes and styles) from a starting state to an ending state over a specified duration. This creates the illusion of movement or change. While D3 transitions are a form of keyframe animation with two keyframes (start and end), they are incredibly versatile for common UI animations and data-driven visual changes.

A simple animation example changing background color:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3 Animation Example</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<h3>Animating with Transitions</h3>
<script>
d3.select("body")
.style("background-color", "white") // Initial state
.transition() // Start a transition
.duration(2000) // Animation will last 2 seconds
.style("background-color", "lightgray"); // Target state
</script>
</body>
</html>

Here, the background color of the document will smoothly change from white to lightgray over 2 seconds.

The duration() method sets how long the animation (transition) will take, in milliseconds. Without a specified duration, transitions default to 250ms.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3 Duration Example</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<h3>Text Color Animation</h3>
<script>
d3.select("h3")
.transition()
.style("color", "green")
.duration(5000); // Text color changes to green over 5 seconds
</script>
</body>
</html>

You can use any valid CSS color format, including RGB:

d3.select("h3")
.transition()
.style("color", "rgb(0, 150, 120)") // Target color
.duration(5000);

D3.js handles the interpolation between the start and end values for styles like color. For numeric attributes or styles, it interpolates numbers. For complex values like colors or transform strings, D3 uses specialized interpolators.

D3 automatically chooses an appropriate interpolator based on the attribute or style being changed. An interpolator is a function that, given a value t usually between 0 (start) and 1 (end), returns an intermediate value. You can access these directly via d3.interpolate*(a, b) functions:

  • d3.interpolateNumber(a, b): Interpolates between two numbers a and b.
  • d3.interpolateRgb(a, b): Interpolates between two colors. Colors can be CSS named colors or RGB/HSL strings.
  • d3.interpolateString(a, b): Interpolates between two strings. It finds numbers embedded in the strings and interpolates them.
  • Many other specialized interpolators exist (e.g., for transforms, arrays, objects).

While D3 usually handles this automatically, you can create custom interpolations using attrTween or styleTween for advanced animations.

The delay() method specifies a wait time in milliseconds before the transition begins.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3 Delay Example</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<h3>Font Size Animation with Delay</h3>
<script>
d3.select("h3")
.transition()
.delay(2000) // Wait 2 seconds before starting
.duration(2000) // Animation lasts 2 seconds
.style("font-size", "28px");
</script>
</body>
</html>

Understanding the transition lifecycle helps in coordinating complex animations and interactions:

  1. Scheduled: A transition is scheduled when selection.transition() is called. At this point, you define its end state (target attributes/styles) and parameters (duration, delay, ease).
  2. Started: The transition starts after its specified delay. If no delay, it starts as soon as possible (typically a few milliseconds). The start event is dispatched for each element when its transition begins.
  3. Running: During its duration, the transition continuously updates the element’s properties based on the easing function and the interpolator. Each step effectively moves the element from t=0 to t=1 through the interpolation.
  4. Ended: The transition ends once its duration has elapsed after starting. The end event is dispatched for each element. The element’s properties are set exactly to their target end values.

You can listen to these lifecycle events using transition.on(eventName, callback):

d3.select("h3")
.style("color", "blue") // Initial color
.transition()
.delay(500)
.duration(1500)
.on("start", function() {
// 'this' refers to the DOM element (h3 here)
// Using a traditional function to preserve 'this' context
d3.select(this).text("Animation Starting!");
})
.style("color", "red") // Target color
.on("end", function() {
d3.select(this).text("Animation Ended!").style("color", "green");
});

Alternatively, for actions after all transitions in a selection are complete, Promises are a cleaner approach:

const myTransition = d3.selectAll(".my-elements")
.transition()
.duration(1000)
.style("opacity", 0);
myTransition.end().then(() => {
console.log("All elements have faded out.");
// d3.selectAll(".my-elements").remove(); // For example
});

Animations are a key part of making D3.js visualizations engaging and informative.