generated at
d3-zoom
拡大縮小を計算するd3.jsのmodule

Reference

d3-zoom#63edb6a11280f00000bc0453のサンプルを改造したものをrund3で動かしてみる
軸はScale (d3)でx,y成分を直接書き換えているが、円群はSVG transformで大きさを変えている
円群もx,y,r成分をscaleで直接書き換えて大きさを変えることもできる
軸は拡大率やスクロール位置によって描画すべき目盛りが変わるので、transformではzoomできない
d3-axisのサンプルも混じっているのわかりずらいな
軸ありのサンプルは別ページで動かしたい

sample.js
const { html, render, useState, useCallback, useMemo, useEffect } = htmPreact; const { scaleLinear, axisBottom, axisRight, zoom, zoomIdentity, select } = d3; const [width, height] = [800, 600]; const dataset = []; for(let i = 0; i < 300; i++) { const r = 0.5 * Math.min(height, width) * Math.random(); const t = 2 * Math.PI * Math.random(); dataset.push([ width / 2 + r * Math.cos(t), // x height / 2 + r * Math.sin(t) // y ]); } /** x成分をグラフ上の座標成分に写像する */ const x = scaleLinear().domain([0, width]).range([0, width]); /** y成分をグラフ上の座標成分に写像する */ const y = scaleLinear().domain([0, height]).range([0, height]); /** x軸のrenderer */ const xAxis = axisBottom(x) .ticks(width / height * 10) .tickSize(height) .tickPadding(8 - height); /** y軸のrenderer */ const yAxis = axisRight(y) .ticks(10) .tickSize(width) .tickPadding(8 - width) const App = () => { const [gX, setGX] = useState(null); const [xScale, setXScale] = useState(() => x); useEffect(() => xAxis.scale(xScale)(select(gX)), [gX, xScale]); const [gY, setGY] = useState(null); const [yScale, setYScale] = useState(() => y); useEffect(() => yAxis.scale(yScale)(select(gY)), [gY, yScale]); const [transform, setTransform] = useState(""); const zoomer = useMemo(() => zoom() .scaleExtent([0.1, 40]) .translateExtent([ [-100, -100], [width + 90, height + 100] ]) .on("zoom", (e) => {

2023-04-08
20:52:25 zoom eventが走る度に、軸が再描画されるように変えた
Axis を再生成する必要がなくなる
2(diff)
-setXAxis(() => makeXAxis().scale(e.transform.rescaleX(x))); +setXScale(() => e.transform.rescaleX(x)); +useEffect(() => select(g).call(xAxis.scale(xScale), [xScale]);
✅2023-02-16 スケール変更が軸に反映されていない
setTransform(e.transform); は機能している
xA.scale() xA をmutableに変更しているせいか?
14:48:50 ビンゴ。毎回 Axis を作り直すようにしたらズームされるようになった
diff
-setXAxis((xA) => xA.scale(e.transform.rescaleX(x))); +setXAxis(() => makeXAxis().scale(e.transform.rescaleX(x)));
sample.js
setTransform(e.transform); setXScale(() => e.transform.rescaleX(x)); setYScale(() => e.transform.rescaleY(y)); }), []); const [svgEl, setSvgEl] = useState(null); useEffect(() => zoomer(select(svgEl)), [svgEl]); const reset = useCallback(() => { select(svgEl).transition() .duration(750) .call(zoomer.transform, zoomIdentity); }, [svgEl]); return html`<style> svg { max-width: 90vw; max-height: 90vh; } </style> <div> <button onClick=${reset}>reset</button> <svg width=${width} height=${height} ref=${setSvgEl}> <g ref=${setGX} /> <g ref=${setGY} /> <g transform=${transform} fill="steelblue" stroke="black"> ${dataset.map( ([cx, cy]) => html`<circle class="view" cx=${cx} cy=${cy} r="5" />` )} </g> </svg> </div>`; }; render(html`<${App} />`, document.body);

<svg> SVG transformを設定したら謎の震えが発生した
<g> に変えたら直った
sample2.js
const { render, html, useState } = htmPreact; const { select, zoom } = d3; render(html`<${() => { const [t, setT] = useState(""); return html`<svg width="460" height="460" style="border: 1px solid black" ref=${(el) => { zoom().on("zoom", (e) => { setT(e.transform); })(select(el)); }}> <g transform=${t}><circle cx="230" cy="230" r="40" fill="#68b2a1" /></g> </svg>`; }} />`, document.body);
拡大範囲を制限する
ボタンでzoomをcontrolする
X軸方向のみ移動を許可する
ZoomBehavior.translateExtent()でY軸方向に動けなくすればいい
拡大縮小の原点が常に <svg> の中心になってしまう
ZoomTransform.toString()transform: translate()してからtransform: scale()しているのが原因かも?
逆にすれば直る?
直らなかった……
transform-originだけでは拡大縮小中心を設定できない?
なわけないだろうから、自分の設定がどこかおかしいのだろう
sample-x.js
const { render, html, useState, useMemo } = htmPreact; const { select, zoom } = d3; render(html`<${() => { const [t, setT] = useState({ x: 0, y: 0, k: 1 }); const transform = useMemo( () => `scale(${t.k})`, [t] ); return html` <div> ${`transform=${transform}`} </div> <svg width="460" height="460" style="border: 1px solid black" ref=${(el) => { zoom() .translateExtent([[-Infinity, 0], [Infinity, 0]]) .on("zoom", (e) => { setT(e.transform); })(select(el)); }}> <g transform=${`translate(${t.x}, 0)`}><rect x="190" y="210" width="80" height="40" fill="#68b2a1" style="transform-origin: 50% 50% 0" transform=${transform} /></g> <!--比較用--> <g><rect x="190" y="210" width="80" height="40" fill="#68b2a1" style="transform-origin: 50% 50% 0;opacity: 0.5" /></g> </svg>`; }} />`, document.body);

mod.ts
export * from "https://cdn.skypack.dev/d3-zoom@v3.0.0?dts";

#2023-04-08 21:00:28
#2023-03-15 22:33:11
#2023-02-17 08:20:09
#2023-02-16 14:51:07