d3-zoom
範例 · 縮放和平移 讓使用者透過限制檢視範圍來關注感興趣的區域。它使用直接操作:按一下並拖曳來平移(轉換),旋轉滾輪來縮放(縮放),或用觸控縮放。縮放和平移廣泛用於網頁地圖,但也可以用於密集時間序列和散佈圖等視覺化。
縮放行為是一種靈活的抽象,處理各種輸入模式和瀏覽器怪癖。縮放行為與 DOM 無關,因此你可以將其與 HTML、SVG 或 畫布 搭配使用。你可以將 d3-zoom 與 d3-scale 和 d3-axis 搭配使用,以縮放座標軸。你可以使用 zoom.scaleExtent 來限制縮放,並使用 zoom.translateExtent 來限制平移。你可以將 d3-zoom 與其他行為結合使用,例如 d3-drag 用於拖曳,以及 d3-brush 用於焦點 + 背景。
縮放行為可以使用 zoom.transform 以程式方式控制,讓你能夠實作驅動顯示或分階段處理動畫導覽的使用者介面控制項,以瀏覽你的資料。 平滑縮放轉場基於 Jarke J. van Wijk 和 Wim A.A. Nuij 的“平滑且高效的縮放和平移”。
另請參閱 d3-tile,以取得平移和縮放地圖的範例。
zoom()
原始碼 · 建立新的縮放行為。傳回的行為 zoom 同時是一個物件和一個函式,通常透過 selection.call 套用至所選元素。
zoom(selection)
原始碼 · 將此縮放行為套用至指定的 selection,繫結必要的事件監聽器以允許平移和縮放,並將每個所選元素上的縮放轉換初始化為身分轉換(如果尚未定義)。
此函式通常不會直接呼叫,而是透過 selection.call 呼叫。例如,要實例化縮放行為並將其套用至選取範圍
selection.call(d3.zoom().on("zoom", zoomed));
在內部,縮放行為使用 selection.on 來繫結縮放時所需的事件監聽器。監聽器使用名稱 .zoom
,因此您可以使用以下方式解除縮放行為的繫結
selection.on(".zoom", null);
若要停用僅由滾輪驅動的縮放(例如,不干擾原生捲動),您可以在將縮放行為套用至選取範圍後,移除縮放行為的滾輪事件監聽器
selection
.call(zoom)
.on("wheel.zoom", null);
或者,使用 zoom.filter 以更精確地控制哪些事件可以啟動縮放手勢。
套用縮放行為也會將 -webkit-tap-highlight-color 樣式設定為透明,停用 iOS 上的輕觸亮顯。如果您想要不同的輕觸亮顯顏色,請在套用拖曳行為後移除或重新套用此樣式。
縮放行為會將縮放狀態儲存在套用縮放行為的元素上,而不是縮放行為本身。這允許縮放行為同時套用至多個元素,並進行獨立縮放。縮放狀態可以透過使用者互動或透過 zoom.transform 以程式方式變更。
若要擷取縮放狀態,請在縮放事件監聽器中使用目前 縮放事件 上的 event.transform(請參閱 zoom.on),或針對特定節點使用 zoomTransform。後者對於以程式方式修改縮放狀態非常有用,例如實作按鈕以進行放大和縮小。
zoom.transform(selection, transform, point)
原始碼 · 如果 selection 是選取範圍,則將所選元素的 目前縮放轉換 設定為指定的 transform,並立即發出開始、縮放和結束 事件。
如果 selection 是轉換,則使用 interpolateZoom 定義「縮放」補間至指定的 transform,在轉換開始時發出開始事件,在轉換的每個刻度發出縮放事件,然後在轉換結束(或中斷)時發出結束事件。轉換將嘗試將指定的 point 周圍的視覺移動降至最低;如果未指定 point,則預設為視窗 範圍 的中心。
轉換可以指定為縮放轉換或傳回縮放轉換的函數;類似地,點可以指定為二元素陣列 [x, y] 或傳回此類陣列的函數。如果為函數,則會針對每個選取的元素呼叫它,傳遞目前的事件 (event
) 和資料 d
,其中 this
的內容為目前的 DOM 元素。
此函數通常不會直接呼叫,而是透過選取.call或轉換.call呼叫。例如,要立即將縮放轉換重設為單位轉換
selection.call(zoom.transform, d3.zoomIdentity);
要平順地將縮放轉換在 750 毫秒內重設為單位轉換
selection.transition().duration(750).call(zoom.transform, d3.zoomIdentity);
此方法需要您完整指定新的縮放轉換,而且不會強制套用已定義的縮放範圍和平移範圍(如果有的話)。若要從現有轉換衍生新的轉換,並強制套用縮放和平移範圍,請參閱便利方法縮放.translateBy、縮放.scaleBy和縮放.scaleTo。
縮放.translateBy(選取, x, y)
來源 · 如果選取是選取,則平移選取元素的目前縮放轉換x和y,使得新的tx1 = tx0 + kx且ty1 = ty0 + ky。如果選取是轉換,則定義一個「縮放」補間,平移目前的轉換。此方法是縮放.transform的便利方法。x和y平移量可以指定為數字或傳回數字的函數。如果為函數,則會針對每個選取的元素呼叫它,傳遞目前的資料 d
和索引 i
,其中 this
的內容為目前的 DOM 元素。
縮放.translateTo(選取, x, y, p)
來源 · 如果選取是選取,則平移選取元素的目前縮放轉換,使得給定的位置⟨x,y⟩出現在給定的點p。新的tx = px - kx且ty = py - ky。如果未指定p,則預設為視窗範圍的中心。如果選取是轉換,則定義一個「縮放」補間,平移目前的轉換。此方法是縮放.transform的便利方法。x和y座標可以指定為數字或傳回數字的函數;類似地,p點可以指定為二元素陣列 [px,py] 或函數。如果為函數,則會針對每個選取的元素呼叫它,傳遞目前的資料 d
和索引 i
,其中 this
的內容為目前的 DOM 元素。
zoom.scaleBy(selection, k, p)
原始碼 · 如果 selection 是選取,縮放 選取元素的目前縮放轉換為 k,因此新的 k₁ = k₀k。參考點 p 會移動。如果未指定 p,它會預設為視窗範圍的中心。如果 selection 是轉場,定義一個「縮放」補間,轉換目前的轉換。這個方法是 zoom.transform 的便利方法。k 縮放因子可以指定為數字或傳回數字的函式;類似地,p 點可以指定為二元素陣列 [px,py] 或函式。如果為函式,它會為每個選取元素呼叫,傳遞目前資料 d
和索引 i
,其中 this
的內容為目前的 DOM 元素。
zoom.scaleTo(selection, k, p)
原始碼 · 如果 selection 是選取,縮放 選取元素的目前縮放轉換為 k,因此新的 k₁ = k。參考點 p 會移動。如果未指定 p,它會預設為視窗範圍的中心。如果 selection 是轉場,定義一個「縮放」補間,轉換目前的轉換。這個方法是 zoom.transform 的便利方法。k 縮放因子可以指定為數字或傳回數字的函式;類似地,p 點可以指定為二元素陣列 [px,py] 或函式。如果為函式,它會為每個選取元素呼叫,傳遞目前資料 d
和索引 i
,其中 this
的內容為目前的 DOM 元素。
zoom.constrain(constrain)
原始碼 · 如果指定了 constrain,則將變換約束函數設定為指定函數,並傳回縮放行為。如果未指定 constrain,則傳回目前的約束函數,其預設值為
function constrain(transform, extent, translateExtent) {
var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
return transform.translate(
dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
);
}
約束函數必須傳回一個 [transform]#zoomTransform),給定目前的 transform、視窗範圍 和 平移範圍。預設實作嘗試確保視窗範圍不會超出平移範圍。
zoom.filter(filter)
原始碼 · 如果指定了 filter,則將篩選器設定為指定函數,並傳回縮放行為。如果未指定 filter,則傳回目前的篩選器,其預設值為
function filter(event) {
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
}
篩選器會傳遞目前的事件 (event
) 和資料 d
,其中 this
的內容為目前的 DOM 元素。如果篩選器傳回假值,則會忽略啟動事件,且不會啟動任何縮放手勢。因此,篩選器會決定要忽略哪些輸入事件。預設篩選器會忽略次要按鈕上的 mousedown 事件,因為這些按鈕通常用於其他用途,例如內容功能表。
zoom.touchable(touchable)
原始碼 · 如果指定了 touchable,則將觸控支援偵測器設定為指定函數,並傳回縮放行為。如果未指定 touchable,則傳回目前的觸控支援偵測器,其預設值為
function touchable() {
return navigator.maxTouchPoints || ("ontouchstart" in this);
}
只有當縮放行為 套用 時,偵測器才會為對應的元素傳回真值,才會註冊觸控事件監聽器。預設偵測器適用於大多數具備觸控輸入功能的瀏覽器,但並非全部;例如,Chrome 的行動裝置模擬器就無法偵測。
zoom.wheelDelta(delta)
原始碼 · 如果指定了 delta,則將滾輪增量函數設定為指定函數,並傳回縮放行為。如果未指定 delta,則傳回目前的滾輪增量函數,其預設值為
function wheelDelta(event) {
return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * (event.ctrlKey ? 10 : 1);
}
輪差函數回傳的 Δ 值決定了針對 WheelEvent 應用縮放的量。縮放因子 transform.k 會乘以 2Δ;例如,Δ 為 +1 時縮放因子會加倍,Δ 為 -1 時縮放因子會減半。
zoom.extent(extent)
原始碼 · 如果指定了 extent,則將視窗範圍設定為指定的點陣列 [[x0, y0], [x1, y1]],其中 [x0, y0] 是視窗的左上角,而 [x1, y1] 是視窗的右下角,並傳回此縮放行為。extent 也可指定為傳回此類陣列的函數;如果是函數,則會針對每個選取的元素呼叫它,並傳遞目前的資料 d
,其中 this
的內容為目前的 DOM 元素。
如果未指定 extent,則傳回目前的範圍存取器,其預設值為 [[0, 0], [width, height]],其中 width 是元素的 用戶端寬度,而 height 是其 用戶端高度;對於 SVG 元素,則使用最近的祖先 SVG 元素的 viewBox,或 width 和 height 屬性。或者,考慮使用 element.getBoundingClientRect。
視窗範圍會影響數個函數:視窗中心在 zoom.scaleBy 和 zoom.scaleTo 進行變更時保持固定;視窗中心和維度會影響 interpolateZoom 選擇的路徑;並且需要視窗範圍來強制套用選用的 平移範圍。
zoom.scaleExtent(extent)
原始碼 · 如果指定了 extent,則將縮放範圍設定為指定的數字陣列 [k0, k1],其中 k0 是允許的最小縮放因子,而 k1 是允許的最大縮放因子,並傳回此縮放行為。如果未指定 extent,則傳回目前的縮放範圍,其預設值為 [0, ∞]。縮放範圍會限制縮放和縮小。它會在互動時以及使用 zoom.scaleBy、zoom.scaleTo 和 zoom.translateBy 時強制執行;但是,當使用 zoom.transform 明確設定轉換時,則不會強制執行。
如果使用者在已經達到縮放範圍的對應限制時,嘗試透過滾動滑輪進行縮放,則滾動事件將會被忽略,且不會啟動縮放手勢。這允許使用者在縮放後向下捲動超過可縮放區域,或在縮放後向上捲動。如果你希望無論縮放範圍為何,都始終防止滾動滑輪輸入,請註冊滾動事件偵聽器以防止瀏覽器的預設行為
selection
.call(zoom)
.on("wheel", event => event.preventDefault());
zoom.translateExtent(extent)
Source · 如果指定 extent,則將平移範圍設定為指定點陣列 [[x0, y0], [x1, y1]],其中 [x0, y0] 是世界左上角,而 [x1, y1] 是世界右下角,並傳回此縮放行為。如果未指定 extent,則傳回目前的平移範圍,其預設值為 [[-∞, -∞], [+∞, +∞]]。平移範圍會限制平移,且可能會導致縮小時平移。它在互動和使用 zoom.scaleBy、zoom.scaleTo 和 zoom.translateBy 時強制執行;但是,在使用 zoom.transform 明確設定轉換時,不會強制執行。
zoom.clickDistance(distance)
Source · 如果指定 distance,則設定滑鼠在 mousedown 和 mouseup 之間移動的最大距離,這將觸發後續的 click 事件。如果在 mousedown 和 mouseup 之間的任何時間點,滑鼠與其在 mousedown 上的位置距離大於或等於 distance,則會抑制 mouseup 之後的 click 事件。如果未指定 distance,則傳回目前的距離閾值,其預設值為零。距離閾值以客戶端座標測量 (event.clientX 和 event.clientY).
zoom.tapDistance(distance)
Source · 如果指定 distance,則設定雙點輕觸手勢在第一次 touchstart 和第二次 touchend 之間移動的最大距離,這將觸發後續的雙擊事件。如果未指定 distance,則傳回目前的距離閾值,其預設值為 10。距離閾值以客戶端座標測量 (event.clientX 和 event.clientY).
zoom.duration(duration)
原始碼 · 如果指定了 duration,則將雙擊和雙點觸控的縮放轉場時間設定為指定的毫秒數,並傳回縮放行為。如果未指定 duration,則傳回目前的持續時間,預設為 250 毫秒。如果持續時間不大於零,則雙擊和雙點觸控會觸發縮放轉換的即時變更,而不是啟動平滑轉場。
若要停用雙擊和雙點觸控轉場,您可以在將縮放行為套用至選取範圍後,移除縮放行為的 dblclick 事件監聽器
selection
.call(zoom)
.on("dblclick.zoom", null);
zoom.interpolate(interpolate)
原始碼 · 如果指定了 interpolate,則將縮放轉場的內插工廠設定為指定的函式。如果未指定 interpolate,則傳回目前的內插工廠,預設為 interpolateZoom 以實作平滑縮放。若要套用兩個檢視之間的直接內插,請改用 interpolate。
zoom.on(typenames, listener)
原始碼 · 如果指定了 listener,則設定指定 typenames 的事件 listener,並傳回縮放行為。如果已為相同的類型和名稱註冊事件監聽器,則在新增新的監聽器之前會移除現有的監聽器。如果 listener 為 null,則移除指定 typenames 的目前事件監聽器(如果有的話)。如果未指定 listener,則傳回目前指定 typenames 所配對的第一個目前已指派監聽器(如果有的話)。當指定的事件被觸發時,每個 listener 都會使用與 selection.on 監聽器相同的內容和引數來呼叫:目前的事件 (event
) 和資料 d
,其中 this
內容為目前的 DOM 元素。
typenames 是包含一個或多個 typename 的字串,各 typename 以空白分隔。每個 typename 都是一個 type,後方可選擇加上一個句點 (.
) 和一個 name,例如 zoom.foo
和 zoom.bar
;名稱允許為相同的 type 註冊多個監聽器。type 必須是下列其中之一
start
- 縮放開始後(例如在 mousedown 時)。zoom
- 縮放轉換變更後(例如在 mousemove 時)。end
- 縮放結束後(例如在 mouseup 時)。
請參閱 dispatch.on 以取得更多資訊。
縮放事件
當呼叫 縮放事件監聽器 時,它會接收目前的縮放事件作為第一個參數。事件物件會顯示多個欄位
- event.target - 關聯的 縮放行為。
- event.type - 字串“start”、“zoom”或“end”;請參閱 zoom.on。
- event.transform - 目前的 縮放轉換。
- event.sourceEvent - 基礎輸入事件,例如 mousemove 或 touchmove。
縮放行為會處理各種互動事件
事件 | 監聽元素 | 縮放事件 | 預設已防止? |
---|---|---|---|
mousedown⁵ | 選取 | 開始 | 否¹ |
mousemove² | 視窗¹ | 縮放 | 是 |
mouseup² | 視窗¹ | 結束 | 是 |
dragstart² | 視窗 | - | 是 |
selectstart² | 視窗 | - | 是 |
按一下 | 視窗 | - | 是 |
按兩下 | 選取 | 多重⁶ | 是 |
滾輪⁸ | 選取 | 縮放⁷ | 是 |
touchstart | 選取 | 多重⁶ | 否⁴ |
touchmove | 選取 | 縮放 | 是 |
touchend | 選取 | 結束 | 否⁴ |
touchcancel | 選取 | 結束 | 否⁴ |
所有已使用的事件傳播都會 立即停止。
¹ 必須擷取 iframe 外部的事件;請參閱 d3-drag#9。
² 僅適用於活動中的滑鼠手勢;請參閱 d3-drag#9。
³ 僅適用於某些滑鼠手勢後;請參閱 zoom.clickDistance。
⁴ 必須允許 在觸控輸入上模擬按一下;請參閱 d3-drag#9。
⁵ 如果在觸控手勢結束後 500 毫秒內,則會忽略;假設 模擬按一下。
⁶ 按兩下和輕觸兩下會啟動發出開始、縮放和結束事件的轉換;請參閱 zoom.tapDistance。
⁷ 第一個滾輪事件會發出開始事件;如果在 150 毫秒內未收到任何滾輪事件,則會發出結束事件。
⁸ 如果已達到 縮放範圍 的對應限制,則會忽略。
zoomTransform(node)
來源 · 傳回指定 node 的目前轉換。請注意,node 通常應該是 DOM 元素,而不是 選取。(選取可能包含多個處於不同狀態的節點,而此函式只會傳回單一轉換。)如果您有選取,請先呼叫 選取.node
var transform = d3.zoomTransform(selection.node());
在 事件監聽器 的背景下,節點 通常是接收輸入事件的元素(應等於 事件.transform),此
var transform = d3.zoomTransform(this);
在內部,元素的變換儲存在 element.__zoom;但是,您應該使用此方法,而不是直接存取它。如果給定的 節點 沒有定義變換,則傳回最近祖先的變換,或者如果沒有,則傳回 恆等變換。傳回的變換表示形式為
k 0 tx
0 k ty
0 0 1
的二維 變換矩陣(此矩陣只能表示縮放和平移;未來的版本也可能允許旋轉,儘管這可能不是向後相容的變更)。位置 ⟨x,y⟩ 變換為 ⟨xk + tx,yk + ty⟩。變換物件公開以下屬性
- transform.x - 沿著 x 軸的平移量 tx。
- transform.y - 沿著 y 軸的平移量 ty。
- transform.k - 縮放因子 k。
這些屬性應被視為唯讀;不要變異變換,而是使用 transform.scale 和 transform.translate 來衍生新的變換。另請參閱 zoom.scaleBy、zoom.scaleTo 和 zoom.translateBy 以取得縮放行為的便利方法。若要建立具有給定 k、tx 和 ty 的變換
var t = d3.zoomIdentity.translate(x, y).scale(k);
若要將變換套用至 Canvas 2D 背景,請使用 背景.translate,然後使用 背景.scale
context.translate(transform.x, transform.y);
context.scale(transform.k, transform.k);
類似地,若要透過 CSS 將變換套用至 HTML 元素
div.style("transform", "translate(" + transform.x + "px," + transform.y + "px) scale(" + transform.k + ")");
div.style("transform-origin", "0 0");
若要將變換套用至 SVG
g.attr("transform", "translate(" + transform.x + "," + transform.y + ") scale(" + transform.k + ")");
或者更簡單地,利用 transform.toString
g.attr("transform", transform);
請注意變換的順序很重要!縮放必須在平移之前套用。
zoomIdentity
來源 · 恆等變換,其中 k = 1,tx = ty = 0。
new d3.ZoomTransform(k, x, y)
來源 · 傳回縮放比例為 k,平移為 (x, y) 的轉換。
transform.scale(k)
來源 · 傳回縮放比例為 k₁ 的轉換,其中 k₀ 為此轉換的縮放比例。
transform.translate(x, y)
來源 · 傳回平移為 tx1 和 ty1 的轉換,其中 tx0 和 ty0 為此轉換的平移,而 tk 為此轉換的縮放比例。
transform.apply(point)
來源 · 傳回指定 point 的轉換,point 為包含兩個數字元素 [x, y] 的陣列。傳回的點等於 [xk + tx, yk + ty]。
transform.applyX(x)
來源 · 傳回指定 x 座標的轉換,xk + tx。
transform.applyY(y)
來源 · 傳回指定 y 座標的轉換,yk + ty。
transform.invert(point)
來源 · 傳回指定 point 的反轉換,point 為包含兩個數字元素 [x, y] 的陣列。傳回的點等於 [(x - tx) / k, (y - ty) / k]。
transform.invertX(x)
來源 · 傳回指定 x 座標的反轉換,(x - tx) / k。
transform.invertY(y)
來源 · 傳回指定 y 座標的反轉換,(y - ty) / k。
transform.rescaleX(x)
原始碼 · 傳回一個 拷貝 的 連續比例尺 x,其 網域 已轉換。這實作方式是先在比例尺的 範圍 上套用 反向 x 轉換,然後套用 反向比例尺 來計算對應的網域
function rescaleX(x) {
var range = x.range().map(transform.invertX, transform),
domain = range.map(x.invert, x);
return x.copy().domain(domain);
}
比例尺 x 必須使用 interpolateNumber;請勿使用 continuous.rangeRound,因為這會降低 continuous.invert 的準確度,並可能導致不準確的重新調整比例網域。此方法不會修改輸入比例尺 x;因此 x 代表未轉換的比例尺,而傳回的比例尺代表其轉換的檢視。
transform.rescaleY(y)
原始碼 · 傳回一個 拷貝 的 連續比例尺 y,其 網域 已轉換。這實作方式是先在比例尺的 範圍 上套用 反向 y 轉換,然後套用 反向比例尺 來計算對應的網域
function rescaleY(y) {
var range = y.range().map(transform.invertY, transform),
domain = range.map(y.invert, y);
return y.copy().domain(domain);
}
比例尺 y 必須使用 interpolateNumber;請勿使用 continuous.rangeRound,因為這會降低 continuous.invert 的準確度,並可能導致不準確的重新調整比例網域。此方法不會修改輸入比例尺 y;因此 y 代表未轉換的比例尺,而傳回的比例尺代表其轉換的檢視。
transform.toString()
原始碼 · 傳回一個字串,代表對應於此轉換的 SVG 轉換。實作如下
function toString() {
return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
}