Day15-D3 的 Zoom 缩放

本篇大纲:d3.zoom( )、zoom 旗下的API、范例

上一篇看完让人烧脑的Force之後,我们这次来看看 D3 另一个有趣的特效:Zoom 缩放~它算是图表中蛮常见的功能,有时候图表的资料太多或是太密集,就能透过缩放功能让使用者能更方便的浏览图表并找到想要的资讯。这个功能虽然比 Froce 简单 (我觉得啦),但它有不少细节跟参数需要设定,今天就来仔细的看一下吧!

d3.zoom( )

当我们使用 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 元素上
https://ithelp.ithome.com.tw/upload/images/20210927/20134930DiSlbc1zNu.jpg

建立好zoom之後,D3也提供了非常多 zoom 旗下的API 以供我们调整缩放的细节
https://ithelp.ithome.com.tw/upload/images/20210927/20134930Z9Qta3FEum.jpg

有需要的人可以上官网看每个API的详细设定,我们这边就先挑比较常用的几个讲解

zoom.transform( )、zoom.translateBy( )、zoom.translateTo( )

这几个方法主要是是用来调整 zoom 的位置。当我们使用 d3.zoom 建立一个 zoom 行为时,zoom 行为的相关状态会被储存到 DOM 元素上,而这个相关的状态可以透过两种方式改变

  1. 使用者的互动行为
  2. zoom.transform 方法

之後透过 .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 相关的设定後,我们实际来写一些范例吧!

范例一: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)

这样就完成啦!!
https://i.imgur.com/Wbg74hO.gif

如果我们想要额外进行一些细节设定,也可以使用上面介绍的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);

范例二:Input 调整 zoom 缩放比例

接着我们来看另一个有趣的例子,这个其实是我朋友接到的一个客户需求,客户的需求是:

  1. 希望能自己输入想要的缩放大小
  2. 希望有个reset功能,能回覆到图表原本的样子

为了满足这两个条件,我们要有一个 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 的功能就完成了~
https://i.imgur.com/FTJcIdL.gif

接着我们来做输入缩放数值的功能吧,先在画面上建立 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的数值。这部分应该还有可以优化的地方,如果大家有想到其他更好的方法,欢迎给我一些建议~~
https://i.imgur.com/Tkwqh2I.gif


好啦,D3 的缩放功能就先讲到这边,等之後结合图表去使用缩放功能,会更加有趣的~~

Github Page 图表与 Github 程序码

这边附上本章的程序码与图表 GithubGithub Page,需要的人请自行取用~


<<:  【Day 12】Set 介绍

>>:  Day15-Overloading

晨间读报,掌握情报

不同的产业,会被政府法令或全球时事所影响,比如说一个油价调涨的新闻一出,原物料的供给成本,会连结到海...

[Python]决策数01─运用CART做决策树

Hi! 大家好,我是Eric,这次要来用Python做决策树。 缘起:决策树因为相对於其他机器学习...

Day30影片教学:Azure小白如何使用Azure Active Directory Identity protection管好管满

在昨天我们谈完Azure小白想早下班-之-使用Azure Synapse Analytics汇入数P...

[Day07] CH05:如果我有一座新冰箱——if/else 条件判断

昨天的练习题有做出来吗?是不是有遇到什麽问题呢? 第一个问题应该是两数相除完呈现整数,这时候我们需要...

2021 WordPress虚拟主机推荐-从SiteGround搬到CLOUDWAYS

想记录一下搬家到Cloudways的心路历程以及经验分享... 老实说,我非常庆幸自己没有经历过Su...