数据可视化-d3.js
1. 环境配置
1.1 配置React + TS
这里使用脚手架配置:
npx create-react-app react-typescript-demo --typescript
2. 使用D3查询SVG
2.1 d3.select(xxx)
d3.select('#rect1'),查询ID为'rect1'的元素,#表示后面的字
符串是一个ID。
d3.select(xxx)也可用于查询类别,如d3.select(“.class1“),但只会返回找到的第一个元素。
2.2 d3.selectAll(xxx)
d3.selectAll('.class1'),查询所有class是'class1'的元素。
d3.selectAll('rect'),查询所有标签是'rect'的元素(rect为SVG中的矩形标签)。
2.3 基于层级的查询
- d3.select('#magingroup rect')
- d3.selectAll('.tick text')
- d3.selectAll("#secondgroup rect")
如: ’#secondgroup rect‘
- 首先会找到id为secondgroup的标签。
- 进一步找到secondgroup的子标签中是rect的。
- 仍然是对rect做查询,只是结果通过父标签做了筛选!
这种形式的查询,经常会在配置坐标轴的代码中使用,请至少熟悉其形式。
3. 使用D3设置SVG中的属性
3.1 常见属性
- id,class(特殊属性,可以使用.attr设置)
- x,y,cx,cy(元的坐标)
- fill,stroke
- height,width,r(圆的半径)
- transform -> translate,rotate,scale
3.2 element.attr(xxx)
-
设置元素的属性:element.attr(’attr_name‘,’attr_value‘)
- react1.attr('y', '100');
- d3.select('#rect1').attr('y', '100');
-
获取元素的属性:element.attr(’attr_name‘);
-
注意:数值类型的数据仍在HTML中仍有字符串存储
- 要使用+(strValue)及时转换,如 let value = +(’233.666‘)
- 活用模板字符串,如
- let width = 666;
- attr(’transform‘,’translate(0, $(width + 100))‘)
- 模板字符串使用’xxx‘ 表示(本质上还是一个字符串)
- ${xxx}可以在字符串中嵌入程序表达式
3.3 设置图元属性
-
selection.attr('attrbuteName', 'value')
- 支持直接通过值来设置属性
- 支持通过函数来设置属性
-
selection.attr('attrbuteName', (d, i) = > {xxx})
- d为绑定给图元的数据(即将到来)
- i为图元的索引,是一个证书,如d3.selectAll('rect')中第几个矩形
- 函数也可以仅使用d => {xxx},但此时函数体无法使用索引
- 即使不使用的绑定的数据(如没有绑定数据),如需使用索引,仍需要完整的写出(d, i) => {xxx}
4.1 属性继承
-
DOM
- 父节点的属性会影响子节点
- 子节点的属性会相对于父节点
-
活用
节点可以省掉很多冗余代码! - d3.select(’#maingroup‘)
- .attr(’transform‘,’translate(200, 100)‘)
4. 使用D3添加&删除SVG元素
4.1 element.append()
-
element.append()
- const myRect = svg.append('rect');
- const myRect = d3.select(’#mainsvg‘).append(’rect‘)
- const myRect = d3.select(’#mainsvg‘).append(’rect‘).attr(’x‘:’100‘)
-
链式添加
- const myReact = d3.select('#mainsvg').append('g').attr('id', 'maingroup')
- .append('rect').attr('fill', 'yellow')
链式调用:习惯这种编程习惯有助于使用D3和浏览D3的代码
-
element.remove()
- 请小心使用,会移除整个标签
-
在debug的过程中可以考虑使用’opacity‘属性hack出移除的效果。
- element.attr('opacity', '0')
5. 比例尺
- 比例尺用于把实际的数据空间映射到屏幕的空间。
- 比例尺非常重要,会经常同时传给坐标轴与数据!
5.1 Scale-Linear
const xScale = d3.scaleLinear().domain([min_d, max_d]).range([min, max]);
const xScale = d3.scaleLinear().domain([0, d3.max(data, daturm => datum.value)]).range([0, innerWidth]);
d3.max(数局,回调:如果提取数据的值)
d3.max: 求出数据某一属性的最大值,比如年龄的最大值
5.2 Scale-Band
const yScale = d3.scaleBand().domain(list).range([min, max]).padding(p);
const yScale = d3.scaleBand().domain(data.map(datum => datum.name)).range([0, innerHeight]).paading(0.1)
5.3 Scale是函数
-
通过d3.scaleLinear或d3.scaleBand得到的返回值本质上是函数。
- 给出数据的值(domain)
- 返回映射后的值(range)
- .domain(xxx)和.range(xxx)可以理解为配置这个函数(功能)的过程。
-
比例尺的定义仍使用链式调用
const xScale = d3.scaleLinear().domain([0, d3.max(data, datum=>datum.value)]).range([0, innerWidth]);
const mapped = xScale(100);
6. 坐标轴
6.1 引入坐标轴
一个坐标轴为一个group(),通常我们需要两个坐标轴
- 坐标轴中包含:
- 一个
用于横跨坐标轴的覆盖范围 - 若干个刻度
- 每个刻度也是一个group
- 每个刻度下属还会包含一个
和一个 用于展示坐标轴的轴线,如左到右或上到下 用于展示坐标轴的刻度值,如实数、性名、日期
- (可选)一个标签用于描述坐标轴
- 一个
定义坐标轴(获得结果是函数)
- const yAxis = d3.axisLeft(yScale);
- const xAxis = d3.axisBottom(xScale);
- axisLeft:左侧坐标轴
- axisBottom:底侧坐标轴
实际配置坐标轴
- const yAxisGroup = g.append('g').call(yAxis);
- const xAxisGroup = g.append('g').call(xAxis);
实际配置后会发现
任何坐标轴在初始化之后会默认放置在坐标原点,需要进一步的平移。
关于selection.call(xxx)
函数的输入为另一个函数。
另一个函数以selection的图元作为输入。
另一个函数中会根据函数体的内容修改selection对应的图元。
定义一个空白的
6. 绘制BarChart
6.1 定义Margin
-
SVG对于D3.js是一个’画布‘。
-
SVG范围外的任何内容属于画布之外,浏览器将不予显示。
-
定义Margin
- const margin = {top: 60, right: 30, bottom: 60, left: 200}
-
计算实际操作的inner 长/宽
- const innerWidth = width - margin.left - margin.right;
- const innerHeight = height - margin.top - margin.bottom;
-
在SVG下额外定义一个组作为新的根节点
- const g = svg.append('g').attr('id', 'maingroup').attr('transform', 'translate(${margin.left}, ${margin.top})');
d3绘制的时候会贴近坐标轴绘制