本篇大纲:d3.zoom( )、zoom 旗下的API、范例
上一篇看完让人烧脑的Force之後,我们这次来看看 D3 另一个有趣的特效:Zoom 缩放
~它算是图表中蛮常见的功能,有时候图表的资料太多或是太密集,就能透过缩放功能让使用者能更方便的浏览图表并找到想要的资讯。这个功能虽然比 Froce 简单 (我觉得啦),但它有不少细节跟参数需要设定,今天就来仔细的看一下吧!
当我们使用 D3 设定缩放功能时,它其实同时包含了平移
跟缩放
两种功能,我们能使用滑鼠滚轮或手指触控的方式在画面上进行相对应的操作。由於 Zoom 会跟多种原生 DOM 事件相呼应,官方文件也列出每个原生的事件会进行哪种Zoom 的操作
原生事件 | zoom 事件 |
---|---|
mousedown (滑鼠点击开始) | start |
mousemove (滑鼠移动) | zoom |
mouseup (滑鼠放开) | end |
dblclick (滑鼠双击) | 放大 |
wheel (滚轮) | zoom |
touchstart (触控开始) | 放大 |
touchmove (触控移动) | zoom |
touchend (触控结束) | end |
了解哪些动作会进行 zoom 对应的行为後,我们来看看官方Zoom文件提供的许多 API 能够对 zoom 进行哪些缩放相关设定:
d3.zoom( )
从官方文件得知,当我们使用 d3.zoom( )时,它会建立一个缩放的行为并回传一个 zoom 方法(也是物件),接着我们要用 selection.call( ) 去呼叫这个方法并绑订到 DOM 元素上
建立好zoom之後,D3也提供了非常多 zoom 旗下的API 以供我们调整缩放的细节
有需要的人可以上官网看每个API的详细设定,我们这边就先挑比较常用的几个讲解
zoom.transform( )、zoom.translateBy( )、zoom.translateTo( )
这几个方法主要是是用来调整 zoom 的位置。当我们使用 d3.zoom 建立一个 zoom 行为时,zoom 行为的相关状态会被储存到 DOM 元素上,而这个相关的状态可以透过两种方式改变
之後透过 .translateBy( ),可以调整 transform 的 x, y 值;透过 .tramslateTo( ) 则可以将 tramsform 置於正中间
zoom.extent([x0, y0], [x1, y1])
这个API 是用来设定viewport的范围,[x0, y0] 代表svg 左上方的起点位置,[x1, y1] 则代表终点的座标,藉由这个方法就能把缩放的画布限缩在某个范围的viewport 中
const zoom = d3.zoom()
.extent([[0, 0], [250, 250]])
zoom.scaleExtent([k0, k1])
设定缩放系数的大小范围,k0 代表缩放的最小值,k1则是缩放的最大值,缩放的比例则会被限制在最小值跟最大值中间,预设是 [0, ∞]。如果两个值都设定为1的话,就代表不能放大或缩小
zoom.duration( )
这个 API 是用来设定滑鼠双击/触控双击时,zoom缩放的变换时长,如果没有特别设定的话预设是250毫秒。
zoom.on( )
主要是用来监听缩放事件的方法。如果想要取消某些原生事件,让它不要与缩放事件呼应,也可以用 zoom.on( ) 把事件设定为null,例如:
selection
.call(zoom)
.on("wheel.zoom", null);
介绍完这几个 Zoom 相关的设定後,我们实际来写一些范例吧!
我们先来做一个最基础的圆点缩放范例。首先,先建立一个svg画面,并加上一个圆点点
// html
<div class="zoom1"></div>
// js
const svg = d3.select('.zoom1')
.append('svg')
.attr('width', width)
.attr('height', height)
// 加个圆点点
const circle = svg.append('circle')
.attr("id", "dot")
.attr("cx", 150)
.attr("cy", 150)
.attr("r", 40)
.attr("fill", "#69b3a2");
接着,我们来建立缩放事件
// Zoom 事件
const zoom = d3.zoom()
// 用 on 来监听缩放事件启动後,要进行什麽动作
.on('zoom', function(event) {
//这边决定要放大谁,我想放大circle
circle.attr('transform', event.transform);
});
// 用 selection.call 呼叫 zoom 方法
svg.call(zoom)
这样就完成啦!!
如果我们想要额外进行一些细节设定,也可以使用上面介绍的API来调整
// Zoom 事件
const zoom = d3.zoom()
// 限定viewport视窗范围
.extent([[0, 0], [250, 250]])
// 限制缩放大小范围
.scaleExtent([0, 5])
// 设定双击的动画时长
.duration(600)
.on('zoom', function(event) {
console.log(event)
circle.attr('transform', event.transform);
});
svg.call(zoom)
// 设定滑鼠滚轮滚动时,不要进行缩放
.on("wheel.zoom", null);
接着我们来看另一个有趣的例子,这个其实是我朋友接到的一个客户需求,客户的需求是:
为了满足这两个条件,我们要有一个 reset button,接着再给一个 input,让客户能自己输入想要的缩放数值。好,说干就干!
我们用范例一建立的单纯圆点来示范,一样先建立好圆点
//html
<div class="zoom2"></div>
// js
const width = 500, height = 400
const svg = d3.select('.zoom2')
.append('svg')
.attr('width', width)
.attr('height', height)
// 加个圆点点
const circle = svg.append('circle')
.attr("id", "dot")
.attr("cx", 150)
.attr("cy", 150)
.attr("r", 40)
.attr("fill", "#69b3a2");
// Zoom 事件
const zoom = d3.zoom()
.on('zoom', function(event) {
circle.attr('transform', event.transform);
});
svg.call(zoom);
接着加上 reset button,并设定reset button 点击时要触发的动作:用 d3.zoomIdentity.scale(1) 把画面回归到一开始设定的大小
// 加上reset button
const resetBtn = d3.select('.zoom2')
.append('div')
.append('button')
.attr('class', 'btn btn-primary')
.attr('id', 'reset')
.text('reset');
// reset button 触发
resetBtn.on('click', function(){
const transform = d3.zoomIdentity.scale(1)
svg.call(zoom.transform, transform);
});
这样 reset 的功能就完成了~
接着我们来做输入缩放数值的功能吧,先在画面上建立 input 跟按钮
// input 输入 Zoom 的大小
const div = d3.select('.zoom2')
.append('div')
.attr('class', 'mt-3')
const zoomScaleInput = div.append('input')
.attr('id', 'zoomscale')
const zoomScaleBtn = div.append('button')
.attr('class', 'btn btn-primary ms-2')
.text('执行');
接着设定按钮点击时,要抓取 input 的数值套用到 d3.zoomIdentity.scale上,最後再呼叫 zoom.transform
// 设定取 value 的值,套用 zoom 的倍数
zoomScaleBtn.on('click', function(){
const scaleValue = document.getElementById('zoomscale').value;
const transform = d3.zoomIdentity.scale(`${scaleValue}`)
svg.call(zoom.transform, transform);
});
这样就完成啦!不过这个方式有个缺点: d3.zoomIdentity.scale 只能填入大於1的数值。这部分应该还有可以优化的地方,如果大家有想到其他更好的方法,欢迎给我一些建议~~
好啦,D3 的缩放功能就先讲到这边,等之後结合图表去使用缩放功能,会更加有趣的~~
这边附上本章的程序码与图表 Github 、 Github Page,需要的人请自行取用~
不同的产业,会被政府法令或全球时事所影响,比如说一个油价调涨的新闻一出,原物料的供给成本,会连结到海...
Hi! 大家好,我是Eric,这次要来用Python做决策树。 缘起:决策树因为相对於其他机器学习...
在昨天我们谈完Azure小白想早下班-之-使用Azure Synapse Analytics汇入数P...
昨天的练习题有做出来吗?是不是有遇到什麽问题呢? 第一个问题应该是两数相除完呈现整数,这时候我们需要...
想记录一下搬家到Cloudways的心路历程以及经验分享... 老实说,我非常庆幸自己没有经历过Su...