使用 zoom.center 转换 d3v6 Observable 示例
Convert d3v6 Observable example using zoom.center
我有一个项目正在尝试缩放到特定点。在 d3v3 中,我可以用这个函数来做到这一点
function zoomTo(place, pscale) {
projection = d3.geo.mercator()
.scale((1 << pscale) / 2 / Math.PI)
.translate([width / 2, height / 2])
center = projection(place);
return zoom
.scale(projection.scale() * 2 * Math.PI)
.translate([width - center[0], height - center[1]]);
}
不过缩放已经改变,这在 d3v6 中不再有效。
我在 Observable 上找到了一个示例,它使用 d3v6 完全符合我的要求:
https://observablehq.com/d/9035ed7049aaa8c6
但是,我不确定如何将它翻译成原版 javascript。
以下是我的尝试。我收到此错误:
d3.zoom(...).scaleExtent(...).on(...).center is not a function
虽然它在 Observable 中运行良好,但显然我遗漏了一些东西。任何帮助将不胜感激!
(另外,我不需要 Observable 示例顶部的按钮,所以我将它们注释掉了。)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zoom to Point</title>
<script src="https://files-7y4qaclbi.vercel.app/d3-zoom.js"></script>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<script>
// const form = html`<form>
// <button name="in" type="button">Zoom In</button>
// <button name="out" type="button">Zoom Out</button>
// <button name="random" type="button">Random</button>
// <button name="reset" type="button">Reset</button>
// </form>`;
// form.in.onclick = () => chart.zoomIn();
// form.out.onclick = () => chart.zoomOut();
// form.random.onclick = () => chart.zoomRandom();
// form.reset.onclick = () => chart.zoomReset();
// return form;
const radius = 6
const step = radius * 5
const width = 500
const height = 500
const theta = Math.PI * (3 - Math.sqrt(5))
const data = Array.from({ length: 1000 }, (_, i) => {
const radius = step * Math.sqrt(i += 0.5), a = theta * i;
return [
width / 2 + radius * Math.cos(a),
height / 2 + radius * Math.sin(a)
];
})
const quadtree = d3.quadtree()
.addAll(data)
// let mutable debug = undefined;
let focussed;
function findCloser(that, coords) {
const p = d3.zoomTransform(that).invert(coords);
focussed = quadtree.find(p[0], p[1]);
return d3.zoomTransform(that).apply(focussed);
}
const zoom = d3
.zoom()
.scaleExtent([1, 40])
.on("zoom", zoomed)
.center(function (event, d) {
// mutable debug = { event, d, r: Math.random() };
let coords = d3.pointer(event, this);
focussed = null;
if (event.deltaY < 0) {
coords = findCloser(this, coords);
}
g.selectAll("circle").attr("r", p =>
p == focussed ? radius + 3 : radius
);
return coords;
});
const svg = d3.select('body').append("svg")
.attr('width', width)
.attr('height', height)
.on("click", reset);
const g = svg.append("g");
g.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", ([x]) => x)
.attr("cy", ([, y]) => y)
.attr("r", radius)
.attr("fill", (d, i) => d3.interpolateRainbow(i / 360))
.on("click", clicked);
svg.datum("I'm your worst datum").call(zoom); //.on("mousemove", mousemove);
function random() {
const [x, y] = data[Math.floor(Math.random() * data.length)];
svg
.transition()
.duration(2500)
.call(
zoom.transform,
d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(40)
.translate(-x, -y)
);
}
function reset() {
svg
.transition()
.duration(750)
.call(
zoom.transform,
d3.zoomIdentity,
d3.zoomTransform(svg.node()).invert([width / 2, height / 2])
);
}
function clicked(event, [x, y]) {
event.stopPropagation();
svg
.transition()
.duration(750)
.call(
zoom.transform,
d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(40)
.translate(-x, -y),
d3.pointer(event, svg.node())
);
}
function zoomed(event) {
g.attr("transform", event.transform);
}
// return Object.assign(svg.node(), {
// zoomIn: () =>
// svg
// .transition()
// .call(zoom.scaleBy, 2, findCloser(svg.node(), [width / 2, height / 2])),
// zoomOut: () => svg.transition().call(zoom.scaleBy, 0.5),
// zoomRandom: random,
// zoomReset: reset
// });
</script>
</body>
</html>
Plunker 中也有,如果有帮助的话:https://plnkr.co/edit/JaiKhTgEXzIFWiar?open=lib%2Fscript.js
在任何 d3-zoom 版本中都没有 zoom.center
,从第一个版本到当前版本(撰写本文时为 v3.0.0)。
那个 zoom.center
方法是由那个 Observable 的作者创建的,它积极地为 D3 做出贡献(see here). You can see the proposal here: https://github.com/d3/d3-zoom/pull/212
但是,如果您检查 the code the author is using,您会发现添加自己的 center
方法并不复杂。您只需要:
zoom.center = function(_) {
return arguments.length ? (center = typeof _ === "function" ? _ : constant([+_[0], +_[1]]), zoom) : center;
};
...和...
function defaultCenter(event) {
return d3Selection.pointer(event, this);
}
有几个 center.apply(this, arguments)
可以通过查看该文件找到。
我有一个项目正在尝试缩放到特定点。在 d3v3 中,我可以用这个函数来做到这一点
function zoomTo(place, pscale) {
projection = d3.geo.mercator()
.scale((1 << pscale) / 2 / Math.PI)
.translate([width / 2, height / 2])
center = projection(place);
return zoom
.scale(projection.scale() * 2 * Math.PI)
.translate([width - center[0], height - center[1]]);
}
不过缩放已经改变,这在 d3v6 中不再有效。
我在 Observable 上找到了一个示例,它使用 d3v6 完全符合我的要求: https://observablehq.com/d/9035ed7049aaa8c6
但是,我不确定如何将它翻译成原版 javascript。 以下是我的尝试。我收到此错误:
d3.zoom(...).scaleExtent(...).on(...).center is not a function
虽然它在 Observable 中运行良好,但显然我遗漏了一些东西。任何帮助将不胜感激! (另外,我不需要 Observable 示例顶部的按钮,所以我将它们注释掉了。)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zoom to Point</title>
<script src="https://files-7y4qaclbi.vercel.app/d3-zoom.js"></script>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<script>
// const form = html`<form>
// <button name="in" type="button">Zoom In</button>
// <button name="out" type="button">Zoom Out</button>
// <button name="random" type="button">Random</button>
// <button name="reset" type="button">Reset</button>
// </form>`;
// form.in.onclick = () => chart.zoomIn();
// form.out.onclick = () => chart.zoomOut();
// form.random.onclick = () => chart.zoomRandom();
// form.reset.onclick = () => chart.zoomReset();
// return form;
const radius = 6
const step = radius * 5
const width = 500
const height = 500
const theta = Math.PI * (3 - Math.sqrt(5))
const data = Array.from({ length: 1000 }, (_, i) => {
const radius = step * Math.sqrt(i += 0.5), a = theta * i;
return [
width / 2 + radius * Math.cos(a),
height / 2 + radius * Math.sin(a)
];
})
const quadtree = d3.quadtree()
.addAll(data)
// let mutable debug = undefined;
let focussed;
function findCloser(that, coords) {
const p = d3.zoomTransform(that).invert(coords);
focussed = quadtree.find(p[0], p[1]);
return d3.zoomTransform(that).apply(focussed);
}
const zoom = d3
.zoom()
.scaleExtent([1, 40])
.on("zoom", zoomed)
.center(function (event, d) {
// mutable debug = { event, d, r: Math.random() };
let coords = d3.pointer(event, this);
focussed = null;
if (event.deltaY < 0) {
coords = findCloser(this, coords);
}
g.selectAll("circle").attr("r", p =>
p == focussed ? radius + 3 : radius
);
return coords;
});
const svg = d3.select('body').append("svg")
.attr('width', width)
.attr('height', height)
.on("click", reset);
const g = svg.append("g");
g.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", ([x]) => x)
.attr("cy", ([, y]) => y)
.attr("r", radius)
.attr("fill", (d, i) => d3.interpolateRainbow(i / 360))
.on("click", clicked);
svg.datum("I'm your worst datum").call(zoom); //.on("mousemove", mousemove);
function random() {
const [x, y] = data[Math.floor(Math.random() * data.length)];
svg
.transition()
.duration(2500)
.call(
zoom.transform,
d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(40)
.translate(-x, -y)
);
}
function reset() {
svg
.transition()
.duration(750)
.call(
zoom.transform,
d3.zoomIdentity,
d3.zoomTransform(svg.node()).invert([width / 2, height / 2])
);
}
function clicked(event, [x, y]) {
event.stopPropagation();
svg
.transition()
.duration(750)
.call(
zoom.transform,
d3.zoomIdentity
.translate(width / 2, height / 2)
.scale(40)
.translate(-x, -y),
d3.pointer(event, svg.node())
);
}
function zoomed(event) {
g.attr("transform", event.transform);
}
// return Object.assign(svg.node(), {
// zoomIn: () =>
// svg
// .transition()
// .call(zoom.scaleBy, 2, findCloser(svg.node(), [width / 2, height / 2])),
// zoomOut: () => svg.transition().call(zoom.scaleBy, 0.5),
// zoomRandom: random,
// zoomReset: reset
// });
</script>
</body>
</html>
Plunker 中也有,如果有帮助的话:https://plnkr.co/edit/JaiKhTgEXzIFWiar?open=lib%2Fscript.js
在任何 d3-zoom 版本中都没有 zoom.center
,从第一个版本到当前版本(撰写本文时为 v3.0.0)。
那个 zoom.center
方法是由那个 Observable 的作者创建的,它积极地为 D3 做出贡献(see here). You can see the proposal here: https://github.com/d3/d3-zoom/pull/212
但是,如果您检查 the code the author is using,您会发现添加自己的 center
方法并不复杂。您只需要:
zoom.center = function(_) {
return arguments.length ? (center = typeof _ === "function" ? _ : constant([+_[0], +_[1]]), zoom) : center;
};
...和...
function defaultCenter(event) {
return d3Selection.pointer(event, this);
}
有几个 center.apply(this, arguments)
可以通过查看该文件找到。