diagram-animation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Diagram & Data Animation

图表与数据动画

Make structure and data legible by revealing it over time — one idea per beat. Build progressive node/edge reveals, flowing connectors, and animated charts (bars grow, lines draw, numbers count up) for web or video.
通过随时间逐步展示内容,让结构与数据更易读——每次呈现一个核心信息。为网页或视频构建渐进式节点/连接线展示、流动连接线,以及动画图表(柱状图增长、线条绘制、数字递增)。

When to use

使用场景

  • Architecture, flow, or sequence diagrams that build step by step.
  • Animated charts: bars grow, lines draw on, numbers count up.
  • "How it works" walkthroughs and process explainers.
  • Showing data or signal flowing along connectors.
  • 需分步构建的架构图、流程图或序列图。
  • 动画图表:柱状图增长、线条绘制、数字递增。
  • “工作原理”演示与流程讲解。
  • 展示数据或信号沿连接线流动的效果。

Core principle: progressive disclosure

核心原则:渐进式披露

Never show everything at once. Reveal in this order: nodes → edges → labels. Highlight the active element and dim the rest (focus + context). Pace to comprehension, not flash — hold each step 0.5–1.5s so it lands. Keep a consistent visual grammar: color means meaning, and that mapping never changes mid-piece.
切勿一次性展示全部内容。按照以下顺序逐步呈现:节点 → 连接线 → 标签。高亮当前元素,调暗其余元素(聚焦+上下文)。节奏以理解为基准,而非追求花哨——每个步骤停留0.5–1.5秒,确保用户消化内容。保持统一的视觉规则:颜色代表特定含义,且该映射在整个作品中始终不变。

Quick reference

速查指南

GoalMechanismEasing
Node appearscale 0.8→1 + opacity,
transform-origin:center
back.out(1.5)
/
easeOut
Edge connect
stroke-dashoffset
len→0
easeInOut
, 0.4–0.7s
Flowing dataanimate
stroke-dashoffset
looping + traveling dot
linear
, infinite
Bar grow
scaleY
0→1,
transform-origin:bottom
easeOut
, stagger 0.06s
Line chartpath
stroke-dashoffset
len→0
easeInOut
, 0.8–1.2s
Count-upinterpolate value with eased
t
easeOutCubic
Highlightactive full-opacity, others
opacity:.35
0.3s
目标实现方式缓动效果
节点出现scale 0.8→1 + opacity,
transform-origin:center
back.out(1.5)
/
easeOut
连接线绘制
stroke-dashoffset
len→0
easeInOut
, 0.4–0.7s
数据流动循环动画
stroke-dashoffset
+ 移动点
linear
, infinite
柱状图增长
scaleY
0→1,
transform-origin:bottom
easeOut
, stagger 0.06s
折线图绘制路径
stroke-dashoffset
len→0
easeInOut
, 0.8–1.2s
数字递增使用缓动
t
插值数值
easeOutCubic
高亮效果当前元素完全不透明,其余元素
opacity:.35
0.3s

Progressive node/edge reveal (SVG + GSAP)

渐进式节点/连接线展示(SVG + GSAP)

html
<svg viewBox="0 0 400 200" id="flow">
  <g class="node" opacity="0"><circle cx="60" cy="100" r="28" fill="#0A84FF"/></g>
  <g class="node" opacity="0"><circle cx="200" cy="100" r="28" fill="#0A84FF"/></g>
  <g class="node" opacity="0"><circle cx="340" cy="100" r="28" fill="#0A84FF"/></g>
  <path class="edge" d="M88 100 H172" stroke="#888" stroke-width="3" fill="none" pathLength="1"/>
  <path class="edge" d="M228 100 H312" stroke="#888" stroke-width="3" fill="none" pathLength="1"/>
</svg>
<script type="module">
  import gsap from "https://cdn.jsdelivr.net/npm/gsap/+esm";
  gsap.set(".edge", { strokeDasharray: 1, strokeDashoffset: 1 });
  const tl = gsap.timeline({ defaults: { ease: "power2.out" } });
  tl.to(".node", { opacity: 1, scale: 1, transformOrigin: "center",
                   duration: .45, stagger: .35, ease: "back.out(1.6)", startAt: { scale: .8 } })
    .to(".edge", { strokeDashoffset: 0, duration: .5, stagger: .35 }, "-=0.9");
