名称:d3-visualization 描述:使用D3.js创建自定义、交互式数据可视化—从零开始构建条形/线/散点图,创建网络图或地理地图,将变化的数据绑定到视觉元素,添加缩放/平移/刷子交互,动画图表过渡,或当图表库(如Highcharts、Chart.js)不支持您的特定可视化设计,并且您需要对数据驱动的DOM操作、比例尺、形状和布局进行低级别控制时使用。
D3.js 数据可视化
目录
先读此部分
此技能的作用
此技能帮助您使用D3.js(数据驱动文档)创建自定义、交互式数据可视化。D3提供低级别构建块,用于数据驱动的DOM操作、视觉编码、布局算法和交互,使能够实现图表库无法提供的定制可视化。
何时使用D3
使用D3时:
- 图表库不支持您的特定设计
- 您需要完全定制控制
- 创建网络图、层次结构或地理地图
- 构建具有链接视图的交互式仪表板
- 平滑动画数据变化
- 处理复杂或非传统数据结构
不要使用D3时:
- 简单条形/线图足够(使用Chart.js、Highcharts—更容易)
- 您需要3D可视化(使用Three.js、WebGL)
- 大规模数据集 >10K点且无聚合(性能限制)
- 您不熟悉JavaScript/SVG/CSS(需要先决条件)
核心概念
数据连接:将数组绑定到DOM元素,创建一一对应关系 比例尺:将数据值转换为视觉值(像素、颜色、大小) 形状:从数据生成SVG路径,用于线条、区域、弧线 布局:为复杂可视化计算位置(网络、树、地图) 过渡:在状态之间动画平滑变化 交互:添加缩放、平移、拖拽、刷子选择行为
技能结构
- 入门:设置、先决条件、第一个可视化
- 选择与数据连接:DOM操作、数据绑定
- 比例尺与轴:数据转换、轴生成
- 形状与布局:路径生成器、基础布局
- 高级布局:力模拟、层次结构、地理地图
- 过渡与交互:动画、缩放/平移/拖拽/刷子
- 工作流:常见图表类型的逐步指南
- 常见模式:可重用代码模板
工作流
根据当前任务选择工作流:
创建基础图表工作流
使用时机: 从零开始构建条形、线或散点图
时间: 1-2小时
复制此检查清单并跟踪进度:
基础图表进度:
- [ ] 步骤1:设置SVG容器和边距
- [ ] 步骤2:加载和解析数据
- [ ] 步骤3:创建比例尺(x、y)
- [ ] 步骤4:生成轴
- [ ] 步骤5:绑定数据并创建视觉元素
- [ ] 步骤6:样式和添加交互性
步骤1:设置SVG容器和边距
创建SVG元素,设置合适尺寸。为轴定义边距:{top: 20, right: 20, bottom: 30, left: 40}。计算内部宽度/高度:width - margin.left - margin.right。参见入门。
步骤2:加载和解析数据
使用d3.csv('data.csv')处理外部文件,或直接定义数据数组。对时间序列使用d3.timeParse('%Y-%m-%d')解析日期。对CSV数据使用转换函数将字符串转换为数字。参见入门。
步骤3:创建比例尺
根据数据类型选择比例尺类型:scaleBand(分类)、scaleTime(时间)、scaleLinear(定量)。使用d3.extent()、d3.max()或手动范围从数据设置域。根据SVG尺寸设置范围。参见比例尺与轴。
步骤4:生成轴
创建轴生成器:d3.axisBottom(xScale)、d3.axisLeft(yScale)。使用变换定位追加g元素。调用轴生成器:.call(axis)。使用.ticks()、.tickFormat()自定义刻度。参见比例尺与轴。
步骤5:绑定数据并创建视觉元素
使用数据连接模式:svg.selectAll(type).data(array).join(type)。使用比例尺和访问器函数设置属性:.attr('x', d => xScale(d.category))。对线图,使用d3.line()生成器。对散点图,创建具有cx、cy、r属性的圆。参见选择与数据连接和形状与布局。
步骤6:样式和添加交互性
应用颜色:.attr('fill', ...)、.attr('stroke', ...)。添加悬停效果:.on('mouseover', ...)与工具提示。添加点击处理程序以进行下钻。对初始动画应用过渡(可选)。参见过渡与交互和常见模式。
用新数据更新可视化
使用时机: 用新数据刷新图表(实时、过滤器、用户交互)
时间: 30分钟 - 1小时
复制此检查清单:
更新进度:
- [ ] 步骤1:将可视化封装在更新函数中
- [ ] 步骤2:如果需要,更新比例尺域
- [ ] 步骤3:使用键函数重新绑定数据
- [ ] 步骤4:向连接添加过渡
- [ ] 步骤5:用新值更新属性
- [ ] 步骤6:在数据变化时触发更新
步骤1:将可视化封装在更新函数中
将基础图表工作流的步骤3-5包装在function update(newData) { ... }中。这使可视化可重用于任何数据集。参见工作流。
步骤2:更新比例尺域
当数据范围变化时重新计算域:yScale.domain([0, d3.max(newData, d => d.value)])。用过渡更新轴:svg.select('.y-axis').transition().duration(500).call(yAxis)。参见选择与数据连接。
步骤3:使用键函数重新绑定数据
使用键函数实现对象恒常性:.data(newData, d => d.id)。确保元素跟踪数据项,而非数组位置。对正确过渡至关重要。参见选择与数据连接。
步骤4:向连接添加过渡
在属性更新前插入.transition().duration(500)。用.ease(d3.easeCubicOut)指定缓动。对自定义进入/退出效果,在.join()中使用进入/退出函数。参见过渡与交互。
步骤5:用新值更新属性
使用更新的比例尺设置位置/大小:.attr('y', d => yScale(d.value))、.attr('height', d => height - yScale(d.value))。过渡从旧值动画到新值。参见常见模式。
步骤6:在数据变化时触发更新
当数据变化时调用update(newData):按钮点击、计时器(setInterval)、WebSocket消息、API响应。对实时数据,使用滑动窗口限制数据点。参见工作流。
创建高级布局工作流
使用时机: 构建网络图、层次结构或地理地图
时间: 2-4小时
复制此检查清单:
高级布局进度:
- [ ] 步骤1:选择适当的布局类型
- [ ] 步骤2:准备和结构化数据
- [ ] 步骤3:创建和配置布局
- [ ] 步骤4:将布局应用于数据
- [ ] 步骤5:将计算属性绑定到元素
- [ ] 步骤6:添加交互(拖拽、缩放)
步骤1:选择适当的布局类型
力模拟:网络图、有机聚类。层次结构:树、簇(节点链接)、矩形树图、圆形树图、分区(空间填充)。地理:带投影的地图。弦图:流图。参见高级布局获取决策指导。
步骤2:准备和结构化数据
力:nodes = [{id, group}]、links = [{source, target}]。层次结构:嵌套对象,带有子数组,用d3.hierarchy(data)转换。地理:GeoJSON特征。参见高级布局。
步骤3:创建和配置布局
力:d3.forceSimulation(nodes).force('link', d3.forceLink(links)).force('charge', d3.forceManyBody())。层次结构:d3.treemap().size([width, height])。地理:d3.geoMercator().fitExtent([[0,0], [width,height]], geojson)。参见高级布局获取每种布局类型。
步骤4:将布局应用于数据
力:模拟自动运行,每个刻度更新节点位置。层次结构:在根上调用布局:treemap(root)。地理:无需应用,投影在路径生成器中使用。参见高级布局。
步骤5:将计算属性绑定到元素
力:在刻度处理器中更新cx、cy:node.attr('cx', d => d.x)。层次结构:使用d.x0、d.x1、d.y0、d.y1用于矩形。地理:使用path(feature)作为d属性。参见工作流获取布局特定示例。
步骤6:添加交互
拖拽用于力网络:node.call(d3.drag().on('drag', dragHandler))。缩放用于地图/大型网络:svg.call(d3.zoom().on('zoom', zoomHandler))。点击用于层次结构下钻。参见过渡与交互。
路径选择菜单
您想做什么?
-
我是D3新手 - 设置环境,理解先决条件,创建第一个可视化
-
构建基础图表 - 条形、线或散点图逐步指南
-
用比例尺转换数据 - 将数据值映射到视觉属性(位置、颜色、大小)
-
将数据绑定到元素 - 连接数组到DOM元素,处理动态更新
-
创建网络/层次结构/地图 - 力导向图、矩形树图、地理可视化
-
添加动画 - 图表状态之间的平滑过渡
-
添加交互性 - 缩放、平移、拖拽、刷子选择、工具提示
-
用新数据更新图表 - 处理实时数据、过滤器、用户交互
-
获取代码模板 - 复制-粘贴-修改常见模式模板
-
理解D3概念 - 深入探索数据连接、比例尺、生成器、布局
快速参考
数据连接模式(核心D3工作流)
// 1. 选择容器
const svg = d3.select('svg');
// 2. 绑定数据
svg.selectAll('circle')
.data(dataArray)
.join('circle') // 自动创建/更新/移除元素
.attr('cx', d => d.x) // 使用访问器函数(d = 数据项,i = 索引)
.attr('cy', d => d.y)
.attr('r', 5);
比例尺创建(数据 → 视觉转换)
// 对定量数据
const xScale = d3.scaleLinear()
.domain([0, 100]) // 数据范围
.range([0, 500]); // 像素范围
// 对分类数据
const xScale = d3.scaleBand()
.domain(['A', 'B', 'C'])
.range([0, 500])
.padding(0.1);
// 对时间数据
const xScale = d3.scaleTime()
.domain([new Date(2020, 0, 1), new Date(2020, 11, 31)])
.range([0, 500]);
形状生成器(SVG路径创建)
// 线图
const line = d3.line()
.x(d => xScale(d.date))
.y(d => yScale(d.value));
svg.append('path')
.datum(data) // 使用.datum()处理单个数据项
.attr('d', line) // 线生成器创建路径数据
.attr('fill', 'none')
.attr('stroke', 'steelblue');
过渡(动画)
// 在属性更新前添加过渡
svg.selectAll('rect')
.data(newData)
.join('rect')
.transition() // 此后的一切都将动画化
.duration(500) // 毫秒
.attr('height', d => yScale(d.value));
常见比例尺类型
| 数据类型 | 任务 | 比例尺 |
|---|---|---|
| 定量(线性) | 位置、大小 | scaleLinear() |
| 定量(指数) | 压缩范围 | scaleLog(), scalePow() |
| 定量 → 圆面积 | 圆大小 | scaleSqrt() |
| 分类 | 条形、分组 | scaleBand(), scalePoint() |
| 分类 → 颜色 | 颜色编码 | scaleOrdinal() |
| 时间 | 时间序列 | scaleTime() |
| 定量 → 颜色 | 热力图 | scaleSequential() |
D3模块导入(ES6)
// 特定函数
import { select, selectAll } from 'd3-selection';
import { scaleLinear, scaleBand } from 'd3-scale';
import { line, area } from 'd3-shape';
// 整个D3命名空间
import * as d3 from 'd3';