iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
0
自我挑戰組

從範例學 D3.js系列 第 3

[03] 從範例學 D3.js - Zoom

  • 分享至 

  • xImage
  •  

中斷了。那就再繼續吧
這次將上次的範例修改,可以透過 d3.zoom() 來達成縮放分布圖與座標軸
上一篇

目標

上次遇到的問題是,
對於坐標軸轉換、圖形位置的轉換,用 scaleLinear() 可以做到
但是,那圖形的放大縮小呢?
要怎麼去算出來比較好 .... 用 zoom()

這次的結果

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 event

再來關鍵的是,當 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);
}

Reset

最後,為了方便復原至縮放前的狀態,我們可以透過使用 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 上的結果

謝謝。

References


上一篇
[02] 從範例學 D3.js - Scaling
系列文
從範例學 D3.js3
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言