</script>
pathLength="1"
lets one dash value drive any edge length. Reveal nodes first, then draw edges between the now-visible nodes.
html
<svg viewBox="0 0 400 200" id="flow">
  <g class="node" opacity="0"><circle cx="60" cy="100" r="28" fill="#0A84FF"/></g>
  <g class="node" opacity="0"><circle cx="200" cy="100" r="28" fill="#0A84FF"/></g>
  <g class="node" opacity="0"><circle cx="340" cy="100" r="28" fill="#0A84FF"/></g>
  <path class="edge" d="M88 100 H172" stroke="#888" stroke-width="3" fill="none" pathLength="1"/>
  <path class="edge" d="M228 100 H312" stroke="#888" stroke-width="3" fill="none" pathLength="1"/>
</svg>
<script type="module">
  import gsap from "https://cdn.jsdelivr.net/npm/gsap/+esm";
  gsap.set(".edge", { strokeDasharray: 1, strokeDashoffset: 1 });
  const tl = gsap.timeline({ defaults: { ease: "power2.out" } });
  tl.to(".node", { opacity: 1, scale: 1, transformOrigin: "center",
                   duration: .45, stagger: .35, ease: "back.out(1.6)", startAt: { scale: .8 } })
    .to(".edge", { strokeDashoffset: 0, duration: .5, stagger: .35 }, "-=0.9");
</script>
pathLength="1"
可让单个虚线值适配任意长度的连接线。先展示节点,再在已可见的节点之间绘制连接线。

Flowing connector (marching dash + traveling dot)

流动连接线(行进虚线 + 移动点)

css
.flow-line { stroke-dasharray: 8 8; animation: march 0.6s linear infinite; }
@keyframes march { to { stroke-dashoffset: -16; } }   /* = sum of dash pattern */
@media (prefers-reduced-motion: reduce){ .flow-line { animation: none; } }
Traveling dot along the path (
offset-path
, no JS):
css
.packet { offset-path: path("M88 100 H312"); animation: travel 1.4s linear infinite; }
@keyframes travel { to { offset-distance: 100%; } }
css
.flow-line { stroke-dasharray: 8 8; animation: march 0.6s linear infinite; }
@keyframes march { to { stroke-dashoffset: -16; } }   /* = sum of dash pattern */
@media (prefers-reduced-motion: reduce){ .flow-line { animation: none; } }
沿路径移动的点(使用
offset-path
,无需JS):
css
.packet { offset-path: path("M88 100 H312"); animation: travel 1.4s linear infinite; }
@keyframes travel { to { offset-distance: 100%; } }

Charts

图表动画

Bar grow (scaleY from the baseline):
css
.bar { transform: scaleY(0); transform-origin: bottom; }
.bar.in { transform: scaleY(1); transition: transform .6s cubic-bezier(.22,1,.36,1); }
/* stagger via inline transition-delay per bar */
Line chart draw-on (the whole series path strokes in):
js
const len = pathEl.getTotalLength();
pathEl.style.strokeDasharray = len;
pathEl.style.strokeDashoffset = len;
pathEl.getBoundingClientRect();                 // force reflow
pathEl.style.transition = "stroke-dashoffset 1s ease-in-out";
pathEl.style.strokeDashoffset = "0";
Count-up with eased interpolation (rAF):
js
function countUp(el, to, dur = 1200) {
  const t0 = performance.now();
  const fmt = new Intl.NumberFormat();
  (function tick(now){
    const k = Math.min(1, (now - t0) / dur);
    const e = 1 - Math.pow(1 - k, 3);           // easeOutCubic
    el.textContent = fmt.format(Math.round(to * e));
    if (k < 1) requestAnimationFrame(tick);
  })(t0);
}
柱状图增长(从基线开始scaleY):
css
.bar { transform: scaleY(0); transform-origin: bottom; }
.bar.in { transform: scaleY(1); transition: transform .6s cubic-bezier(.22,1,.36,1); }
/* stagger via inline transition-delay per bar */
折线图绘制(整个系列路径逐步描边):
js
const len = pathEl.getTotalLength();
pathEl.style.strokeDasharray = len;
pathEl.style.strokeDashoffset = len;
pathEl.getBoundingClientRect();                 // force reflow
pathEl.style.transition = "stroke-dashoffset 1s ease-in-out";
pathEl.style.strokeDashoffset = "0";
带缓动插值的数字递增(使用rAF):
js
function countUp(el, to, dur = 1200) {
  const t0 = performance.now();
  const fmt = new Intl.NumberFormat();
  (function tick(now){
    const k = Math.min(1, (now - t0) / dur);
    const e = 1 - Math.pow(1 - k, 3);           // easeOutCubic
    el.textContent = fmt.format(Math.round(to * e));
    if (k < 1) requestAnimationFrame(tick);
  })(t0);
}

