声明: 本教程针对 D3.js v3 版本进行讲解
Lesson1 理解 UpdateEnterExit
UpdateEnterExit 是 D3 中三个非常重要的概念, 它处理的是当选择集和数据的数量关系不确定的情况
1. 什么是 UpdateEnterExit
前面章里, 反复出现了形如以下的代码
- svg.selectAll("rect") // 选择 svg 内所有的矩形
- .data(dataset) // 绑定数组
- .enter() // 指定选择集的 enter 部分
- .append("rect") // 添加足够数量的矩形元素
前面提到, 这段代码使用的情况是当以下情况出现的时候:
有数据, 而没有足够图形元素的时候, 使用此方法可以添加足够的元素
当时并没有深究这段代码是什么意思, 本章将对此进行讲解但是, 由于此问题相对复杂, 本章只进行最初步的介绍
假设, 在 body 中有三个 p 元素, 有一数组 [3, 6, 9], 则可以将数组中的每一项分别与一个 p 元素绑定在一起但是, 有一个问题: 当数组的长度与元素数量不一致 (数组长度> 元素数量 or 数组长度 <元素数量) 时呢? 这时候就需要理解 UpdateEnterExit 的概念
如果数组为 [3, 6, 9, 12, 15], 将此数组绑定到三个 p 元素的选择集上可以想象, 会有两个数据没有元素与之对应, 这时候 D3 会建立两个空的元素与数据对应, 这一部分就称为 Enter 而有元素与数据对应的部分称为 Update 如果数组为 [3], 则会有两个元素没有数据绑定, 那么没有数据绑定的部分被称为 Exit 示意图如下所示
看到这, 我想大家能体会到为什么本节最开始处的代码能够给 SVG 内添加足够数量的元素了吧它的意思其实是:
此时 SVG 里没有 rect 元素, 即元素数量为 0 有一数组 dataset, 将数组与元素数量为 0 的选择集绑定后, 选择其 Enter 部分 (请仔细看上图), 然后添加(append) 元素, 也就是添加足够的元素, 使得每一个数据都有元素与之对应
2. Update 和 Enter 的使用
当对应的元素不足时 ( 绑定数据数量> 对应元素 ), 需要添加元素(append)
现在 body 中有三个 p 元素, 要绑定一个长度大于 3 的数组到 p 的选择集上, 然后分别处理 update 和 enter 两部分
- var dataset = [ 3 , 6 , 9 , 12 , 15 ];
- // 选择 body 中的 p 元素
- var p = d3.select("body").selectAll("p");
- // 获取 update 部分
- var update = p.data(dataset);
- // 获取 enter 部分
- var enter = update.enter();
- //update 部分的处理: 更新属性值
- update.text(function(d){
- return "update" + d;
- });
- //enter 部分的处理: 添加元素后赋予属性值
- enter.append("p")
- .text(function(d){
- return "enter" + d;
- });
结果如下图, update 部分和 enter 部分被绑定的数据很清晰地表示了出来
请大家记住:
update 部分的处理办法一般是: 更新属性值
enter 部分的处理办法一般是: 添加元素后, 赋予属性值
3. Update 和 Exit 的使用
当对应的元素过多时 ( 绑定数据数量 < 对应元素 ), 需要删掉多余的元素
现在 body 中有三个 p 元素, 要绑定一个长度小于 3 的数组到 p 的选择集上, 然后分别处理 update 和 exit 两部分
- var dataset = [3];
- // 选择 body 中的 p 元素
- var p = d3.select("body").selectAll("p");
- // 获取 update 部分
- var update = p.data(dataset);
- // 获取 exit 部分
- var exit = update.exit();
- //update 部分的处理: 更新属性值
- update.text(function(d) {
- return "update" + d;
- });
- //exit 部分的处理: 修改 p 元素的属性
- exit.text(function(d) {
- return "exit";
- });
- //exit 部分的处理通常是删除元素
- // exit.remove();
结果如下, 请大家区分好 update 部分和 exit 部分这里为了表明哪一部分是 exit, 并没有删除掉多余的元素, 但实际上 exit 部分的绝大部分操作是删除
请大家记住:
exit 部分的处理办法一般是: 删除元素(remove)
Lesson2 让图表动起来
D3 支持制作动态的图表有时候, 图表的变化需要缓慢的发生, 以便于让用户看清楚变化的过程, 也能给用户不小的友好感
1. 什么是动态效果
前面一章制作的图表是一蹴而就地出现, 然后绘制完成后不再发生变化的, 这是静态的图表
动态的图表, 是指图表在某一时间段会发生某种变化, 可能是形状颜色位置等, 而且用户是可以看到变化的过程的
例如, 有一个圆, 圆心为 (100, 100)现在我们希望圆的 x 坐标从 100 移到 300, 并且移动过程在 2 秒的时间内发生
这种时候就需要用到动态效果, 在 D3 里我们称之为过渡(transition)
2. 实现动态的方法
D3 提供了 4 个方法用于实现图形的过渡: 从状态 A 变为状态 B
transition()
启动过渡效果
其前后是图形变化前后的状态(形状位置颜色等等), 例如:
- .attr("fill","red") // 初始颜色为红色
- .transition() // 启动过渡
- .attr("fill","steelblue") // 终止颜色为铁蓝色
D3 会自动对两种颜色 (红色和铁蓝色) 之间的颜色值 (RGB 值) 进行插值计算, 得到过渡用的颜色值我们无需知道中间是怎么计算的, 只需要享受结果即可
duration()
指定过渡的持续时间, 单位为毫秒
如 duration(2000) , 指持续 2000 毫秒, 即 2 秒
ease()
指定过渡的方式, 常用的有:
linear: 普通的线性变化
circle: 慢慢地到达变换的最终状态
elastic: 带有弹跳的到达最终状态
bounce: 在最终状态处弹跳几次
调用时, 格式形如: ease(bounce)
delay()
指定延迟的时间, 表示一定时间后才开始转变, 单位同样为毫秒此函数可以对整体指定延迟, 也可以对个别指定延迟
例如, 对整体指定时:
- .transition()
- .duration(1000)
- .delay(500)
如此, 图形整体在延迟 500 毫秒后发生变化, 变化的时长为 1000 毫秒因此, 过渡的总时长为 1500 毫秒
又如, 对一个一个的图形 (图形上绑定了数据) 进行指定时:
- .transition()
- .duration(1000)
- .delay(funtion(d,i){
- return 200*i;
- })
如此, 假设有 10 个元素, 那么第 1 个元素延迟 0 毫秒(因为 i = 0), 第 2 个元素延迟 200 毫秒, 第 3 个延迟 400 毫秒, 依次类推. 整个过渡的长度为 200 * 9 + 1000 = 2800 毫秒
3. 实现简单的动态效果
下面将在 SVG 画布里添加三个圆, 圆出现之后, 立即启动过渡效果
第一个圆, 要求移动 x 坐标
- var circle1 = svg.append("circle")
- .attr("cx", 100)
- .attr("cy", 100)
- .attr("r", 45)
- .style("fill","green");
- // 在 1 秒 (1000 毫秒) 内将圆心坐标由 100 变为 300
- circle1.transition()
- .duration(1000)
- .attr("cx", 300);
第二个圆, 要求既移动 x 坐标, 又改变颜色
- var circle2 = svg.append("circle")... // 与第一个圆一样, 省略部分代码
- // 在 1.5 秒 (1500 毫秒) 内将圆心坐标由 100 变为 300,
- // 将颜色从绿色变为红色
- circle2.transition()
- .duration(1500)
- .attr("cx", 300)
- .style("fill","red");
第三个圆, 要求既移动 x 坐标, 又改变颜色, 还改变半径
- var circle3 = svg.append("circle")... // 与第一个圆一样, 省略部分代码
- // 在 2 秒 (2000 毫秒) 内将圆心坐标由 100 变为 300
- // 将颜色从绿色变为红色
- // 将半径从 45 变成 25
- // 过渡方式采用 bounce(在终点处弹跳几次)
- circle3.transition()
- .duration(2000)
- .ease("bounce")
- .attr("cx", 300)
- .style("fill","red")
- .attr("r", 25);
4. 给柱形图加上动态效果
在上一章完整柱形图的基础上稍作修改, 即可做成一个带动态效果的有意思的柱形图
在添加文字元素和矩形元素的时候, 启动过渡效果, 让各柱形和文字缓慢升至目标高度, 并且在目标处跳动几次
对于文字元素, 代码如下:
- .attr("y",function(d){
- var min = yScale.domain()[0];
- return yScale(min);
- })
- .transition()
- .delay(function(d,i){
- return i * 200;
- })
- .duration(2000)
- .ease("bounce")
- .attr("y",function(d){
- return yScale(d);
- });
文字元素的过渡前后, 发生变化的是 y 坐标其起始状态是在 y 轴等于 0 的位置 (但要注意, 不能在起始状态直接返回 0, 要应用比例尺计算画布中的位置) 终止状态是目标值
对于矩形元素, 思想与文字元素一样, 只是在计算起始状态时要稍微复杂一些, 请读者自行研读页面底部的示例代码地址中的代码
Lesson3 交互式操作
与图表的交互, 指在图形元素上设置一个或多个监听器, 当事件发生时, 做出相应的反应
1. 什么是交互
交互, 指的是用户输入了某种指令, 程序接受到指令之后必须做出某种响应对可视化图表来说, 交互能使图表更加生动, 能表现更多内容例如, 拖动图表中某些图形鼠标滑到图形上出现提示框用触屏放大或缩小图形等等
用户用于交互的工具一般有三种: 鼠标键盘触屏
2. 如何添加交互
对某一元素添加交互操作十分简单, 代码如下:
- var circle = svg.append("circle");
- circle.on("click", function(){
- // 在这里添加交互内容
- });
这段代码在 SVG 中添加了一个圆, 然后添加了一个监听器, 是通过 on() 添加的在 D3 中, 每一个选择集都有 on() 函数, 用于添加事件监听器
on() 的第一个参数是监听的事件, 第二个参数是监听到事件后响应的内容, 第二个参数是一个函数
鼠标常用的事件有:
click: 鼠标单击某元素时, 相当于 mousedown 和 mouseup 组合在一起
mouseover: 光标放在某元素上
mouseout: 光标从某元素上移出来时
mousemove: 鼠标被移动的时候
mousedown: 鼠标按钮被按下
mouseup: 鼠标按钮被松开
dblclick: 鼠标双击
键盘常用的事件有三个:
keydown: 当用户按下任意键时触发, 按住不放会重复触发此事件该事件不会区分字母的大小写, 例如 A 和 a 被视为一致
keypress: 当用户按下字符键 (大小写字母数字加号等号回车等) 时触发, 按住不放会重复触发此事件该事件区分字母的大小写
keyup: 当用户释放键时触发, 不区分字母的大小写
触屏常用的事件有三个:
touchstart: 当触摸点被放在触摸屏上时
touchmove: 当触摸点在触摸屏上移动时
touchend: 当触摸点从触摸屏上拿开时
当某个事件被监听到时, D3 会把当前的事件存到 d3.event 对象, 里面保存了当前事件的各种参数, 请大家好好参详如果需要监听到事件后立刻输出该事件, 可以添加一行代码:
- circle.on("click", function(){
- console.log(d3.event);
- });
3. 带有交互的柱形图
将 D3.js 入门系列一的部分代码修改成如下代码
- var rects = svg.selectAll(".MyRect")
- .data(dataset)
- .enter()
- .append("rect")
- .attr("class","MyRect") // 把类里的 fill 属性清空
- .attr("transform","translate(" + padding.left + "," + padding.top + ")")
- .attr("x", function(d,i){
- return xScale(i) + rectPadding/2;
- } )
- .attr("y",function(d){
- return yScale(d);
- })
- .attr("width", xScale.rangeBand() - rectPadding )
- .attr("height", function(d){
- return height - padding.top - padding.bottom - yScale(d);
- })
- .attr("fill","steelblue") // 填充颜色不要写在 CSS 里
- .on("mouseover",function(d,i){
- d3.select(this)
- .attr("fill","yellow");
- })
- .on("mouseout",function(d,i){
- d3.select(this)
- .transition()
- .duration(500)
- .attr("fill","steelblue");
- });
这段代码添加了鼠标移入 (mouseover), 鼠标移出(mouseout) 两个事件的监听器监听器函数中都使用了 d3.select(this), 表示选择当前的元素, this 是当前的元素, 要改变响应事件的元素时这么写就好
mouseover 监听器函数的内容为: 将当前元素变为黄色
mouseout 监听器函数的内容为: 缓慢地将元素变为原来的颜色(蓝色)
代码示例地址: github.com/legend-li/D
未完待续~
参考资料: www.ourd3js.com/
D3.js(v3)中文 api:github.com/d3/d3/wiki/
来源: https://juejin.im/post/5aa74339f265da2377191241