最近需要使用到D3.js,所以在B站上找到清华的D3.js教程,同时在这里做相关的自学笔记
原视频链接如下:
【数据可视化编程-使用D3.js(2022)】 https://www.bilibili.com/video/BV1qg411X7bB/?share_source=copy_web&vd_source=773b408053db6c74535b5afe2aa8feb9
D3.js是目前最主流的,社区规模最大的,支持定制图元级别可视化效果的框架
相关文档:https://github.com/xswei/d3js_doc
D3画廊:https://observablehq.com/@d3/gallery
其他如Echarts不支持图元级别定制,但是支持图表混搭
之后课程会介绍动画与交互,可视化图表绘制,其他常用接口
D3: Data-Driven Documents
通过D3提供的接口基于数据操控文档(画布)的各个图元
接口约等于D3.js提供的函数调用
主要参考资料:
https://d3js.org/ 官网,文档,样例
https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute SVG 属性参考
https://github.com/d3/d3 官方样例仓库
https://observablehq.com/@d3/gallery 官方样例仓库
https://github.com/xswei/d3js_doc d3.js资源汇总,包括示例、书籍、API文档等
https://github.com/Shao-Kui/D3.js-Demos
D3.js基于JS,用于在Web前端控制HTML中的元素
HyperText Markup Language
HTML包含大量元素(标签)
:JS脚本或者脚本链接
D3.js或Echart的编程主要写于此标签中
:对于D3最重要的标签
主要操作的对象画布,同时包括所有图元应用的标签,如
等
D3.js是JS的外库,必须先将其导入
如同Python的import
Java的import
C/C++的include
node.js的require
这里通过Script标签导入
./d3.min.js
https://unpkg.com/browse/[email protected]/dist/d3.js
D3的绘制画布
Scalable Vector Graphics 可缩放矢量图形
对于根节点的操作会影响子节点
常用父节点中的
解释型
不需要编译
JS语句类似于C与C++
变量声明不需指定类型 int double function等,直接let, var const
运算操作基本等同C,C++和Java
下面是函数定义:
function abc(a) { return a + 5; }
const p = function(a, b){return a + b;}
let f = datum => datum.value;
let myFunction = (a, b) => a + b
let f = (d, i) => { console.log(d);return d + I;}
一个变量可以是一个函数
类似于C/C++中的函数指针
const myFunction = function(a, b){ return a + b; }
回调(CallBack)
JS脚本中常见将函数作为变量输入用于实现异步编程
setTimeout( funciton() {
console.log('hello world')
}, 1000);
在D3中存在大量类似调用
模版字符串:
let a = 10;
let myString = `abc-${a}`;
// myString最终为'abc-10'
数组 a = [1, 2, 3]
对象 a = {name: ‘Zane’, age: 24, lab: ‘cs’}
D3数据可视化常见对象数组
a = [{name:'Zane', age: 20, dept: 'cs'},
{name:'LJD', age: 21, dept: 'cs'},
{name: 'LH', age: 22, dept: 'ee'}]
数组排序 a.sort()
- 可以通过加入回调函数来替代缺省的排序方案,如为日期排序
- a.sort(function(a, b){ return new Date(b.date) - new Date(a.date); }
数组查询 a.find()
a.find( d => d.name === ‘Zane’)
将字符串转为数值 +(‘3.14’)
D3.js经常读取CSV,JSON等文件,涉及大量数组,对象操作
使用D3获取,修改,增加以及删除节点(图元)
数据读取 - CSV
数值计算
比例尺:1. 线性比例尺 Linear Scale
2. 条带比例尺 Band Scale
坐标轴绘制: Margin
Data-Join基础
基于D3与Data-Doin绘制柱状图
d3.select(‘#rect1’) 永远返回第一个
d3.selectAll(‘.class1’) 返回所有
ID前加#,Class前加.,标签前不加
层级查询
d3.select(‘#maingroup rect’)
d3.select(‘.tick text’)
d3.selectAll(‘#secondgroup rect’)
常见属性:
SVG的属性非常多,并且属性的取值范围和类型各不相同
https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute
屏幕空间的坐标系与常见坐标系不同
左上方为原点
y,x分别垂直向下、水平向右
element.attr(…)
设置元素属性: element.attr(‘attr_name’, ‘attr_value’)
获取元素属性: element.attr(‘attr_name’)
链式调用:
DOM
父节点的属性会影响子节点
子节点的属性相对于父节点
以下代码可以直接移动组内的所有元素
d3.select(‘#maingroup’).attr(‘transform’, ‘translate(200, 100)’)
element.append(…)
D3的链式添加(调用)
element.remove()
在debug过程中可以使用’opacity’属性hack出移除效果
第一行是属性列表,后面每一行对应一条数据
CSV本质上是纯文本,区别于EXCEL格式
d3.csv(…):
d3.csv是一个JavaScript异步函数:
d3.csv(‘path/to/data.csv’).then( data => { // ‘数据读取后的代码逻辑’ } )
JavaScript异步机制
读取后的数据格式(接口)与原本的CSV结构不同
数据可视化常常涉及对数据的处理与计算
下面三个接口分别用于计算数组的最大值,最小值,[最小值, 最大值]
d3.max(array)
返回数组中的最大值
d3.min(array)
返回数组中的最小值
d3.extent(array)
同时以数组的形式返回最小值与最大值
数组中的内容可以是任意对象
比例尺用于将实际数据空间映射到屏幕(画布)空间,也即两个空间转化
用于映射数据和创建坐标轴,区别主要在于数据的尺度不同
d3.scaleLinear():
scale.domain([min_d, max_d]).range([min, max]):
比例尺本质上是函数
常常结合读取的数据与d3.max等接口连用
d3.scaleBand():
scale.domain(array).range([min, max]):
d3.scaleBand()常常结合JS的array.map接口一同使用:
scale.padding(0.1):
设置条带间距占各自区域的比重
scale.bandwidth():
返回条带长度
一个坐标轴为一个group,也即
通常需要两个坐标轴
坐标轴中包含:
用于横跨坐标轴的覆盖范围.tick
,每个刻度也是一个group
和一个
用于展示轴线,如左到右或上到下
用于展示刻度值,如实数,姓名,日期坐标轴的定义需要比例尺
定义坐标轴(获得结果仍是函数)
绘制坐标轴
中增加了与坐标轴相关的元素任何坐标轴在初始化之后会默认放置在坐标原点,需要进一步平移
关于 selection.call(…)
,D3会帮助我们定义好另一个函数,我们通过.call(…)让
得以在另一个函数中修改
配置坐标轴
可以对坐标轴的风格进行修改
坐标轴的标签加入不在D3.Axis接口的负责范围内:
标签.append(‘text’)来实现Margin
SVG对于D3.js是一个画布
SVG范围外的任何内容属于画布之外,浏览器不予显示
定义Margin:
计算实际操作的inner长/宽
在SVG下额外定义一个组作为新的根节点
- const g = svg.append('g').attr('id', 'maingroup')
.attr('transform', `translate(${margin.left}, ${margin.top})`)
HTML确实在样式表中提供margin属性,然而设置其他图元的位置,仍
需要计算innerWidth(Height)
调用示例
const yAxis = d3.axisLeft(yScale) // .tickSize(-innerWidth);
const xAxis = d3.axisBottom(xScale) // .tickSize(-innerHeight);
const yAxisGroup = g.append('g').call(yAxis)
.append('text')
.text('Name')
.attr('font-size', '3em')
.attr('transform', 'rotate(-90)') // y-axis label needs an additional transform;
.attr('x', -innerHeight / 2)
.attr('y', -120)
.attr('fill', 'black')
const xAxisGroup = g.append('g').call(xAxis)
.attr('transform', `translate(${0}, ${innerHeight})`)
.append('text')
.text('Value')
.attr('font-size', '3em')
.attr('x', innerWidth / 2)
.attr('y', 50)
.attr('fill', 'black');
d3.selectAll('.tick text).attr('font-size', '2em');
g.append('text').text('Members of CSCG').attr('font-size', '3em')
.attr('x', innerWidth / 2 - 200).attr('y', -10)
本质上是将数据与图元进行绑定
以数据为中心的可视化操作(Data-Driven)
数据发生变化时可以自动对图元增删改查
d3.selectAll(‘.class’).data(dataArray)
dataArray在保证是数组的前提下可以是任何形式如数值数组,对象数组
。。。