对于前端可视化库来说,交互效果是其基本功能,需要有优雅的效果和简洁的API才能出彩,而如果一个前端可视化工具只能生成静态图表,绝对会显得格格不入,因为在前端拥有交互功能并不复杂。与图表的交互,是指图表元素能根据用户的键盘鼠标操作做出相应的反应,例如悬停高亮、缩放、漫游、拖动节点、点击涟漪效果等等。
对于HTML元素来说,要响应用户的行为,可以在图形元素上添加一个或多个事件监听器,当监测到对应行为时,执行某些响应代码。
事件监听器
JavaScript 有一个事件模型,在这个模型中,“事件”由发生的事情来触发,比如用户通过键鼠或触摸屏输入信息。大多数情况下,没人监听事件,事件就自生自灭,我们就无感知。而如果我们添加事件监听器后,触发对应的事件就能调用这个监听器的设置,具体来说就是执行某些代码。
D3的选择集有一个方法on()
,用来设定事件的监听器。在可视化绘制时我们普遍用了var svg=d3.select("body").append("svg")
或类似的代码,就可以使用以下代码给元素绑定事件监听器:
1 | var rect=svg.selectAll("rect").data(ds).enter().append("rect") |
以上代码可以给柱状图添加悬停高亮的交互效果,mouseover是事件名称,function()是监听器函数。当鼠标移动到某个柱子上时,触发一个mouseover事件,调用function()将所d3所选中的柱的填充色修改为设置的颜色。演示如下:
【gif】
为图表赋予交互能力只要两步:
- 给选择集绑定事件监听器;
- 定义响应行为。
键鼠事件
在交互中最常见的行为当然要属鼠标触发的,经典的鼠标行为有单机、双击、选中拖动等。常用的事件如下:
- click:单击事件,鼠标单击某个元素触发,相当于mousedown和mouseup组合在一起;
- dblclick:鼠标双击事件;
- mouseover:鼠标的光标放在某元素上(悬停在元素上);
- mouseout:光标从某元素上移出来时;
- mousedown:鼠标按钮被按下;
- mouseup:鼠标按钮被松开;
以下代码为图表标题添加了一个单击事件的监听器,当点击标题元素,会将标题加粗并在控制台输出当前标题文本;而如果当前是加粗的效果,点击后是变成非加粗文本,也就是点击会切换加粗和正常文本两种效果;
1 | // var svg=d3.select("body").append("svg") 等等 |
键盘事件也很实用,特别是可以结合一些控制鼠标按键的自动化程序。键盘事件有三种:
- keydown:当用户按下任意键时触发,按住不放会重复触发此事件,这一事件不会区分字母的大小写,例如“A”和“a”被视为一致;
- keypress:当用户按下字符键(大小写字母、数字、加号、等号、回车等)时触发,按住不放会重复触发此事件,该事件就会区分字母的大小写;
- keyup:当用户松开按键时触发,该事件不区分字母的大小写;
keydown和keypress事件的区别在于keydown用于任意键的事件,而keypress用于字符键,如果只需要处理字母数字类的响应,或是要对大小写字母分别处理的时候,使用keypress;如果要处理上下左右(↑→)、Shift、Ctrl等特殊键的输入,使用keydown。
随着各种移动设备的普及,触屏有着广泛的使用场景,无论是我们的手机还是触屏的显示器,触屏离我们很近。常用的触屏事件有以下三种:
- touchstart:当触摸点被放在触摸屏上时,也就是触摸到某个元素;
- touchmove:当触摸点在触摸屏上移动时;
- touchend:当触摸点从触摸屏上拿开时;
我们可以为触摸事件配置点击事件以及拖动事件,也就是触摸有选中并拖动的效果。
缩放
通过d3.zoom().on("zoom", zoomed)
配置缩放的交互,具体用法如下。需要说明的是在v3.x版本中是使用d3.behavior.zoom()
创建缩放行为,而v5.x及之后的版本是d3.zoom()
,不再有behavior这一层抽象;
给矩形和坐标轴添加缩放交互响应:
1 | var zoom = d3.zoom() |
绑定d3.zoom()
的行为后,就具备了漫游的交互,zoom不仅仅可以放大缩小,还可以拖动元素进行漫游。
漫游是一种拖拽效果,但在力导向图等的交互中,我们希望有更纯粹的拖拽元素效果,因此d3也有d3.drag()
用于创建拖拽行为。和zoom一样的,在v5.x版本中是使用d3.drag()
而v3.x版本是使用d3.behavior.drag()
。drag没有缩放功能。
drag和zoom一般通过call调用,写在svg.append("rect")
语句中变成svg.append("rect").call(zoom)
,或者写svg.call(zoom)
。
1 | drag = simulation => { |
[gif]
悬停文本标签
要实现鼠标悬停在图形元素上时显示其标签的tooltip效果,仍然使用选择集的on监听mouseover和mouseout事件,只是把响应的代码从修改选定的rect元素变成了增加文本标签元素,具体实现是可以选择加svg的<text>
标签或者加HTML的<div>
标签,按需使用。
1 | svg.selectAll("rect").data(dataset).enter().append("rect") |
[gif]
过渡动画
过渡动画同样通过事件监听和缓动实现过渡效果和数据更新,实现友好的交互;还有便是用好.transition()
,在方法链上,要把transition的调用插到选择元素之后,改变任何属性之前。transition()默认情况延迟(delay)为0ms,持续时长(duration)为250ms,可以自行设置这两个参数。
例如对一个矩形的变换应用过渡效果:
1 | svg.append("rect") |
和HTML元素交互
D3作为一个JavaScript库,自然可以和原生的HTML元素进行交互,例如响应按钮的点击事件,在html中配置了按钮和点击监测,<button type="button" onclick="update()"> 更新 </button>
,点击按钮触发事件,在函数update里面调用d3的绘制代码,实现交互。
状态条是很实用的元素,通过状态条调节d3图表的参数,例如下面通过状态条调节绘制矩形的填充颜色,给状态条添加了onchange的事件监听器,有变化时更新矩形的颜色。
1 | //html中需要额外引入 <script src="https://unpkg.com/d3-simple-slider"></script> |
可视化结果输出
d3绘制的图像是svg或canvas对象,要将生成的可视化结果导出可以选择直接复制svg节点数据,从DOM里直接复制 SVG 代码,然后粘贴到文本文件里,命名为chart.svg,如果觉得麻烦可以用其他工具,导出的需求挺普遍,当然有大佬造了轮子,d3-downloadable是一个JavaScript库,用于下载绘制的svg图形,在html里引入后,在JavaScript代码里加入svg.call(d3.downloadable({width:w,height:h,filename: "filename"}));
就可以下载svg文件了。而如果只需要图片,就可以直接用截图工具截图保存,例如在写这些笔记时,自己大部分图片都是直接截图的,部分svg图形在DOM里直接复制出来粘到文本文件里。
总结
交互是JavaScript可视化库的基本功能,一些封装的基于前端的Python库也都实现了缩放漫游、悬停文本标签等交互功能。d3实现交互效果并不复杂,只需要对选择集使用on()
,设定事件的监听器,在监听器里写交互的代码,定义响应的行为。基础可视化实现挺简单,而深度交互的内容很多,如更优雅的过渡和渐变效果、更深入的适应触摸设备交互、迷你图加入悬停框等等,在之后的具体实践中深入学习。