因为业务需要折腾起了图表,《D3.js-Tree 实战笔记》系列用于记录使用该库制作 Tree 图表的一些笔记。本节我们来给图表添加拖动和缩放吧。
添加鼠标事件
d3-zoom
不得不说,这节我们增加的拖动和缩放功能,都是通过d3-zoom
模块来完成的。
d3-zoom
可以对 selections 进行平移和缩放,它封装了浏览器支持的 input events 并对浏览器兼容性做了处理。
我们先来介绍下基本的 API:
d3.zoom()
创建一个 zoom 操作。返回一个 zoom 对象方法,通常被传递给 selection.call 来调用。
zoom.scaleExtent([extent])
设置或获取缩放范围。默认为[0, ∞]
。
1 2
| var zoom = d3.zoom().scaleExtent([0.1, 100]);
|
zoom.on(typenames[, listener])
三种情况:设置,取消,获取事件监听器。
- 如果指定了 listener,则为对应的 typenames 设置事件监听器。
- 如果 listenter 为 null,则取消对应的 typenames 监听器。
- 如果没有指定 listenter,则返回对应的 typenames 监听器。
typenames 是一个字符串,由 type 和 name 组成。也就是可以为同一种事件类型添加多个监听器。type 必须为如下几种:
- start:开始缩放 (比如鼠标按下)
- zoom:开始缩放变换(比如拖拽)
- end:缩放结束(比如鼠标抬起 )
1 2 3 4 5 6 7 8 9
| var zoom = d3 .zoom() .scaleExtent([0.1, 100]) .on("zoom", () => {}); selection.call(zoom).on("dblclick.zoom", () => {});
|
更多的 API 详情,请参考d3-zoom。
Zoom Events
当 zoom event listener 被调用时, d3.event 会被设置为当前的 zoom 事件。zoom event 对象由以下几部分组成:
- target:当前的缩放 zoom behavior。
- type:事件类型:“start”, “zoom” 或者 “end”; 参考 zoom.on。
- transform:当前的 zoom transform(缩放变换)。
- sourceEvent:原始事件, 比如 mousemove 或 touchmove。
1 2 3 4
| zoom.on("zoom", () => { console.log(d3.event.transform); });
|
添加 zoom 事件的处理
svg 里面包括很多的子元素,我们这里采用给整个 svg 添加 zoom 事件,来进行完整的缩放和拖动处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| var svg = d3 .select("body") .append("svg") .attr("width", width + margin.right + margin.left) .attr("height", height + margin.top + margin.bottom); var view = svg .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var zoom = d3 .zoom() .scaleExtent([0.1, 100]) .on("zoom", () => { view.attr( "transform", "translate(" + (d3.event.transform.x + margin.left) + "," + (d3.event.transform.y + margin.top) + ") scale(" + d3.event.transform.k + ")" ); }); svg.call(zoom).on("dblclick.zoom", () => {});
|
这里,不管是鼠标滚轮的事件还是拖动事件,都会触发 zoom 事件响应。如果说滚轮事件与页面上下滚动事件相斥的话,我们可以通过zoom.filter()
来过滤。我们如果想要改成按下 ctrl 键的同时,滚动鼠标滚轮才进行缩放,可以这样修改:
1 2 3 4 5 6 7 8 9 10
| var zoom = d3 .zoom() .scaleExtent([0.1, 100]) .filter(function() { var isWheelEvent = d3.event instanceof WheelEvent; return !isWheelEvent || (isWheelEvent && d3.event.ctrlKey); }) .on("zoom", () => {});
|
效果如图,缩放前:
放大后:
根据节点数量和层级调整间距
而当我们的层级树和子节点树上去之后,我们的节点会挤到一起,而要避免文字被遮挡,需要调整间距。
我们先来定义两个函数方法,分别用来获取最深层数和最大子节点数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function getMax(obj) { let max = 0; if (obj.children) { max = obj.children.length; obj.children.forEach(d => { const tmpMax = this.getMax(d); if (tmpMax > max) { max = tmpMax; } }); } return max; } function getDepth(obj) { var depth = 0; if (obj.children) { obj.children.forEach(d => { var tmpDepth = this.getDepth(d); if (tmpDepth > depth) { depth = tmpDepth; } }); } return 1 + depth; }
|
同时,我们在每次更新节点状态时,重新进行 tree 的大小调整:
1 2 3 4 5 6 7
| function updateChart(source) { var scale = (getDepth(root) / 8 || 0.5) + (getMax(root) / 12 || 0.5); var treemap = d3.tree().size([height * scale, width]); }
|
结束语
本节我们添加了鼠标操作,包括滚轮缩放和拖动,主要依靠 d3-zoom 模块来实现。同时,考虑文字重叠,也进行了些处理。
细心的小伙伴们或许会发现 d3-drag 模块,骚年还没用到,不过或许区别在于整体的拖动和单个元素的拖动吧。
此处查看项目代码
此处查看页面效果
查看Github有更多内容噢:https://github.com/godbasin
更欢迎来被删的前端游乐场边撸猫边学前端噢