d3-zoom
拡大縮小を計算するd3.jsのmodule
https://github.com/d3/d3-zoom
Reference
https://wizardace.com/d3-zoom/
d3-zoom#63edb6a11280f00000bc0453のサンプルを改造したものをrund3で動かしてみる
軸はScale (d3)でx,y成分を直接書き換えているが、円群はSVG transformで大きさを変えている
円群もx,y,r成分をscaleで直接書き換えて大きさを変えることもできる
軸は拡大率やスクロール位置によって描画すべき目盛りが変わるので、transformではzoomできない
d3-axisのサンプルも混じっているのわかりずらいな
軸ありのサンプルは別ページで動かしたい
code: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を再生成する必要がなくなる
code: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を作り直すようにしたらズームされるようになった
code:diff
-setXAxis((xA) => xA.scale(e.transform.rescaleX(x)));
+setXAxis(() => makeXAxis().scale(e.transform.rescaleX(x)));
https://gyazo.com/c36ced8ff43931461374f19c6c247e72
ZoomTransform.rescaleX()で新しいZoomScaleを作る
code: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);
サンプルその2
run
<svg>にSVG transformを設定したら謎の震えが発生した
<g>に変えたら直った
code: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する
https://www.d3indepth.com/zoom-and-pan/
X軸方向のみ移動を許可する
run
❌ZoomBehavior.translateExtent()でY軸方向に動けなくすればいい
拡大縮小の原点が常に<svg>の中心になってしまう
ZoomTransform.toString()がtransform: translate()してからtransform: scale()しているのが原因かも?
逆にすれば直る?
直らなかった……
https://stackoverflow.com/questions/42907047/d3-v4-ajust-zoom-to-center
https://stackoverflow.com/questions/20674537/d3-how-to-get-correct-scale-and-translate-origin-after-manual-zoom-and-pan-to
transform-originだけでは拡大縮小中心を設定できない?
なわけないだろうから、自分の設定がどこかおかしいのだろう
code: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);
API reference
code: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