数据可视化-d3.js 1-6
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.2 配置坐标轴
可以对坐标轴的风格进行修改:d3.selectAll('.tick text').attr('font-size', '2em');
.tickSize来设置网格线
坐标轴的标签加入不在D3接口的负责范围内:
- 通过对应组.append('text')来人为实现
- (左)纵轴需要.att('transform', 'rotate(-90)') 来旋转
- 纵轴坐标旋转后, x / y会颠倒甚至取值范围相反
- 回忆DOM:父节点的属性会影响子节点,而坐标轴默认的'fill'属性是'none',因此请一定手动设置文字颜色.attr('fill', 'black')
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绘制的时候会贴近坐标轴绘制
7. Data-Join
本质上是将数据与图元进行绑定。
使用Data-Join可以省去大量 根据数据设置图元属性 的代码量。
对于 动态变化的数据 提供统一的接口。
以数据为中心的可视化操作
根据数据的每个属性自动调整绑定图元的属性。
不再需要手动添加、'修改'、删除图元
会根据Data-Join的绑定自动推断。
如果图元的数目不等于数据的条目?
根据数据条目的数量选定相应数据的图元。
7.1 data()
element.data(data1).attr('width', d => xScale(d.value));
7.2 key
data(data, keyFunction)
- keyFunction的返回值通常是一个字符串(string)
- keyFunction的定义根据数据,比如keyFunction = d => d.name
e.g.,selection.data(data, d => d.name)
e.g.,d3.selectAll('rect').data(data2, d => d.name).attr('width', d=>xScale(d.value))
在绑定数据给图元时:
- keyFunction为每条输入绑定的数据执行一次。
- keyFunction为每个包含数据的图元执行一次。
如果图元之前没有绑定过任何数据,则keyFunction会报错!
- 第一次绑定时根据索引即可
- 实际的可视化任务,图元都是根据数据的'条'数动态添加(enter)、删除(exit),只需要在添加时指定好DOM的ID即可。
8. Enter Update Exit
D3.js绑定数据的三个'状态'
8.1 Enter
数据的条目多于图元甚至没有图元,常用于第一次绑定数据
D3.js会自动'搞清楚'那些数据是新增的,根据新增的数据生成相应的图元。生成图元的占位,占位的内容需要编程者自行添加(append)
const p = maingroup.selectAll('.class').data(data).enter().append('').attr(xxx)
enter本质上生成指向父节点的指针,而apprend操作相当于在父节点后添加指针数量的图元并将其与多出的数据绑定。
8.2 update
图元和数据条目相同,之前的介绍均为单纯的update
const p = maingroup.selectAll('.datacurve').data(data).attr(xxx).attr(xxx)
tip1:
Update作为实际可视化任务最常用的状态,经常被单独封装成一个函数
tip2:updateSelection.merge(enterSelection).attr(xxx).attr(xxx)
将两个selection合并到一起操作。
enterSelection在与updateSelection merge之前要至少已经调用了append(xxx)语句添加好图元。
8.3 Exit
数据的条目少于图元甚至没有数据,常用于结束可视化
D3.js会自动'搞清楚'那些图元是不绑定数据的
const p = maingroup.selectAll('.class').data(data).exit().remove()
8.4 Data-Join的简介形式
.data(xxx).join(xxx)
默认enter和update的执行形式相同。
默认exit是删除(remove)节点。
默认data-join形式简介但不灵活。
必须需要设置enter数据的初始图元属性,update会每次重新设置初始值,从而导致动画出现’奇怪‘的效果
仍支持‘定制’
.join(
enter => enter.append("text").attr("fill", "green").text(d => d),
update => (),
exit => ()
)
9. 让数据动起来
Update经常与D3.js的动画一起使用。
transition().duration()。
d3.selectAll('rect').data(data2, d => d.name)
.transiton().duration(3000).attr('width', d=> xScale(d.value))。
.duraton(xxx)中为毫秒,即3000表示3秒钟。
.transition(xxx)经过调用后,后续的链式调用会变成数值上的渐变,渐变的时间由.duration(xxx)设定。
插值的方式由.ease(xxx)设定
如,设置数据的更新和无效数据透明,在5秒钟内平滑完成。
let updateSelection = d3.selectAll(".dataRect").data(data2, (d: any) => {
return d.name;
});
updateSelection.transition().duration(5000).attr("width", (d) => xScale(d.value) || 0);
updateSelection.exit().transition().duration(5000).attr("opacity", 0.3);
10. 数据的读取
d3.csv('path/to/data/csv').then(data => {xxx})
.csv函数的返回值是一个JS的'Promise'对象,'promise'对象用于执行异步操作
.then(xxx)的参数为一个函数,参数为.csv(xxx)的返回值,实际上在JavaScript异步中是一个resolve。
d3.csv(xxx)会正常向服务器请求数据,在请求并处理好之后,将结果扔给.then(xxx)中的回调函数。 resolve(data)扔给第一个回调函数,reject(error)扔给第二个回调函数。
11. Path
path元素是SVG基本形状中最强大的一个,它不仅能创建其他基本形状,还能创建更多其他形状。你可以使用path元素绘制矩形(直角矩形或者圆角矩形)、圆形、椭圆、折线形、多边形,以及一些其他的形状,例如贝塞尔曲线、2次曲线等曲线。
path元素的形状是通过属性d来定义的,属性d的值是一个”命令+参数“的序列(见下面)
path作为SVG提供的标签之一,是实现众多可视化方案的基础
path可以做什么?
- 折线图
- 地图
- 主题河流
11.1 Path的属性
- d。
- fill:填充颜色。
- stroke:描边颜色。
- stroke-width:描边宽度。
- transform=“translate(x,y)”:加了描边后需要平移(x=stroke-width/2, y=stroke-width/2).
11.2 d属性
-
M = moveto(M X, Y):将画笔移动到指定的坐标位置。
-
L = lineto(L X, Y):画直线到指定的坐标位置。
-
H = horizontal lineto(H X):画水平线到指定的X坐标位置。
-
V = vertical lineto(V Y):画垂直线到指定的Y坐标位置。
-
C = curveto(C X1, Y1, X2, Y2, ENDX, ENDY):三次贝塞曲线。
-
S = smooth curveto(S X2, Y2, ENDX, ENDY):平滑曲线。
-
Q = quadratic Belzier curve(Q X, Y, ENDX, ENDY):二次贝塞曲线。
-
T = smooth quadratic Belzier curveto(T, ENDX, ENDY): 映射
-
A = elliptical Arc(A RX, RY, XROTATION, FLAG1, FLAG2, X, Y):弧线
-
Z = closepath():关闭路径
以上所有命令均允许小写字母。大写表示绝对定位,小写表示相对定位。
11.3 Path生成器
- d3.line(xxx).x(xxx).y(xxxx):用于折线图。
- d3.geoPath().projection():用于地图。
- d3.area():用于主题河流。
- d3.arc(xxx).innerRadius(xxx).outerRadius(xxx):用于饼图。
- d3.lineRedial(xxx).angle(xxx).radius(xxx):极坐标系版本的d3.line(xxx)
12. Interaction
12.1 Map
地图数据的可视化与D3.js的交互
如何使用D3绘制一张地图。
- Json数据
- TopoJson & GeoJson
D3中如何实现交互
- 什么是事件
- 事件的监听与处理
D3-Tip
层叠式样式表
12.1.1 Json
- JavaScript Object Notation(JSON)。
- 从数据格式上(本质上)是JavaScript的对象。保存为文件后是文本。
- 文本与JavaScript的对象可以‘对等转换’。
- 与CSV不同,读入后几乎不存在‘转换’。
12.1.2 TopoJson & GeoJson
TopoJson
- 本质上是JSON格式
- 对处理了GeoJson数据冗余的特点,节约存储空间
- 由D3的作者Mike Bostock制定
GeoJson
- 本质上是JSON格式
- 官方:GeoJson is a format for encoding a variety of geographic data structures。
- D3.js的geoPath使用GeoJson格式的地图数据
12.1.3 Json数据的读取
Json数据的读取与CSV数据的读取非常相似
d3.json('/static/data/countries-110m.json/').then(
function(data){
xxx
}
);
读取后会直接转换成JavaScript的对象。
CSV数据读取后会转换成一个对象的数组。
注意!转换后的GeoJson不可直接使用,需要根据比例尺进一步转换!
D3的geoPath使用GeoJson的格式,因此需要转换:
// convert topo-json to geo-json
worldMeta = topojson.feature(data, data.objects.countries);
12.1.4 地图数据的可视化 - geoPath与投影
如何将地形的数据映射到画布上?
const projection = d3.geoNaturalEarth1();
const pathGenerator = d3.geoPath().projection(projection);
类似“比例尺”,地图要画在多大的画布上?
projection.fitSize([innerWidth, innerHeight], worldMeta);
12.1.5 D3与Web中的事件机制
事件的设置对应于D3中的.on('eventName', callBack)
d3.select('#someprimitive').on('click', myCallBack)
图元.on(事件类型,触发动作)
g.selectAll("path")
.data(worldMeta.features)
.enter()
.append("path")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("d", (data: any) => geo(data))
.on("mouseover", function (d) {
d3.select(this)
.attr("opacity", 0.5)
.attr("stroke", "white")
.attr("stroke-width", 6);
})
.on("mouseout", function (d) {
d3.select(this)
.attr("opacity", 1)
.attr("stroke", "black")
.attr("stroke-width", 1);
});
12.1.6 标签 d3.tip
d3之外的库,d3.tip
12.2 Stacks
一些形状类型可以被堆叠,将一个形状与另一个形状相邻放置。例如,每月销售的柱状图可以按产品类别分解为多系列的柱状图,将柱状图垂直叠加。这相当于将柱状图根据序数维度(如产品类别)细分,并应用颜色编码。
堆叠图标可以同时展示全部值和每种类别。然而,跨类别进行比较通常是比较困难,因为只有堆叠的底层是对齐的。因此,仔细选择堆叠顺序,并考虑流图。
与饼生成器一样,堆叠生成器不会直接生成形状。相反,他计算位置,然后你可以传递给区域生成器或直接使用,如位置条。
12.2.1 d3.stack(data, [, arguments...])
根据给出的数据数组生成一个堆叠,返回代表每个序列的数组。任何附加参数都是任意的,它们只是与此对象一起传播到访问器。
序列由键访问器决定;返回数组中的每个级数i对应于第i个键。每个序列都是一个点数组,其中每个点j对应于输入数据中的第j个元素。最后将每个点表示为一个数组[y0, y1],其中y0是较低的值(基线),y1是较高的值(背线);y和y的差对应于这个点的计算值。每个系列的键可作为系列使用。每个序列的键都可以使用series.key,每个索引作为series.index。每个点的输入数据元素都可以使用point.data。
This might be represented in JavaScript as an array of objects:
var data = [
{month: new Date(2015, 0, 1), apples: 3840, bananas: 1920, cherries: 960, dates: 400},
{month: new Date(2015, 1, 1), apples: 1600, bananas: 1440, cherries: 960, dates: 400},
{month: new Date(2015, 2, 1), apples: 640, bananas: 960, cherries: 640, dates: 400},
{month: new Date(2015, 3, 1), apples: 320, bananas: 480, cherries: 640, dates: 400}];
To produce a stack for this daa:
var stack = d3.stack()
.keys(["apples", "banans", "cherries", "dates"])
.order(d3.stackOrderNone)
.offser(d3.stackOffsetNone)
The resluting array has one element per series. Each series has one point per month, and each point has a lower and upper value defining the baseline and topline:
重新分配数组每个序列有一个元素。每个序列每个月都有一个点,每个点都有一个定义基线和背线的上下限。
[
[[ 0, 3840], [ 0, 1600], [ 0, 640], [ 0, 320]], // apples
[[3840, 5760], [1600, 3040], [ 640, 1600], [ 320, 800]], // bananas
[[5760, 6720], [3040, 4000], [1600, 2240], [ 800, 1440]], // cherries
[[6720, 7120], [4000, 4400], [2240, 2640], [1440, 1840]], // dates]
Each series in then typically passed to an aree generator to render an are chart, or used to construct rectangles for a bar chart.
12.2.2 stack.keys([keys])
如果指定了键,则将键访问器设置为指定的函数或数组并返回此堆叠生成器。如果未指定键,则返回当前键访问器,默认为空数组。为每个键生成一个序列(层)。键通常是字符串,但也可以是任意值。序列的键与每个数据点一起被传递给值访问器,以计算该点的值。
12.2.3 stack.value([value])
如果指定了值,则将值访问器设置为指定的函数或数字并返回此堆叠生成器。如果未指定值,返回当前值访问器,它默认为
function value(d, key){
return d[key];
}
因此,默认情况下,堆栈生成器假定输入数据是一个对象数组,每个对象用数值暴露命名属性。
12.2.4 stack.order([order])
如果指定了顺序,则将顺序访问器设置为指定的函数或数组并返回当前堆叠生成器。如果没有指定顺序,返回当前的顺序访问器,默认情况下是stackOrderNone;这使用键访问器给出的顺序。
如果顺序是函数,它将被传递生成的序列数组,并且必须返回表示堆叠顺序的数值索引数组。例如,默认顺序定义为:
function orderNone(series) {
var n = series.length, o = new Array(n);
while (--n >= 0) o[n] = n;
return o;
}
堆叠顺序在偏移量之前计算;因此,在计算顺序时,所有点的较低值为零。每个序列的索引属性也只有在计算了顺序之后才设置。
12.2.5 stack.offset([offset])
如果指定偏移,则将偏移访问器设置为指定函数并且返回当前堆叠生成器。如果没有指定偏移,返回当前偏移访问器,默认情况下是stackOffsetNone;它使用零基准线。
偏移函数被传递序列数组和顺序索引数组;然后,它负责更新序列数组中的上、下值。例如,默认偏移量定义为:
function offsetNone(series, order){
if(!((n = seris.length) > 1)) return
for(var i = 1; s0, s1 = series[order[0]], n. m = s1.length; i < n; ++i){
s0 = s1, s1 = series[order[i]]
for(var j = 0; j < m; ++j){
s1[j][i] += s1[j][0] = s0[j][1]
}
}
}
13 Tree & Graph
A ''tree is a 'graph'
-
层级结构的可视化,层级结构:树、Tree、Hierarchy
- d3.hierarchy
- '直接的'可视化方案
- d3.tree
- 更直观的可视化方案
- d3.partition & d3.arc for 'd' of
- d3.partition & d3.arc for 'd' of
-
网络结构的可视化
- 网络结构:图、Graph、Network
- D3.js: Force Simulation
13.1 层级数据可视化
13.1.1 层级数据?
- 数据格式仍为Json
- 节点可以包含'属性'
13.1.2 D3.js的层级数据预处理
- d3.hierarchy
- 保持数据的原始结构,并将输入层级数据转换成D3中的hierarcy对象(result instanceof d3.hierarchy),同时引入
- height( * 不是逐层递减)
- depth
- children(原始结构)
- parent
- data 原始数据的映射
- d3.hierarchy可作为一个‘中间结果’,继续输入到更多D3.js提供的数据预处理接口中。
如何将处理好的数据‘进一步’预处理?
d3.tree().size([innerHeight, innerWidth])
返回一个函数,函数接受的参数为d3.hierarchy,函数会根据设置的size将数性结构的每个节点映射到空间中‘合适’的位置。
13.1.3 得到处理后的数据做Path的'd'属性
d3.linkHorizontal(xxx)
- d3.linkHorizontal().x(d => d.y).y(d => d.x)
- .x(xxx).y(xxx)分别表示如何在source与target中取横纵坐标值
g.selectAll('path')
.data(root.links())
.join("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.attr("d", d3.linkHorizontal().x(d=>d.y).y(d=>d.x));
roo.links()
返回树形结构中存在的所有‘链接’,链接(们)以如下形式给出:
source R1 {data: {xxx}, height: 4, depth: 0, paren: null, children: Array(7), xxx}
targe R1 {data: {xxx}, height: 2, depth: 1, paren: R1, children: Array(7), xxx}
14 Force
let nodes = [{}, {}, {}, {}, {}]
let simulation = d3.forceSimulation(nodes)
定义后会发生:
补全nodes中每个节点的数据结构,包括index,x,y,vx,vy,后两者为速度。
开始模拟粒子运动,粒子质量为1,不断地通过内部timer触发'tick'事件。
根据一系列的‘力’来计算每个例子的加速度、速度、位置。
'力'的数据来源: http://networkrepository.com/socfb-Caltech36.php
14.1 Force 的'force'
14.1.1 d3.forceManyBody
粒子之间两两的作用力,strength为正互相吸引,为负则相互排斥。
14.1.2 d3.forceCenter
指向某一个中心的力,会尽可能让粒子向中心靠近或重合。
14.1.3 d3.forceLink
粒子之间两两的作用力?让互相之间有链接的节点保持在某一个特定的距离,是否有链接需要通过图的边集合给出。
simulation = d3.forceSimulation(nodes)
.force('manyBody', d3.forceManyBody().strength(-30))
.force('center', d3.forceCenter(width / 2, height / 2))
.force('link', d3.forceLink(links).strength(0.1),distance(100))
14.1.4 Force: 时钟滴tic答toc
forceSimulation会通过每次'tick'来更新当前节点的状态,状态包括位置、速度、加速度等。
更新后的状态仅仅为'状态',不会反映到任何图元,仅仅对数据进行修改
人为设置每次tick要如何更新图元,simulation.on('tick', ticked);
在初始化每个图元后,只要为simulation配置了'tick'的回调,simulation会自动开始模拟。注意:simulation.stop()会停止timer的tick循环。
function ticked(){
lines
.attr('x1', d=> d.source.x)
.attr('y1', d=> d.source.y)
.attr('x2', d=> d.target.x)
.attr('y2', d=> d.target.y);
circles
.attr('cx', d=> d.x)
.attr('cy', d=> d.y)
}
15 Color-Gradient
使用颜色的"梯度"设置图元的填充,"fill"属性不是单纯可以使用"颜色"进行填充。
Linear: 线性"梯度"。
Radial: 从中心向外扩散的"梯度"。
设置图元的梯度填充:
- 对填充进行定义并赋予索引。
- 使用索引设置图元的fill属性。
<defs>
<linearGradient id="linearGrad">
<stop offset="0%" stop-color="#8dd3c7" stop-opacity="1"></stop>
<stop offset="100%" stop-color="#ffffb3" stop-opacity="1"></stop>
</linearGradient>
</defs>
<svg width="200" height="200">
<circle cx="100" cy="100" r="50" fill="url(#linearGrad)"/>
</svg>
16 添加一副图片
添加一个纯粹的图片HTML元素:
svg.append("image").attr("id", "myimg")
.attr("x", 600).attr("y", 350)
.attr("width", 300).attr("height", 300)
.attr("preserveAspectRatio", "none")
.attr("href", "ff7.jpg")
- 一张图片自身以link的形式给出。
- '图片'(image)本身也是一个HTML元素。
- href表示图片的路径。
- preserveAspectRatio表示图片的'对齐'与'缩放'。
16.1 perserveAspectRatio
-
此属性表示图片的'对齐'和'缩放'。
-
图片下方表示这个属性的可能取值。
-
取值不限于下方的示例。
-
meet和slice本质上是对viewbox这个属性进行修改。
-
none
-
xMidYMid slice
-
xMidYMid meet
-
xMidYmin slice
-
xMaxYMax meet
17 Pattern
width, height:在重复下一个图案之前应该跨过多远。
x,y:Pattern的偏移。
patternUnit: Pattern'使用'的坐标系。
预定义的图形、图像通过子节点的方式(如d3.append)给出
<defs>
<pattern id="eofxu" patternUnits="userSpaceOnUse" width="20" height="20">
<circle cx="10" cy="10" r="2" fill="green" stroke="green" stroke-width="0"></circle>
</pattern>
</defs>
<circle class="mark" cx="400" cy="50" r="100" style="stroke: black; fill: url("#eofxu");"></circle>
18 Stack + Path(可以) = 主题河流
关键接口:
- d3.stack().offset(d3.stackOffsetWiggle)
- d3.area():设置path的d属性
- clipPath: 动效
const area = d3.area()
.curve(d3.curveCardinal.tension(0.3))
.x(d => xScale(xValue(d.data)))
.y0(d => yScale(d[0]))
.y1(d => yScale(d[1]));
18.1 同一坐标轴的不同尺度
yScale = d3.scaleLinear()
.domain([up_max, 0, -low_max])
.range([innerHeight, innerHeight / 2, 0]])
.nice();
19 D3中的数字格式化与坐标轴的格式化
d3.format(specifier),specifier遵循下述规则
[[fill]align][sign][symbol][0][width][,][.precision][~][type]
典型的formater,'.'后的数字表示精度:
- d3.format('.2f')(666.666) 小数点后保留两位,666.67
- d3.format('.2r')(2467) 只保留两位有效数字 2500
- d3,format('.3s')(2366.666) 只保留三位有效数字且加以后缀 2.37K
.tickFormat(d3.format(xxx)): 根据formater来设置坐标轴上的数字格式
时间的格式化
d3.timeFormat(xxx),如 d3.timeFormat('%b-%b')
20 辅助交互的接口
20.1 Drag
21 异步机制
async:将函数转换为异步函数,即把返回值包装成一个Promise对象。
await:强制等待异步函数执行结束。
let newdata = await d3.csv("static/data/2019.json"。
注意await 关键字只能async中使用。
使用异步机制等待transition的结束
why? -> 多个transition对于同一个图元的转换不会自动同步。
transition.end()可以返回一个Promise对象。
使用await关键字可以对这个promise进行等待。
21.1 transition.tween
transition.attr v.s selection.attr。
selection.attr 设置/获取图元的属性
transition.attr 使用默认的插值器在两个给定的属性之间进行过度(tween),例如从红色渐变到蓝色、从左边移动到右边 .....
,tween:介词,之间,补间的含义。
transition.tween 使用自定义的插值器进行过度
'自定义的插值器':返回函数的函数
- 插值器本身的参数:类似于.attr中的数据'd'与索引'l'
- 返回函数的参数:从0到1的、根据时间变化的数字t
22 d3.js的局限性
D3尽管作为目前Web端最强大的可视化框架之一
三维数据的可视化。
确实存在第三方的库可以解决: d3-3d
游戏引擎(unity, unreal) or three.js
使用three.js可视化三维场景并自由漫游