web-dataviz-d3
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseD3.js Data Visualization Patterns
D3.js 数据可视化模式
Quick Guide: D3 v7 is fully modular ES modules. Usefor the data join (replaces manual enter/update/exit). Prefer modular imports (selection.join(),d3-selection, etc.) to reduce bundle size. Scales map data domains to visual ranges; axes render tick marks from scales. Shape generators (d3-scale,d3.line,d3.arc) produce SVG path strings from data arrays. Transitions animate attribute/style changes with automatic interpolation. For framework integration, let D3 handle data computation (scales, layouts, shapes) and let your framework own the DOM.d3.area
<critical_requirements>
快速指南: D3 v7 是完全模块化的 ES 模块。使用实现数据连接(替代手动的 enter/update/exit 流程)。优先使用模块化导入(selection.join()、d3-selection等)以减小打包体积。比例尺用于将数据域映射到视觉范围;坐标轴基于比例尺渲染刻度。图形生成器(d3-scale、d3.line、d3.arc)从数据数组生成 SVG 路径字符串。过渡动画通过自动插值实现属性/样式的变化。框架集成时,让 D3 处理数据计算(比例尺、布局、图形),让框架负责 DOM 管理。d3.area
<critical_requirements>
CRITICAL: Before Using This Skill
重要提示:使用此技能之前
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,, named constants)import type
(You MUST use for data joins — NOT manual enter/append/merge/exit chains)
selection.join()(You MUST use modular imports (, , ) — NOT in production bundles)
d3-selectiond3-scaled3-shapeimport * as d3 from "d3"(You MUST use named constants for ALL visual dimensions, colors, and timing values — NO magic numbers)
(You MUST type D3 selections and scales with TypeScript generics — , )
Selection<SVGGElement, Datum, ...>ScaleLinear<number, number></critical_requirements>
Auto-detection: D3, d3, d3.js, d3-selection, d3-scale, d3-shape, d3-axis, d3-transition, d3-force, d3-geo, d3-zoom, d3-brush, d3-drag, d3-array, selection.join, data join, enter update exit, scaleLinear, scaleBand, scaleTime, axisBottom, axisLeft, forceSimulation, geoPath, geoMercator, line generator, arc generator, SVG visualization, data-driven documents
When to use:
- Building custom SVG/Canvas data visualizations from scratch
- Bindings between data arrays and DOM elements (the data join)
- Mapping data domains to pixel ranges (scales and axes)
- Generating SVG paths from data (lines, arcs, areas, pies)
- Animating data transitions with interpolated attributes
- Force-directed graph layouts and geographic map projections
- Adding zoom, brush, and drag interactions to visualizations
When NOT to use:
- Standard chart types (bar, line, pie) with minimal customization — use a charting library built on D3
- Dashboards with many chart widgets — use a higher-level charting library
- Simple data tables or non-graphical data display
Key patterns covered:
- Selections and the data join ()
selection.data().join() - Scales (linear, band, time, ordinal) and axes
- Shape generators (line, area, arc, pie, stack)
- Transitions and animated updates
- Force-directed graph layouts
- Geographic projections and choropleth maps
- Zoom, brush, and drag interactions
- Framework integration: D3 for math, framework for DOM
- Responsive SVG with viewBox
- TypeScript typing for D3
Detailed Resources:
- examples/core.md - Selections, data joins, scales, axes, shapes, responsive SVG
- examples/interaction.md - Transitions, zoom, brush, drag, tooltips
- examples/advanced.md - Force layouts, geo projections, framework integration patterns
- reference.md - Module reference, decision frameworks, anti-patterns
<philosophy>
所有代码必须遵循 CLAUDE.md 中的项目规范(短横线命名、命名导出、导入顺序、、命名常量)import type
(必须使用 进行数据连接——禁止手动的 enter/append/merge/exit 链式调用)
selection.join()(必须使用模块化导入(、、)——生产环境打包中禁止使用 )
d3-selectiond3-scaled3-shapeimport * as d3 from "d3"(所有视觉尺寸、颜色和时间值必须使用命名常量——禁止魔法数字)
(必须使用 TypeScript 泛型为 D3 选择器和比例尺添加类型——、)
Selection<SVGGElement, Datum, ...>ScaleLinear<number, number></critical_requirements>
自动检测关键词: D3、d3、d3.js、d3-selection、d3-scale、d3-shape、d3-axis、d3-transition、d3-force、d3-geo、d3-zoom、d3-brush、d3-drag、d3-array、selection.join、data join、enter update exit、scaleLinear、scaleBand、scaleTime、axisBottom、axisLeft、forceSimulation、geoPath、geoMercator、line generator、arc generator、SVG visualization、data-driven documents
适用场景:
- 从零构建自定义 SVG/Canvas 数据可视化
- 数据数组与 DOM 元素的绑定(数据连接)
- 将数据域映射到像素范围(比例尺和坐标轴)
- 从数据生成 SVG 路径(折线、圆弧、面积、饼图)
- 通过插值属性实现数据过渡动画
- 力导向图布局和地理地图投影
- 为可视化添加缩放、刷选和拖拽交互
不适用场景:
- 只需少量自定义的标准图表类型(柱状图、折线图、饼图)——使用基于 D3 构建的图表库
- 包含多个图表组件的仪表盘——使用更高层级的图表库
- 简单的数据表格或非图形化的数据展示
涵盖的核心模式:
- 选择器与数据连接()
selection.data().join() - 比例尺(线性、带状、时间、序数)与坐标轴
- 图形生成器(折线、面积、圆弧、饼图、堆叠)
- 过渡与动画更新
- 力导向图布局
- 地理投影与分级统计图
- 缩放、刷选和拖拽交互
- 框架集成:D3 负责计算,框架负责 DOM
- 带 viewBox 的响应式 SVG
- D3 的 TypeScript 类型定义
详细资源:
- examples/core.md - 选择器、数据连接、比例尺、坐标轴、图形、响应式 SVG
- examples/interaction.md - 过渡动画、缩放、刷选、拖拽、工具提示
- examples/advanced.md - 力导向布局、地理投影、框架集成模式
- reference.md - 模块参考、决策框架、反模式
<philosophy>
Philosophy
设计理念
D3 is a low-level visualization grammar, not a charting library. It provides primitives for binding data to DOM elements and applying data-driven transformations. This gives maximum control at the cost of more code than higher-level alternatives.
Core mental model:
- Select elements (existing or placeholder)
- Bind data to selections with
.data() - Join to create/update/remove elements with
.join() - Encode data as visual attributes with scales
- Annotate with axes, labels, legends
- Animate changes with transitions
D3 v7 key decisions:
- Pure ES modules — use modular imports for tree-shaking
- replaces manual enter/update/exit boilerplate
selection.join() - TypeScript types ship with each module (no needed for core packages, but available for convenience)
@types/d3 - Works with any framework — D3 handles computation, your framework handles DOM rendering
When NOT to use D3 directly:
- Standard charts with minimal customization (use a charting library)
- Rapid prototyping where development speed matters more than customization
- Teams without SVG/visualization experience
<patterns>
D3 是一个底层可视化语法,而非图表库。它提供了将数据绑定到 DOM 元素并应用数据驱动变换的原语。这带来了最大的控制权,但相比高层级替代方案需要编写更多代码。
核心思维模型:
- 选择元素(现有元素或占位符)
- 绑定数据到选择器(使用 )
.data() - 连接以创建/更新/移除元素(使用 )
.join() - 编码数据为视觉属性(使用比例尺)
- 标注坐标轴、标签、图例
- 动画变化(使用过渡)
D3 v7 关键决策:
- 纯 ES 模块——使用模块化导入实现摇树优化
- 替代手动的 enter/update/exit 样板代码
selection.join() - TypeScript 类型随每个模块发布(核心包无需 ,但为方便仍可使用)
@types/d3 - 兼容任意框架——D3 处理计算,框架处理 DOM 渲染
何时不应直接使用 D3:
- 只需少量自定义的标准图表(使用图表库)
- 开发速度优先于定制化的快速原型开发
- 团队缺乏 SVG/可视化经验
<patterns>
Core Patterns
核心模式
Pattern 1: Selections and the Data Join
模式 1:选择器与数据连接
The data join is D3's core pattern: bind an array of data to DOM elements, then use to create, update, and remove elements as data changes.
.join()typescript
import { select } from "d3-selection";
const BAR_HEIGHT = 30;
const BAR_GAP = 5;
// Select, bind data, join
select(svgElement)
.selectAll<SVGRectElement, number>("rect")
.data(values, (d) => String(d)) // key function for identity
.join("rect") // enter + update merged
.attr("y", (_, i) => i * (BAR_HEIGHT + BAR_GAP))
.attr("width", (d) => xScale(d))
.attr("height", BAR_HEIGHT);Why good: handles enter/update/exit in one call, key function ensures correct element-data binding across updates, typed selection generics
join("rect")For advanced join with separate enter/update/exit callbacks, see examples/core.md Pattern 1.
数据连接是 D3 的核心模式:将数据数组绑定到 DOM 元素,然后使用 在数据变化时创建、更新和移除元素。
.join()typescript
import { select } from "d3-selection";
const BAR_HEIGHT = 30;
const BAR_GAP = 5;
// 选择、绑定数据、连接
select(svgElement)
.selectAll<SVGRectElement, number>("rect")
.data(values, (d) => String(d)) // 用于标识的键函数
.join("rect") // 合并 enter + update
.attr("y", (_, i) => i * (BAR_HEIGHT + BAR_GAP))
.attr("width", (d) => xScale(d))
.attr("height", BAR_HEIGHT);优势: 一次调用处理 enter/update/exit,键函数确保更新时元素与数据的正确绑定,带类型的选择器泛型
join("rect")如需带单独 enter/update/exit 回调的高级连接,请查看 examples/core.md 模式 1。
Pattern 2: Scales — Mapping Data to Pixels
模式 2:比例尺——数据到像素的映射
Scales are functions that map an input domain (data values) to an output range (pixel positions, colors).
typescript
import { scaleLinear, scaleBand, scaleTime, scaleOrdinal } from "d3-scale";
const CHART_WIDTH = 600;
const CHART_HEIGHT = 400;
// Continuous: numbers -> pixels
const x = scaleLinear<number>()
.domain([0, max(data, (d) => d.value)!])
.range([0, CHART_WIDTH]);
// Categorical: strings -> pixel bands (bar charts)
const y = scaleBand<string>()
.domain(data.map((d) => d.label))
.range([0, CHART_HEIGHT])
.padding(0.1);
// Time: Date -> pixels
const timeScale = scaleTime<number>()
.domain([startDate, endDate])
.range([0, CHART_WIDTH]);Why good: TypeScript generics on scales, domain derived from data with , on band scale for gaps between bars
max().padding()See examples/core.md Pattern 2 for ordinal color scales and / patterns.
scaleLogscaleSqrt比例尺是将输入域(数据值)映射到输出范围(像素位置、颜色)的函数。
typescript
import { scaleLinear, scaleBand, scaleTime, scaleOrdinal } from "d3-scale";
const CHART_WIDTH = 600;
const CHART_HEIGHT = 400;
// 连续型:数字 -> 像素
const x = scaleLinear<number>()
.domain([0, max(data, (d) => d.value)!])
.range([0, CHART_WIDTH]);
// 分类型:字符串 -> 像素带(柱状图)
const y = scaleBand<string>()
.domain(data.map((d) => d.label))
.range([0, CHART_HEIGHT])
.padding(0.1);
// 时间型:Date -> 像素
const timeScale = scaleTime<number>()
.domain([startDate, endDate])
.range([0, CHART_WIDTH]);优势: 带 TypeScript 泛型的比例尺,从数据推导域(使用 ),带 的带状比例尺实现柱状图间距
max().padding()如需序数颜色比例尺和 / 模式,请查看 examples/core.md 模式 2。
scaleLogscaleSqrtPattern 3: Axes — Rendering Scale Tick Marks
模式 3:坐标轴——渲染比例尺刻度
Axes are SVG groups generated from a scale. Render with .
selection.call(axis)typescript
import { axisBottom, axisLeft } from "d3-axis";
import { format } from "d3-format";
const TICK_COUNT = 5;
// Create axes from scales
const xAxis = axisBottom(xScale).ticks(TICK_COUNT).tickFormat(format(",.0f"));
const yAxis = axisLeft(yScale);
// Render into <g> containers
svg
.append("g")
.attr("transform", `translate(0,${CHART_HEIGHT - MARGIN_BOTTOM})`)
.call(xAxis);
svg.append("g").attr("transform", `translate(${MARGIN_LEFT},0)`).call(yAxis);Why good: axes derived from scales (always in sync), pattern for reusable rendering, for tick label formatting
selection.call()d3-formatSee examples/core.md Pattern 3 for time axes and grid line patterns.
坐标轴是基于比例尺生成的 SVG 组,使用 渲染。
selection.call(axis)typescript
import { axisBottom, axisLeft } from "d3-axis";
import { format } from "d3-format";
const TICK_COUNT = 5;
// 从比例尺创建坐标轴
const xAxis = axisBottom(xScale).ticks(TICK_COUNT).tickFormat(format(",.0f"));
const yAxis = axisLeft(yScale);
// 渲染到 <g> 容器中
svg
.append("g")
.attr("transform", `translate(0,${CHART_HEIGHT - MARGIN_BOTTOM})`)
.call(xAxis);
svg.append("g").attr("transform", `translate(${MARGIN_LEFT},0)`).call(yAxis);优势: 坐标轴基于比例尺生成(始终保持同步), 模式实现可复用渲染, 用于刻度标签格式化
selection.call()d3-format如需时间坐标轴和网格线模式,请查看 examples/core.md 模式 3。
Pattern 4: Shape Generators — SVG Paths from Data
模式 4:图形生成器——从数据生成 SVG 路径
Shape generators are functions that take data arrays and produce SVG attribute strings.
dtypescript
import { line, area, arc, pie, curveMonotoneX } from "d3-shape";
import type { PieArcDatum } from "d3-shape";
// Line generator
const lineGen = line<DataPoint>()
.x((d) => xScale(d.date))
.y((d) => yScale(d.value))
.curve(curveMonotoneX);
svg
.append("path")
.datum(data)
.attr("d", lineGen)
.attr("fill", "none")
.attr("stroke", "steelblue");
// Pie + arc generators
const INNER_RADIUS = 0;
const OUTER_RADIUS = 150;
const pieGen = pie<SliceData>().value((d) => d.value);
const arcGen = arc<PieArcDatum<SliceData>>()
.innerRadius(INNER_RADIUS)
.outerRadius(OUTER_RADIUS);
svg
.selectAll("path")
.data(pieGen(sliceData))
.join("path")
.attr("d", arcGen)
.attr("fill", (d) => colorScale(d.data.label));Why good: generators configured once then reused, for smooth interpolation, typed generic for pie data, named radius constants
.curve()PieArcDatumSee examples/core.md Pattern 4 for area charts and stacked layouts.
图形生成器是接收数据数组并生成 SVG 属性字符串的函数。
dtypescript
import { line, area, arc, pie, curveMonotoneX } from "d3-shape";
import type { PieArcDatum } from "d3-shape";
// 折线生成器
const lineGen = line<DataPoint>()
.x((d) => xScale(d.date))
.y((d) => yScale(d.value))
.curve(curveMonotoneX);
svg
.append("path")
.datum(data)
.attr("d", lineGen)
.attr("fill", "none")
.attr("stroke", "steelblue");
// 饼图 + 圆弧生成器
const INNER_RADIUS = 0;
const OUTER_RADIUS = 150;
const pieGen = pie<SliceData>().value((d) => d.value);
const arcGen = arc<PieArcDatum<SliceData>>()
.innerRadius(INNER_RADIUS)
.outerRadius(OUTER_RADIUS);
svg
.selectAll("path")
.data(pieGen(sliceData))
.join("path")
.attr("d", arcGen)
.attr("fill", (d) => colorScale(d.data.label));优势: 生成器配置一次即可复用, 实现平滑插值,带类型的 泛型用于饼图数据,命名的半径常量
.curve()PieArcDatum如需面积图和堆叠布局,请查看 examples/core.md 模式 4。
Pattern 5: Responsive SVG with viewBox
模式 5:带 viewBox 的响应式 SVG
Use the attribute so SVG scales to its container without JS resize handlers.
viewBoxtypescript
const VIEWBOX_WIDTH = 960;
const VIEWBOX_HEIGHT = 500;
const svg = select(container)
.append("svg")
.attr("viewBox", `0 0 ${VIEWBOX_WIDTH} ${VIEWBOX_HEIGHT}`)
.attr("preserveAspectRatio", "xMidYMid meet")
.style("width", "100%")
.style("height", "auto");Why good: SVG scales automatically, no resize listeners needed, chart dimensions stay consistent regardless of container size
For dynamic resizing with , see examples/core.md Pattern 5.
ResizeObserver使用 属性让 SVG 自适应容器大小,无需 JS resize 处理器。
viewBoxtypescript
const VIEWBOX_WIDTH = 960;
const VIEWBOX_HEIGHT = 500;
const svg = select(container)
.append("svg")
.attr("viewBox", `0 0 ${VIEWBOX_WIDTH} ${VIEWBOX_HEIGHT}`)
.attr("preserveAspectRatio", "xMidYMid meet")
.style("width", "100%")
.style("height", "auto");优势: SVG 自动缩放,无需 resize 监听器,无论容器大小如何,图表尺寸保持一致
如需使用 实现动态缩放,请查看 examples/core.md 模式 5。
ResizeObserverPattern 6: Transitions — Animated Data Updates
模式 6:过渡动画——数据更新动画
Transitions interpolate attributes and styles over time with easing.
typescript
import { transition } from "d3-transition";
import { easeCubicOut } from "d3-ease";
const TRANSITION_DURATION_MS = 750;
const STAGGER_DELAY_MS = 50;
select(svgElement)
.selectAll<SVGRectElement, DataPoint>("rect")
.data(newData, (d) => d.id)
.join(
(enter) =>
enter
.append("rect")
.attr("width", 0)
.call((s) =>
s
.transition()
.duration(TRANSITION_DURATION_MS)
.attr("width", (d) => xScale(d.value)),
),
(update) =>
update.call((s) =>
s
.transition()
.duration(TRANSITION_DURATION_MS)
.attr("width", (d) => xScale(d.value)),
),
(exit) =>
exit.call((s) =>
s
.transition()
.duration(TRANSITION_DURATION_MS)
.attr("width", 0)
.remove(),
),
);Why good: enter/update/exit each have distinct animated behavior, stagger creates cascading effect, named timing constants
See examples/interaction.md Pattern 1 for easing functions and chained transitions.
过渡动画通过缓动效果随时间插值属性和样式。
typescript
import { transition } from "d3-transition";
import { easeCubicOut } from "d3-ease";
const TRANSITION_DURATION_MS = 750;
const STAGGER_DELAY_MS = 50;
select(svgElement)
.selectAll<SVGRectElement, DataPoint>("rect")
.data(newData, (d) => d.id)
.join(
(enter) =>
enter
.append("rect")
.attr("width", 0)
.call((s) =>
s
.transition()
.duration(TRANSITION_DURATION_MS)
.attr("width", (d) => xScale(d.value)),
),
(update) =>
update.call((s) =>
s
.transition()
.duration(TRANSITION_DURATION_MS)
.attr("width", (d) => xScale(d.value)),
),
(exit) =>
exit.call((s) =>
s
.transition()
.duration(TRANSITION_DURATION_MS)
.attr("width", 0)
.remove(),
),
);优势: enter/update/exit 各有不同的动画行为,交错延迟创建级联效果,命名的时间常量
如需缓动函数和链式过渡,请查看 examples/interaction.md 模式 1。
Pattern 7: Zoom and Pan
模式 7:缩放与平移
Apply zoom behavior with and transform the visualization on zoom events.
d3.zoom()typescript
import { zoom, zoomIdentity } from "d3-zoom";
import type { D3ZoomEvent } from "d3-zoom";
const MIN_ZOOM = 0.5;
const MAX_ZOOM = 32;
const zoomBehavior = zoom<SVGSVGElement, unknown>()
.scaleExtent([MIN_ZOOM, MAX_ZOOM])
.on("zoom", (event: D3ZoomEvent<SVGSVGElement, unknown>) => {
chartGroup.attr("transform", event.transform.toString());
});
svg.call(zoomBehavior);Why good: typed zoom event and element generics, scale extent prevents over-zoom, transform applied to inner group (not the SVG itself)
See examples/interaction.md Pattern 2 for semantic zoom and programmatic zoom controls.
使用 应用缩放行为,并在缩放事件时变换可视化内容。
d3.zoom()typescript
import { zoom, zoomIdentity } from "d3-zoom";
import type { D3ZoomEvent } from "d3-zoom";
const MIN_ZOOM = 0.5;
const MAX_ZOOM = 32;
const zoomBehavior = zoom<SVGSVGElement, unknown>()
.scaleExtent([MIN_ZOOM, MAX_ZOOM])
.on("zoom", (event: D3ZoomEvent<SVGSVGElement, unknown>) => {
chartGroup.attr("transform", event.transform.toString());
});
svg.call(zoomBehavior);优势: 带类型的缩放事件和元素泛型,缩放范围限制防止过度缩放,变换应用到内部组(而非 SVG 本身)
如需语义缩放和程序化缩放控制,请查看 examples/interaction.md 模式 2。
Pattern 8: Force-Directed Graph Layout
模式 8:力导向图布局
Force simulations position nodes using physics-based forces (repulsion, attraction, centering).
typescript
import {
forceSimulation,
forceLink,
forceManyBody,
forceCenter,
forceCollide,
} from "d3-force";
const CHARGE_STRENGTH = -300;
const COLLISION_RADIUS = 5;
const simulation = forceSimulation(nodes)
.force(
"link",
forceLink(links)
.id((d: NodeDatum) => d.id)
.distance(100),
)
.force("charge", forceManyBody().strength(CHARGE_STRENGTH))
.force("center", forceCenter(width / 2, height / 2))
.force("collide", forceCollide(COLLISION_RADIUS))
.on("tick", () => {
// Update node and link positions from simulation
});Why good: named force constants, accessor for node identity, for link length, collision prevents overlap
.id().distance()See examples/advanced.md Pattern 1 for complete graph rendering with drag interaction.
力导向模拟使用基于物理的力(排斥、吸引、居中)定位节点。
typescript
import {
forceSimulation,
forceLink,
forceManyBody,
forceCenter,
forceCollide,
} from "d3-force";
const CHARGE_STRENGTH = -300;
const COLLISION_RADIUS = 5;
const simulation = forceSimulation(nodes)
.force(
"link",
forceLink(links)
.id((d: NodeDatum) => d.id)
.distance(100),
)
.force("charge", forceManyBody().strength(CHARGE_STRENGTH))
.force("center", forceCenter(width / 2, height / 2))
.force("collide", forceCollide(COLLISION_RADIUS))
.on("tick", () => {
// 从模拟更新节点和链接位置
});优势: 命名的力常量, 访问器用于节点标识, 控制链接长度,碰撞力防止重叠
.id().distance()如需带拖拽交互的完整图渲染,请查看 examples/advanced.md 模式 1。
Pattern 9: Geographic Projections
模式 9:地理投影
Project geographic coordinates onto a 2D plane and render GeoJSON features as SVG paths.
typescript
import { geoMercator, geoPath, geoNaturalEarth1 } from "d3-geo";
import type { GeoPermissibleObjects } from "d3-geo";
const projection = geoNaturalEarth1().fitSize(
[CHART_WIDTH, CHART_HEIGHT],
geoJsonData,
);
const pathGenerator = geoPath().projection(projection);
svg
.selectAll("path")
.data(geoJsonData.features)
.join("path")
.attr("d", pathGenerator)
.attr("fill", (d) => colorScale(dataByRegion.get(d.properties.id) ?? 0));Why good: auto-scales projection to container, path generator produces strings from GeoJSON, color encodes data values per region
fitSizedSee examples/advanced.md Pattern 2 for choropleth maps and interactive tooltips.
将地理坐标投影到 2D 平面,并将 GeoJSON 要素渲染为 SVG 路径。
typescript
import { geoMercator, geoPath, geoNaturalEarth1 } from "d3-geo";
import type { GeoPermissibleObjects } from "d3-geo";
const projection = geoNaturalEarth1().fitSize(
[CHART_WIDTH, CHART_HEIGHT],
geoJsonData,
);
const pathGenerator = geoPath().projection(projection);
svg
.selectAll("path")
.data(geoJsonData.features)
.join("path")
.attr("d", pathGenerator)
.attr("fill", (d) => colorScale(dataByRegion.get(d.properties.id) ?? 0));优势: 自动缩放投影以适配容器,路径生成器从 GeoJSON 生成 字符串,颜色编码各区域的数据值
fitSized如需分级统计图和交互式工具提示,请查看 examples/advanced.md 模式 2。
Pattern 10: Framework Integration
模式 10:框架集成
When using D3 with a component framework, split responsibilities: D3 computes layouts, scales, and shapes; your framework renders the DOM.
typescript
// Pattern: D3 for computation, framework for rendering
// In your component:
const xScale = scaleLinear().domain([0, maxValue]).range([0, width]);
const yScale = scaleBand().domain(labels).range([0, height]).padding(0.1);
const linePath = line<DataPoint>()
.x((d) => xScale(d.x))
.y((d) => yScale(d.y)!)(data);
// Your framework renders SVG with computed values
// <svg><path d={linePath} /><rect width={xScale(d.value)} /></svg>When D3 must own the DOM (zoom, brush, drag, force tick): use a ref to an SVG element and call D3 in a lifecycle hook. Clean up the simulation/behavior on unmount.
See examples/advanced.md Pattern 3 for the ref-based integration pattern and cleanup.
</patterns>
<decision_framework>
在组件框架中使用 D3 时,拆分职责:D3 计算布局、比例尺和图形;框架负责渲染 DOM。
typescript
// 模式:D3 负责计算,框架负责渲染
// 在组件中:
const xScale = scaleLinear().domain([0, maxValue]).range([0, width]);
const yScale = scaleBand().domain(labels).range([0, height]).padding(0.1);
const linePath = line<DataPoint>()
.x((d) => xScale(d.x))
.y((d) => yScale(d.y)!)(data);
// 框架使用计算后的值渲染 SVG
// <svg><path d={linePath} /><rect width={xScale(d.value)} /></svg>当 D3 必须控制 DOM 时(缩放、刷选、拖拽、力导向 tick):使用 ref 获取 SVG 元素,并在生命周期钩子中调用 D3。在卸载时清理模拟/行为。
如需基于 ref 的集成模式和清理方法,请查看 examples/advanced.md 模式 3。
</patterns>
<decision_framework>
Decision Framework
决策框架
D3 Module Selection
D3 模块选择
What are you building?
|
+-> Bar/line/area chart?
| -> d3-selection, d3-scale, d3-axis, d3-shape, d3-array
|
+-> Pie/donut chart?
| -> d3-shape (pie + arc generators), d3-scale (color)
|
+-> Force-directed graph?
| -> d3-force, d3-selection, d3-drag
|
+-> Geographic map?
| -> d3-geo, d3-selection, d3-scale (color)
|
+-> Animated transitions?
| -> d3-transition, d3-ease, d3-interpolate
|
+-> Interactive (zoom/brush)?
-> d3-zoom or d3-brush, d3-selection你要构建什么?
|
+-> 柱状图/折线图/面积图?
| -> d3-selection, d3-scale, d3-axis, d3-shape, d3-array
|
+-> 饼图/环形图?
| -> d3-shape(pie + arc 生成器), d3-scale(颜色)
|
+-> 力导向图?
| -> d3-force, d3-selection, d3-drag
|
+-> 地理地图?
| -> d3-geo, d3-selection, d3-scale(颜色)
|
+-> 过渡动画?
| -> d3-transition, d3-ease, d3-interpolate
|
+-> 交互式(缩放/刷选)?
-> d3-zoom 或 d3-brush, d3-selectionFramework Integration Strategy
框架集成策略
Does the visualization need zoom, brush, drag, or force tick?
|
+-> NO -> D3 for computation only (scales, shapes, layouts)
| Your framework renders SVG/HTML directly
| Cleanest integration, fully declarative
|
+-> YES -> D3 owns the SVG via a ref element
Call D3 in a lifecycle hook (mount/update)
Clean up behaviors on unmount可视化需要缩放、刷选、拖拽或力导向 tick 吗?
|
+-> 不需要 -> D3 仅负责计算(比例尺、图形、布局)
| 框架直接渲染 SVG/HTML
| 最简洁的集成方式,完全声明式
|
+-> 需要 -> D3 通过 ref 元素控制 SVG
在生命周期钩子(挂载/更新)中调用 D3
在卸载时清理行为Scale Selection
比例尺选择
| Data Type | Scale | Example |
|---|---|---|
| Continuous | scaleLinear | Revenue, temperature |
| Categorical | scaleBand | Categories on bar chart |
| Categorical dots | scalePoint | Categories without width |
| Time series | scaleTime | Dates on x-axis |
| Logarithmic | scaleLog | Exponential data ranges |
| Square root | scaleSqrt | Bubble chart radius |
| Color categories | scaleOrdinal | Category -> color |
</decision_framework>
<red_flags>
| 数据类型 | 比例尺 | 示例 |
|---|---|---|
| 连续型 | scaleLinear | 收入、温度 |
| 分类型 | scaleBand | 柱状图上的分类 |
| 分类点 | scalePoint | 无宽度的分类 |
| 时间序列 | scaleTime | X 轴上的日期 |
| 对数型 | scaleLog | 指数数据范围 |
| 平方根型 | scaleSqrt | 气泡图半径 |
| 颜色分类 | scaleOrdinal | 分类 -> 颜色 |
</decision_framework>
<red_flags>
RED FLAGS
警示事项
High Priority Issues:
- Using in production — imports the entire 240KB+ bundle. Use modular imports:
import * as d3 from "d3"import { select } from "d3-selection" - Manual enter/append/merge/exit chains — use instead (simpler, fewer bugs)
selection.join() - Animating non-interpolable attributes (class names, boolean attributes) — only animate numeric attributes and colors
- Missing key function in when data identity matters — causes incorrect element-data binding on updates
.data(array, key) - Mutating data arrays bound to selections — D3 stores references; mutations cause stale renders
Medium Priority Issues:
- Magic numbers for margins, radii, durations, colors — use named constants
- Creating a new SVG on every data update — select the existing SVG, update data bindings
- Appending axes on every update (duplicated tick marks) — select existing and
<g>again, or use.call(axis)pattern.join() - Forgetting on exit — exiting elements stay in the DOM invisible
transition.remove() - Not using on linear scales — domain ends at awkward values like
.nice()[0, 473]
Gotchas & Edge Cases:
- returns the merged enter+update selection — chained attributes apply to both new and existing elements
selection.join() - returns the computed bar width — use it for rect width, not a hardcoded value
scaleBand().bandwidth() - returns
d3.max()for empty arrays — guard withundefinedor check array length first?? 0 - is per-element, not total — a 750ms transition on 100 elements still takes 750ms (not 75,000ms)
transition.duration() - runs asynchronously via
forceSimulation— stop it on unmount to prevent memory leaksrequestAnimationFrame - without a projection renders pre-projected coordinates — only omit projection if GeoJSON is already projected
geoPath - Zoom transform applied to the SVG root clips panned content — apply transform to an inner group instead
<g> - must be imported for
d3-transitionto exist — it extends the selection prototype via side effectselection.transition() - binds data to a single element without computing a join — use
.datum()for arrays,.data()for single objects.datum()
</red_flags>
<critical_reminders>
高优先级问题:
- 生产环境中使用 ——会导入整个 240KB+ 的包。使用模块化导入:
import * as d3 from "d3"import { select } from "d3-selection" - 手动的 enter/append/merge/exit 链式调用——改用 (更简单,更少 bug)
selection.join() - 对不可插值的属性(类名、布尔属性)执行动画——仅对数值属性和颜色执行动画
- 数据标识重要时,中缺少键函数——更新时会导致元素与数据绑定错误
.data(array, key) - 绑定到选择器的数据数组被修改——D3 存储引用;修改会导致渲染过期
中优先级问题:
- 边距、半径、时长、颜色使用魔法数字——使用命名常量
- 每次数据更新都创建新的 SVG——选择现有 SVG,更新数据绑定
- 每次更新都添加坐标轴(重复刻度)——选择现有的 并再次
<g>,或使用.call(axis)模式.join() - 退出时忘记 ——退出的元素仍会留在 DOM 中不可见
transition.remove() - 线性比例尺未使用 ——域会以尴尬的值结束,如
.nice()[0, 473]
陷阱与边缘情况:
- 返回合并后的 enter+update 选择器——链式属性会应用于新元素和现有元素
selection.join() - 返回计算后的柱状宽度——使用它设置矩形宽度,而非硬编码值
scaleBand().bandwidth() - 对空数组返回
d3.max()——使用undefined或先检查数组长度?? 0 - 是每个元素的时长,而非总时长——100 个元素的 750ms 过渡仍只需 750ms(不是 75,000ms)
transition.duration() - 通过
forceSimulation异步运行——卸载时停止它以防止内存泄漏requestAnimationFrame - 不带投影的 会渲染预投影坐标——仅当 GeoJSON 已投影时才省略投影
geoPath - 缩放变换应用到 SVG 根元素会裁剪平移内容——改为应用到内部 组
<g> - 必须导入 才能使用
d3-transition——它通过副作用扩展选择器原型selection.transition() - 将数据绑定到单个元素,不计算连接——数组使用
.datum(),单个对象使用.data().datum()
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
重要提醒
All code must follow project conventions in CLAUDE.md
(You MUST use for data joins — NOT manual enter/append/merge/exit chains)
selection.join()(You MUST use modular imports (, , ) — NOT in production bundles)
d3-selectiond3-scaled3-shapeimport * as d3 from "d3"(You MUST use named constants for ALL visual dimensions, colors, and timing values — NO magic numbers)
(You MUST type D3 selections and scales with TypeScript generics — , )
Selection<SVGGElement, Datum, ...>ScaleLinear<number, number>Failure to follow these rules will cause bloated bundles, incorrect data binding, and untyped visualization code.
</critical_reminders>
所有代码必须遵循 CLAUDE.md 中的项目规范
(必须使用 进行数据连接——禁止手动的 enter/append/merge/exit 链式调用)
selection.join()(必须使用模块化导入(、、)——生产环境打包中禁止使用 )
d3-selectiond3-scaled3-shapeimport * as d3 from "d3"(所有视觉尺寸、颜色和时间值必须使用命名常量——禁止魔法数字)
(必须使用 TypeScript 泛型为 D3 选择器和比例尺添加类型——、)
Selection<SVGGElement, Datum, ...>ScaleLinear<number, number>不遵循这些规则会导致包体积过大、数据绑定错误和无类型的可视化代码。
</critical_reminders>