Skip to content

D3.js - 动画 (Animation)

D3.js 中的动画主要通过其强大的**过渡(transition)**系统实现。过渡会在指定的时间内平滑地内插元素属性(attributes)和样式(styles),使其从起始状态变化到结束状态。这创造了运动或变化的错觉。虽然 D3 过渡是一种只有两个关键帧(起始和结束)的关键帧动画形式,但它们对于常见的 UI 动画和数据驱动的视觉变化非常灵活。

一个简单的改变背景颜色的动画示例:

<!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 (动画将持续 2 秒)
.style("background-color", "lightgray"); // Target state (目标状态)
</script>
</body>
</html>

在这里,文档的背景颜色将在 2 秒内从白色平滑变为浅灰色。

duration() 方法设置动画(过渡)的持续时间,单位为毫秒。如果没有指定持续时间,过渡默认为 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 (文本颜色在 5 秒内变为绿色)
</script>
</body>
</html>

您可以使用任何有效的 CSS 颜色格式,包括 RGB:

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

D3.js 处理颜色等样式起始值和结束值之间的内插。对于数字属性或样式,它内插数字。对于复杂的值,如颜色或 transform 字符串,D3 使用专门的内插器(interpolators)。

D3 会根据正在改变的属性或样式自动选择合适的内插器。内插器是一个函数,给定一个通常介于 0(起始)和 1(结束)之间的值 t,它返回一个中间值。您可以通过 d3.interpolate*(a, b) 函数直接访问这些内插器:

  • d3.interpolateNumber(a, b): 内插两个数字 a 和 b。
  • d3.interpolateRgb(a, b): 内插两种颜色。颜色可以是 CSS 命名颜色或 RGB/HSL 字符串。
  • d3.interpolateString(a, b): 内插两个字符串。它会找到字符串中嵌入的数字并进行内插。
  • 还有许多其他专门的内插器(例如,用于 transform、数组、对象)。

虽然 D3 通常会自动处理这个问题,但您可以使用 attrTween 或 styleTween 为高级动画创建自定义内插。

delay() 方法指定过渡开始前的等待时间,单位为毫秒。

<!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 (等待 2 秒后开始)
.duration(2000) // Animation lasts 2 seconds (动画持续 2 秒)
.style("font-size", "28px");
</script>
</body>
</html>

了解过渡生命周期有助于协调复杂的动画和交互:

  1. 已调度 (Scheduled): 当调用 selection.transition() 时,过渡被调度。此时,您定义它的最终状态(目标属性/样式)和参数(持续时间、延迟、缓动)。
  2. 已启动 (Started): 过渡在其指定的 delay 之后开始。如果没有延迟,它会尽快开始(通常是几毫秒)。当每个元素的过渡开始时,会分派 start 事件。
  3. 正在运行 (Running): 在其 duration 期间,过渡根据缓动函数和内插器不断更新元素的属性。每一步实际上将元素从 t=0 移动到 t=1 通过内插。
  4. 已结束 (Ended): 过渡在其开始后经过持续时间后结束。会为每个元素分派 end 事件。元素的属性被精确地设置为其目标结束值。

您可以使用 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) (这里的 'this' 指的是 DOM 元素 (即 h3))
// Using a traditional function to preserve 'this' context (使用传统函数以保留 'this' 上下文)
d3.select(this).text("Animation Starting!");
})
.style("color", "red") // Target color (目标颜色)
.on("end", function() {
d3.select(this).text("Animation Ended!").style("color", "green");
});

或者,对于选择集中所有过渡完成后执行的操作,Promise 是更简洁的方法:

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 (例如)
});

动画是使 D3.js 可视化引人入胜且信息丰富的重要组成部分。