教程
D3.js Tutorial – Data Visualization for Beginners
官方的教程与示例
使用方法
// 安装D3
npm i d3@6.7.0
// 全部引入
import * as d3 from 'd3';
API 列表简介
官方的API的排序是按照字母升序排列的,我这儿按照我自己的理解。做了一下分类。
入门必备
用于坐标轴绘制
D3内部封装的时间函数,可以用于Transition动画。
比例尺,D3里面比较重要的概念。
用于元素集合操作,D3里面必须掌握的基本操作和概念。
D3内部封装的一些用于绘制形状的API,比如扇形,弧,点等等。
可以为元素增加过渡动画效果。
数据处理
封装了一系列操作数组的方法。
D3内部封装的处理CSV(逗号分隔值)的API,比如将其转换为JS对象等等操作。
D3内部封装的用于数字格式转化的API。
生成随机数
用于时间格式转换操作。
用于时间处理操作。
用于颜色相关的数据处理。
内置的一系列颜色方案,比如你的扇形图每个块需要不同的颜色,那么你就可以根据Color Schemes提供的方案去选取颜色。
插值操作,一般是返回一个处理两个值(自己指定)之间的范围的函数。
用户交互
直译为刷子,这个主要用来用户做选定区域的操作,帮助用户来关注自己想要关注的指定区域。
用于拖拽操作。
为用户提供缩放和平移操作
图形绘制
用于弦图的绘制。
用于轮廓的绘制(如地理学上的等高线图),比如下图
用于维诺图(泰森多边形)的绘制。
用于地图相关的绘制。
层级图对应的API,D3官方对应的集群图(Cluster)、树状图(Tree)、矩形树状图(TreeMap)、分区图(Partition)、打包图(Pack)由层级图(Hierarchy)延伸出来的。
用于四叉树绘制
用于路径绘制
为二维多边形提供了一些基本的几何运算。
-Forces 绘制力导向图
其他
Dispatch可以用于自定义事件监听。
获取外部文件提供的数据,比如JSON、CSV、SVG等。
这个模块提供了一个高效的队列,能够管理数千个并发动画,同时保证与并发或分段动画保持一致、同步的计时。在内部,它使用requestAnimationFrame进行流体动画(如果可用),当延迟超过24ms时切换到setTimeout。
内置图表
D3.js封装了复杂图表的绘制方法,有如下几种:
力导向图(力学图)
弦图
集群图
根据我对D3官方解释的理解,Cluster是比Tree更复杂的树状图,Cluster的概念建立在Dendrogram之上。
树状图
按照官方的解释,Tree相比与Cluster更加简洁清晰
官方解释:The tree layout produces tidy node-link diagrams of trees using the Reingold–Tilford “tidy” algorithm, improved to run in linear time by Buchheim et al. Tidy trees are typically more compact than dendrograms.
矩形树状图
打包图
分区图
饼状图
堆栈图
准确的说堆图不能算是一个独立的图表类型,堆图相当于是把一些相关的形状挨着放在一起。
直方图
直方图跟柱状图(条形图)有区别,不要混为一谈;柱状图和直方图唯一的区别就是一个用于分类数据,一个用于连续数据。
常用API
选取元素
- select 选择元素(类似jquery的操作)
d3.d3.select('#text').style('color', 'red');
- selectAll 选择所有匹配的元素
d3.selectAll('.hello').style('color', 'blue');
操作选取元素
- attr()
d3.select("p").attr("name", "fred")
- classed()
d3.select("p").classed('test', true) // true表示增加,false表示移除
- style()
d3.select("p").style('color', 'blue')
- property()
d3.select("p").property('value', '2333')
- text()
d3.select("p").text('Hello D3')
- html()
d3.select("p").html('<span>yayayaya</span>')
- append() 在选择集尾部插入元素
d3.select("p").append('span')
- insert() 在选择集前面插入元素
d3.select("div").insert("p", "h1") // 在h1元素前插入p元素
- remove()
d3.select("div").remove("p")
Update、Enter、Exit
- update() 当对应的元素正好满足时 ( 绑定数据数量 = 对应元素 )
实际上并不存在这样一个函数,只是为了要与之后的 enter 和 exit 一起说明才想象有这样一个函数。但对应元素正好满足时,直接操作即可,后面直接跟 text ,style 等操作即可。
<ul id="list">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
d3.select("#list").selectAll("li")
.data([10, 20, 30, 40])
.text(function(d) { return d; });
- enter() 当对应的元素不足时 ( 绑定数据数量 > 对应元素 )
当对应的元素不足时,通常要添加元素,使之与绑定数据的数量相等。后面通常先跟 append 操作。
<ul id="enter">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
d3.select('#enter')
.selectAll('li')
.data([1, 2, 3, 4, 5])
.text((item) => {
return item;
})
.enter()
.append('li')
.text((item) => {
return item;
});
- exit() 当对应的元素过多时 ( 绑定数据数量 < 对应元素 )
当对应的元素过多时,通常要删除元素,使之与绑定数据的数量相等。后面通常要跟 remove 操作。
<ul id="exit">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
d3.select('#exit')
.selectAll('li')
.data([1, 2, 3, 4, 5])
.text((item) => {
return item;
})
.exit()
.remove('li')
.text((item) => {
return item;
});
加入数据
- data() join()
d3.select('.fruits')
.selectAll('p')
.data(['apple', 'peach', 'orange'])
.join('p')
.text((item) => item);
引入外部数据
支持array、json、csv、xml等。
- json()、csv()
// async await
const data = await d3.csv("/path/to/file.csv");
console.log(data);
// or
d3.json("/path/to/file.json").then((data) => { console.log(data); })
绘制一个简单的矩形(svg)
<div class="svg">
<svg id="svgDemo" class="svg-demo"></svg>
</div>
let marge = { top: 60, bottom: 60, left: 60, right: 60 }; // 设置边距
let dataset = [250, 210, 170, 130, 90]; // 数据(表示矩形的宽度)
let svgInstance = d3.select('#svgDemo'); // 获取svg元素
let g = svgInstance
.append('g') // 定义一个用来装整个图表的一个分组,并设置他的位置
.attr('transform', 'translate(' + marge.top + ',' + marge.left + ')');
let rectHeight = 30; // 设置每一个矩形的高度
g.selectAll('rect')
.data(dataset) // dataset里面有5个数据,根据enter()不足补足的原则,这儿会补足5个react元素
.enter()
.append('rect')
.attr('x', 20) // 设置左上点的x
.attr('y', function (d, i) {
// 设置左上点的y
return i * rectHeight;
})
.attr('width', function (d) {
//设置宽
return d;
})
.attr('height', rectHeight - 5) // 设置长
.attr('fill', 'green'); // 颜色填充
比例尺
线性比例尺
- scaleLinear() domain() range() 这三个函数一般是结合起来用,线性比例尺,能将一个连续的区间,映射到另一区间。这个相当于是控制比例的。
// 将0-300这个范围按比例映射到0-15这个区域上
let scaleLinear = d3.scaleLinear()
.domain([0,15])
.range([0,300]);
序数比例尺
- scaleOrdinal() domain() range() 有时候,定义域和值域不一定是连续的。domain域和range域是离散的,也就是数组。
let color = ["red","blue","yellow","black","green"];
let scaleOrdinal = d3.scaleOrdinal()
.domain(index)
.range(color);
坐标轴
- axisBottom() 创建一个新的面向底部的轴生成器
- axisTop() 创建一个新的顶部定向轴生成器
- axisLeft() 创建一个新的左边的轴生成器
- axisRight() 创建一个新的右边的轴生成器
- ticks() 刻度数
- call() 调用某个函数
- scaleBand() 创建一个有序的频带比例尺
带坐标轴的代码
let marge = { top: 60, bottom: 60, left: 60, right: 60 }; //设置边距
let dataset = [1.5, 2.1, 1.7, 1.3, 0.9]; //数据(表示矩形的宽度)
let svgInstance = d3.select('#svgDemo'); // 获取svg元素
// 定义比例尺
let scaleLinear = d3.scaleLinear().domain([0, 3]).range([0, 300]);
let g = svgInstance
.append('g') //定义一个用来装整个图表的一个分组,并设置他的位置
.attr('transform', 'translate(' + marge.top + ',' + marge.left + ')');
let rectHeight = 30; //设置每一个矩形的高度
g.selectAll('rect')
.data(dataset) // dataset里面有5个数据,根据enter()不足补足的原则,这儿会补足5个react元素
.enter()
.append('rect')
.attr('x', 20) //设置左上点的x
.attr('y', function (d, i) {
//设置左上点的y
return i * rectHeight;
})
.attr('width', function (d) {
//设置宽
return scaleLinear(d);
})
.attr('height', rectHeight - 5) //设置长
.attr('fill', 'red'); //颜色填充
// 增加一个X轴
// 比例尺
let xScale = d3.scaleLinear().domain([0, 3]).range([0, 300]);
// 定义坐标轴
let xAxis = d3.axisBottom(xScale).ticks(7);
g.append('g')
.attr(
'transform',
'translate(' + 20 + ',' + dataset.length * rectHeight + ')'
)
.call(xAxis);
动画
- transition() 添加过度效果
- duration() 动画过渡时间(毫秒)
- delay() 动画延时执行时间(毫秒)
饼图Pie
绘制饼状图时,d3.arc与d3.pie()要结合使用。
API
-
d3.arc() 绘制弧形
-
arc.innerRadius 设置饼图对应弧度的内半径,如果内半径不为0,则饼图当中是空心的。
-
arc.outerRadius 设置饼图对应弧度的外半径,控制饼图具体绘制的大小。
-
d3.pie() 绘制饼状图
-
pie.padAngle(angle) 设置饼图每个块直接的偏移角度值(我感觉也可以理解为间距)不给参数值angle时,返回0。
-
pie.sort(compare) 饼图的每个块的排序方式,未指定compare时按默认的数据顺序排列。
pie.sort((a, b) => a.name.localeCompare(b.name));
其他常用API
d3.map(data, mapper)
按照给定mapper函数定义的顺序,返回一个包含iterable中映射值的新数组。等效于 Array.prototype.map() 与 Array.from。
// 示例
d3.map(data, (item) => item.name)
d3.range([start, ]stop[, step])
返回一个包含等差数列的数组。这种方法通常用于迭代均匀间隔的数值序列,例如数组的索引或线性刻度尺的刻度。
- start省略的话,默认为0。
- step省略的话,步进值为1。
d3.range(0, 1, 0.2) // [0, 0.2, 0.4, 0.6000000000000001, 0.8]
d3.schemeSpectral
返回一个二维数组,长度为12,索引0-2没有值;有值的数组里面包含颜色的16进制值。这里面的颜色值来自“光谱”发散颜色,这个还有一个变种函数:d3.interpolateSpectral(t),t的取值是 [0, 1],返回值是一个rgb字符串,像这样:rgb(94, 79, 162)。
d3.schemeCategory10
一个由十种分类颜色组成的数组