d3-zoom
Reference
円群もx,y,r成分をscaleで直接書き換えて大きさを変えることもできる
軸は拡大率やスクロール位置によって描画すべき目盛りが変わるので、transformではzoomできない
軸ありのサンプルは別ページで動かしたい
sample.jsconst { 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);
<g>
に変えたら直った
sample2.jsconst { 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軸方向のみ移動を許可する
拡大縮小の原点が常に <svg>
の中心になってしまう
逆にすれば直る?
直らなかった……
なわけないだろうから、自分の設定がどこかおかしいのだろう
sample-x.jsconst { 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.tsexport * from "https://cdn.skypack.dev/d3-zoom@v3.0.0?dts";