d3-zoom
Reference
円群もx,y,r成分をscaleで直接書き換えて大きさを変えることもできる
軸は拡大率やスクロール位置によって描画すべき目盛りが変わるので、transformではzoomできない
軸ありのサンプルは別ページで動かしたい
code:sample.js
const { html, render, useState, useCallback, useMemo, useEffect } = htmPreact;
const { scaleLinear, axisBottom, axisRight, zoom, zoomIdentity, select } = d3;
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成分をグラフ上の座標成分に写像する */
/** y成分をグラフ上の座標成分に写像する */
/** 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 = () => {
useEffect(() => xAxis.scale(xScale)(select(gX)), gX, xScale); useEffect(() => yAxis.scale(yScale)(select(gY)), gY, yScale); const zoomer = useMemo(() => zoom()
.translateExtent([
])
.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
code:sample.js
setTransform(e.transform);
setXScale(() => e.transform.rescaleX(x));
setYScale(() => e.transform.rescaleY(y));
}), []);
useEffect(() => zoomer(select(svgEl)), svgEl); const reset = useCallback(() => {
select(svgEl).transition()
.duration(750)
.call(zoomer.transform, zoomIdentity);
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);
<g>に変えたら直った
code:sample2.js
const { render, html, useState } = htmPreact;
const { select, zoom } = d3;
render(html`<${() => {
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軸方向のみ移動を許可する
拡大縮小の原点が常に<svg>の中心になってしまう
逆にすれば直る?
直らなかった……
なわけないだろうから、自分の設定がどこかおかしいのだろう
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}),
);
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);
code:mod.ts