本篇大綱: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,需要的人請自行取用~