Build-tool choice

构建工具选择

  • Inline animated SVG + GSAP/CSS: best for bespoke diagrams and full control on the web.
  • Framer Motion (React): declarative reveals via
    variants
    +
    staggerChildren
    ; pairs with Visx/Recharts for charts.
  • D3 + transitions: data-bound charts;
    .transition().duration().attr()
    for grow/draw,
    tween
    for count-up.
  • Mermaid / Excalidraw: author the diagram structure fast, render to SVG, then animate the SVG.
  • Remotion (React): data-driven, deterministic renders for video output and repeatable exports.
  • 内嵌动画SVG + GSAP/CSS:最适合定制化图表,在网页上可实现完全控制。
  • Framer Motion (React):通过
    variants
    +
    staggerChildren
    实现声明式展示;可搭配Visx/Recharts实现图表动画。
  • D3 + transitions:数据绑定图表;使用
    .transition().duration().attr()
    实现增长/绘制效果,
    tween
    实现数字递增。
  • Mermaid / Excalidraw:快速绘制图表结构,渲染为SVG后再为其添加动画。
  • Remotion (React):数据驱动的确定性渲染,适用于视频输出和可重复导出。

Output checklist

输出检查清单

  • Reveal order is nodes → edges → labels; nothing dumps in all at once.
  • Each step holds long enough to read (0.5–1.5s).
  • Color grammar is consistent and meaningful.
  • Active element highlighted, context dimmed.
  • prefers-reduced-motion
    path shows the final composed diagram without looping motion.
  • 展示顺序为节点→连接线→标签;切勿一次性全部加载。
  • 每个步骤停留时间足够用户阅读(0.5–1.5秒)。
  • 颜色规则统一且具有明确含义。
  • 当前元素高亮显示,上下文元素调暗。
  • prefers-reduced-motion
    模式下展示最终合成的图表,无循环动画。

Deliver & verify (standalone HTML)

交付与验证(独立HTML文件)

Packaged helper (
scripts/
):
scripts/seek-shot.sh anim.html 0 1.5 3
freezes the
?t=N
harness and screenshots each moment;
scripts/contact-sheet.sh sheet.png frame-*.png
tiles them for one-glance review. See
scripts/README.md
.
For a self-contained diagram (reveal, flowing connector, animated chart) the deliverable is one HTML file that opens directly in a browser — no build step, no render pipeline. A single file is the right tier for web motion. (If the diagram is part of a rendered video, build it as a Remotion composition instead and verify via
remotion still
— see remotion-video.)
Output contract:
  • One
    .html
    file: SVG inline; Framer Motion / D3 / anime / GSAP from CDN; the animation in one inline
    <script>
    (one driver).
  • A way to freeze an exact moment for screenshots, matched to how the diagram animates:
    • GSAP / JS timeline: build one master
      tl
      , then
      ?t=N
      tl.pause(); tl.seek(N)
      .
    • SVG SMIL:
      svg.pauseAnimations(); svg.setCurrentTime(N)
      .
    • CSS keyframes: drive reveal order with
      animationDelay
      ; freeze by pausing/seeking the equivalent JS timeline.
html
<script>
  // ... build your master timeline as `tl` ...
  const t = new URLSearchParams(location.search).get("t");
  if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); }  // frozen at t seconds
  window.__ready = true;                                    // ready signal for headless wait
  console.log("duration", tl.duration());
