由于想对IMR磁道读写过程进行可视化,以丰富实验内容,最近一段时间在调研并学习d3.js来实现快速的可视化。这篇博客就记录一下学习过程中的一些重点内容,方便编码过程中的查阅以及以后有需要时可以很快捡起来。
D3:Data-Driven Documents
参考资料
- https://d3js.org/ (官方网站,包括文档、样例)
- https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute (SVG属性表)
- https://github.com/d3/d3/wiki/Gallery (官方样例的仓库)
- https://observablehq.com/@d3/gallery (官方样例的仓库)
- https://github.com/xswei/d3js_doc(d3.js资源汇总,包括示例、书籍、API文档等)
运行环境
搭建简易Web开发环境,例如 python Flask + d3.js
重点内容
d3.js介绍
对于d3.js来说,重要的HTML标签主要有两个:
- <script> : D3.js的编程主要写在这个标签中,为JavaScript脚本
- <svg> : 对D3来说最重要的标签,是主要操作的对象(画布)
导入D3.js
1 | <script src="https://d3js.org/d3.v5.min.js"></script> |
也更加建议先下载到本地,再以本地路径来导入。
SVG—可缩放矢量模型
svg是D3.js主要操作对象,是一个容器,用于包含画在上面的各个图元。
svg作为矢量图,不会随着图片的缩放而失真。
可以通过d3.select来获取svg对象:
1 | const svg = d3.select('svg'); |
一些必要的JavaScript语法
熟悉js中函数定义的多种形式:
- function abc(a){return a+a;}
- let f = datum => datum.value;
- const p = function(a,b){return a+b;}
- let myFunction = (a,b) =>a+b;
- let f = (d,i) => {console.log(d); return d+i;}
我比较熟悉第三种,即let f = funciton(a,b){…}这种类型。但是其他的类型看到也要明白。
回调函数:
用于实现异步编程,通常会把函数作为变量输入,如
1 | setTimeout(function(){ |
D3中常用的接口:
模板字符串: let myString = ‘abc-${a}’; (若a为数字10,那么结果为abc-10)
数组:a = [1,2,3]
对象:a = {name: “zzm”, age: 22, lab: ‘cpss’}
数组排序: a.sort(),或者加入回调函数来替代缺省的排序方案:
1
2
3a.sort(function(x,y){
return new Date(x.date)-new Date(y.date);
})数组查询:a.find(d=>d.name === ‘Wen-Yang’)
将数字字符串转化成数值:+(‘3.14’)
D3.js还经常读取CSV,JSON等文件,涉及大量对象数组的操作。
D3.js语法基础
使用D3查询SVG
元素/图元有ID,Class以及标签。ID是元素的唯一标识符;Class表示人为赋予元素的“类别”,不同元素的class可以相同;标签就是html的标签,使用标签往往难以直接索引到目标元素。
查询API:
1 | // 只找一个元素 |
若选取元素的ID进行查询则需要在ID前加‘#’,同理,在Class前加’.’,标签名前不加符号。
:balloon:注意:查询可以层级来进行,例如:
1 | d3.selectAll('#secondgroup rect') |
这样的语句,会先找到id为secondgroup的标签,然后进一步地,找到该标签下所有rect标签的元素。
svg的属性
常见的属性:
- id,class(特殊属性,可以用.attr设置)
- x, y, cx, cy
- fill, stroke
- height, width, r
- transform -> translate, rotate, scale
svg的属性非常多,不认识的可以查阅(https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute)
屏幕空间的坐标系为:
左上方是原点,x水平向右,y垂直向下
element.attr(…)
可以使用该方法设置和获取元素的属性,例如:
1 | // 设置元素属性:element.attr('attr_name','attr_value') |
.attr()方法返回选择的图元本身,所以经常会使用链式调用的形式来对图元的属性进行操作:
1 | selection.attr(…).attr(…).attr(…) |
使用D3添加/删除SVG元素
添加:element.append(…)
1
const myRect = d3.select('#mainsvg').append('rect').attr('x','100');
删除:element.remove()
方法会移除整个标签,不过应该小心使用。
Tip: 在debug的使用可以用’opacity’属性hack出移除的效果,element.attr(‘opacity’,’0’);
数据读取—CSV数据
可以用d3.csv(…)来读取目标路径下的CSV文件,例如:
1 | d3.csv('path/….csv').then(data=>{//数据读取后的逻辑}) |
d3.csv是一个JavaScript异步函数,注意,不能直接获取它的返回值!它返回的是一个Promise对象,要通过.then(data=>{…})的方式来获得读取后的数据。data即为读取后的数据。
D3.js数值计算
- d3.max(array):返回数组中的最大值
- d3.min(array):返回数组中的最小值
- d3.extent(array):同时返回数组中的最小值与最大值,以数组[最小值,最大值]的形式
数组中的内容可以是任意对象,而对象包含多个属性,具体取哪个属性的数值进行比较可以通过回调函数来提示d3的API,例如:
1 | let a = [{name:"Alice",age:21},{name:'Bob',age:25}] |
比例尺
比例尺用于把实际数据空间映射到屏幕(画布)空间,即两个空间的转化。
常用于映射数据以及创建坐标轴。
线性比例尺-Linear
可以用下面的接口来定义一个线性比例尺,其返回值是一个函数,然后可以对比例尺设置定义域以及值域。
1 | let scale = d3.scaleLinear(); // 定义线性比例尺 |
条带比例尺-Band
条带比例尺的定义域是离散的,而值域是连续的。可以这样定义一个条带比例尺:
1 | const scale = d3.scaleBand() |
坐标轴
一个坐标轴为一个group(<g>),通常需要两个坐标轴。
坐标轴中包含:
一个<path>用于横跨坐标轴的覆盖范围
若干个刻度(.tick){对应于比例尺的定义域},每个刻度也是一个group
每个可读下还包含一个<line>和一个<text>
<line> : 展示坐标轴的轴线,如左到右或者上到下
<text> : 展示坐标轴的刻度值
(可选)一个标签用以描述坐标轴
坐标轴的定义通常需要结合比例尺,例如:
1 | // 定义坐标轴 |
注意:任何坐标轴在初始化之后会默认放置在坐标原点,需要进一步的平移。
可以对坐标轴的风格进行修改,例如:
1 | d3.selectAll('.tck text').attr('font-size','2em'); |
.tick是D3对于坐标轴定义的统一Class。
记住:左纵轴坐标需要.attr(‘transform’,’rotate(-90)’)来旋转
由于坐标轴初始化在父节点的左上角,而SVG范围之外的内容浏览器并不会显示,所以我们往往需要定义Margin,比如:
1 | // 定义margin |
得到的maingroup视图如下,然后坐标轴以maingroup作为父节点,就能看见margin的效果了。
![DV7IZTN~7XPL72_VU`021_O.png](https://i.loli.net/2021/10/19/3uUPfz9lHEKsrnb.png)
举一个具体的实例来说明:
Data-Join
这个点特别重要,能够实现数据的动态变化、动态更新。
Data-join实现了以数据为中心(Data-Driven)的可视化操作,将数据与图元进行绑定,根据数据来自动调整图元的属性。这样数据发生变化时,就不再需要手动添加、修改或者删除图元,而是可以自动完成。
图元绑定数据
1 | d3.selectAll('.class').data(dataArray); |
dataArray
需要保证是一个数组,可以是普通数组,对象数组等任何形式。
.data(…)只考虑数据和图元数目相同的情况。
默认的绑定按照双方的索引顺序。
而数据的更新,只需要重新绑定到另一个dataArray就可以了。
调用形式
1 | d3.selectAll('.class').data(myData).join('图元').attr(d=>...).attr((d,i)=>...) |
.join(…)会根据数据的条目自动补全or删除图元。