中斷了。那就再繼續吧
這次將上次的範例修改,可以透過d3.zoom()
來達成縮放分布圖與座標軸
上一篇
上次遇到的問題是,
對於坐標軸轉換、圖形位置的轉換,用 scaleLinear()
可以做到
但是,那圖形的放大縮小呢?
要怎麼去算出來比較好 .... 用 zoom()
吧
zoom
的行為,是透過 selection.call()
來使用
像是這樣,就是簡單的對 selection
增加 zoom
的行為,並設定 event listener onZoom
selection.call(d3.zoom().on('zoom', onZoom))
接著,我們可以透過 zoom.scaleExtent([extent])
設定縮放比例的最小值與最大值
預設是 [0, ∞]
也就是,上面的片段可以改成
selection.call(d3.zoom()
.scaleExtent([1, 20]) // Scale factor: 1x ~ 20x
.on('zoom', onZoom))
除此之外,還可以使用 zoom.extent([extent])
設定可以處理事件的區塊[exten]=[[x0, y0], [x1, y1]]
從左上到右下座標的這範圍
如此一來,根據上次的範例
我們這樣設定,定義縮放的比例從 1 到 20 倍
與縮放的區塊,是選擇的 element 從 (0, 0)
到 (gWidth, gHeight)
zoom = d3.zoom()
.scaleExtent([1, 20])
.extent([[0, 0], [gWidth, gHeight]])
.on("zoom", onZoom);
此外,我們將會保留前面到建立 scatter 的部分,brush 區塊也不需要用到了
相同概念的,我們建立另一個區塊,監聽 zoom
事件
svg.append("rect")
.attr('id', 'zoom_area')
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.call(zoom);
pointer-events
的樣式表示在甚麼條件/情況下,可以作為滑鼠游標事件的目標 (target)
主要應用在 SVG 上
再來關鍵的是,當 zoom
事件觸發,可以怎麼做?
在 zoom
事件觸發,event listener 接收到的參數會帶有其中一個屬性是 transform
transform
帶有縮放的狀態資訊 (表示一個二維的變形矩陣,請詳見官方文件)
我們可以透過他來做轉換:rescaleX()
rescaleY()
轉換 x, y 的 domain
如此,可以變換我們的 x-y 軸,與圓心的座標換算
function onZoom(ev) {
// recover the new scale
const newX = ev.transform.rescaleX(x);
const newY = ev.transform.rescaleY(y);
const newCX = ev.transform.rescaleX(cx);
const newCY = ev.transform.rescaleY(cy);
// update axes with these new boundaries
svg.select('#axis_x').transition().call(d3.axisTop(newX));
svg.select('#axis_y').transition().call(d3.axisLeft(newY));
// update circle position
scatter
.selectAll("circle")
.attr('cx', d => newCX(d.x))
.attr('cy', d => newCY(d.y))
.attr('transform', ev.transform);
}
最後,為了方便復原至縮放前的狀態,我們可以透過使用 zoom.transform(selection, transform[, point])
做轉換
通常透過 selection.call()
來呼叫
要還原成變更前的狀態,我們可以這樣寫
selection.call(zoom.transform, d3.zoomIdentity)
加上綁定至一個按鈕的 click
事件後:
d3.select('button')
.on('click', () => {
d3.select('#zoom_area').transition().duration(750).call(zoom.transform, d3.zoomIdentity);
})
最後呈現出 Observable 上的結果
謝謝。