</script>
Verify loop — render → freeze → screenshot → check:
  1. Open the file at start / mid / end:
    …/diagram.html?t=0
    ,
    ?t=<dur/2>
    ,
    ?t=<dur>
    (read
    tl.duration()
    from the console).
  2. Headless-screenshot each frozen frame:
    bash
    npx playwright screenshot --wait-for-timeout=500 "file://$PWD/diagram.html?t=1.2" frame-mid.png
  3. Check fidelity — the step-by-step reveal appears in order (nodes → edges → labels, each step present at its frame), connectors point at the right nodes, count-ups land on the EXACT values — and artifacts (clipped labels, off-canvas nodes, FOUC before fonts, jank at seams).
Before you finish:
  1. Opens standalone in a browser — no console errors, no missing CDN.
  2. One driver/master timeline; the freeze (
    ?t=N
    /
    setCurrentTime
    ) lands on a deterministic still.
  3. Screenshotted at start / mid / end — reveal order correct, connectors land on the right nodes, numbers exact.
  4. prefers-reduced-motion
    shows the final composed diagram without looping motion.
  5. Color grammar consistent; active element highlighted, context dimmed; nothing dumps in all at once.
打包工具
scripts/
目录):
scripts/seek-shot.sh anim.html 0 1.5 3
可冻结
?t=N
机制并截取每个时刻的截图;
scripts/contact-sheet.sh sheet.png frame-*.png
可将截图拼接为一张预览图。详见
scripts/README.md
对于包含展示效果、流动连接线、动画图表的独立图表,交付物应为可直接在浏览器中打开的单个HTML文件——无需构建步骤,无需渲染流水线。单个文件是网页动画的最佳交付形式。(如果图表是渲染视频的一部分,则改为Remotion合成,并通过
remotion still
验证——详见remotion-video。)
输出约定:
  • 单个
    .html
    文件:内嵌SVG;Framer Motion / D3 / anime / GSAP从CDN引入;动画逻辑放在单个内嵌
    <script>
    中(单一驱动)。
  • 提供冻结精确时刻以截图的方式,与图表动画逻辑匹配:
    • GSAP / JS时间线:构建主时间线
      tl
      ,通过
      ?t=N
      触发
      tl.pause(); tl.seek(N)
    • SVG SMIL:
      svg.pauseAnimations(); svg.setCurrentTime(N)
    • CSS关键帧:使用
      animationDelay
      控制展示顺序;通过暂停/跳转等效JS时间线实现冻结。
html
<script>
  // ... 构建主时间线`tl` ...
  const t = new URLSearchParams(location.search).get("t");
  if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); }  // 冻结在t秒处
  window.__ready = true;                                    // 无头模式就绪信号
  console.log("duration", tl.duration());
</script>
验证流程 — 渲染→冻结→截图→检查:
  1. 在起始/中间/结束时刻打开文件:
    …/diagram.html?t=0
    ?t=<dur/2>
    ?t=<dur>
    (从控制台读取
    tl.duration()
    )。
  2. 无头模式截取每个冻结帧:
    bash
    npx playwright screenshot --wait-for-timeout=500 "file://$PWD/diagram.html?t=1.2" frame-mid.png
  3. 检查保真度——分步展示顺序正确(节点→连接线→标签,每个步骤在对应帧中呈现)、连接线指向正确节点、数字递增最终值准确——以及瑕疵(标签被裁剪、节点超出画布、字体加载前的FOUC、衔接处卡顿)。
完成前检查:
  1. 可在浏览器中独立打开——无控制台错误,无CDN缺失。
  2. 单一驱动/主时间线;冻结功能(
    ?t=N
    /
    setCurrentTime
    )可定位到确定的静态画面。
  3. 已在起始/中间/结束时刻截图——展示顺序正确、连接线指向正确节点、数字准确。
  4. prefers-reduced-motion
    模式下展示最终合成的图表,无循环动画。
  5. 颜色规则统一;当前元素高亮,上下文元素调暗;无一次性全部加载的内容。

Reference files

参考文件

  • references/diagram-and-chart-recipes.md
    — fuller runnable code: staged node/edge reveal with labels, flowing-dash + traveling-dot connectors, bar/line/count-up chart recipes, sequence-diagram and architecture build patterns, and D3 / Framer Motion / Remotion implementations with easing notes.
  • references/diagram-and-chart-recipes.md
    — 完整可运行代码:带标签的分步节点/连接线展示、流动虚线+移动点连接线、柱状图/折线图/数字递增图表示例、序列图与架构图构建模式,以及带缓动说明的D3 / Framer Motion / Remotion实现。