D3.zoom 以编程方式缩放后使用鼠标滚轮时跳转
D3.zoom jumps when using mouse wheel after programmatically zoom
当我通过单击鼠标缩放到特定位置然后尝试平移时,或者当我使用鼠标滚轮时,缩放行为会跳跃。似乎我的缩放级别正在恢复,就像单击鼠标之前一样。
这是我的事件处理程序:
function click(d) {
var x, y, k;
if (d && centered !== d) {
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
svgContainer.transition()
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")");
}
这就是我 "activate" 缩放和平移功能的方式。
var svgContainer = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().on("zoom", function () {
svgContainer.attr("transform", d3.event.transform)
}))
.append("g")
...
svgContainer.selectAll(null)
.data(feat.features)
.enter()
.append("path")
.on("click", click)
...
背景
您在单击函数中操纵应用于您的元素的变换,但未更新缩放状态以反映这一点。 d3.zoom 根本不跟踪元素的变换。因此,当您独立于 d3.zoom 修改元素的变换属性时,d3.zoom 不再 "knows" 该元素的变换是什么 - 它保持不变。 D3.zoom 确实跟踪缩放状态 - 在内部独立于任何元素的变换属性。
d3.zoom 不跟踪元素的变换可能看起来很奇怪,但这是有充分理由的。 d3.zoom 并不总是用于操纵元素的变换,它可能会改变元素宽度或比例之类的东西,而该元素的变换保持不变。这是我的 bl.ock,其中 d3.zoom 仅操纵 canvas.
上的圆半径
问题
由于您没有在点击事件中更新缩放状态,d3.zoom 会在应用新的缩放事件时从上次离开的位置拾取,这解释了您的症状:"It seems that my zoom level being restored like it was before the mouse click."
解决方案
那么,你需要做什么?您需要将缩放转换传递给 d3.zoom 并触发缩放事件。这样 d3.zoom 就会知道当前的缩放状态。幸运的是,有一种方法可以做到这一点。我们采用 d3.zoomIdentity (k=1,x=0,y=0) 并根据需要进行平移和缩放,我们可以平移、缩放然后再次平移,就像您所做的那样:
// Create a zoom transform from d3.zoomIdentity
var transform = d3.zoomIdentity
.translate(250,150)
.scale(k)
.translate(-x,-y);
然后我们通过调用 zoom.transform 来应用缩放,根据文档:
sets the current zoom transform of the selected elements to the
specified transform, instantaneously emitting start, zoom and end
events. If selection is a transition, defines a “zoom” tween to the
specified transform using d3.interpolateZoom, emitting a start event
when the transition starts, zoom events for each tick of the
transition, and then an end event when the transition ends (or is
interrupted). (link)
我们可以通过以下方式调用 zoom.transform:
// Apply the zoom and trigger a zoom event with a provided zoom transform:
svg.call(zoom.transform, transform);
因此,如果这与您所拥有的类似:
var zoom = d3.zoom()
.scaleExtent([1,8])
.translateExtent([[0,0],[500,300]])
.on("zoom",zoomed);
var svg = d3.select("div")
.append("svg")
.attr("width",500)
.attr("height",300)
.call(zoom);
var g = svg.append("g");
var rects = g.selectAll(null)
.data(d3.range(750))
.enter()
.append("rect")
.attr("width",17)
.attr("height",17)
.attr("fill","#eee")
.attr("y", function(d) { return Math.floor(d/50) * 20; })
.attr("x", function(d) { return d%50 * 20; })
.on("click", click);
function zoomed() {
g.attr("transform", d3.event.transform);
}
function click(d) {
rects.attr("fill","#eee");
var clicked = d3.select(this);
clicked.attr("fill","orange");
var x = +clicked.attr("x")+10;
var y = +clicked.attr("y")+10;
var k = 5;
var transform = "translate(" + 250 + "," + 150 + ")scale(" + k + ")translate(" + -x + "," + -y + ")";
g.transition()
.attr("transform",transform)
.duration(1000);
}
rect {
stroke-width: 1px;
stroke: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
这可能与您的解决方案类似:
var zoom = d3.zoom()
.scaleExtent([1,8])
.translateExtent([[0,0],[500,300]])
.on("zoom",zoomed);
var svg = d3.select("div")
.append("svg")
.attr("width",500)
.attr("height",300)
.call(zoom);
var g = svg.append("g");
var rects = g.selectAll(null)
.data(d3.range(750))
.enter()
.append("rect")
.attr("width",17)
.attr("height",17)
.attr("fill","#eee")
.attr("y", function(d) { return Math.floor(d/50) * 20; })
.attr("x", function(d) { return d%50 * 20; })
.on("click", click);
function zoomed() {
g.attr("transform", d3.event.transform);
}
function click(d) {
rects.attr("fill","#eee");
var clicked = d3.select(this);
clicked.attr("fill","orange");
var x = +clicked.attr("x")+10;
var y = +clicked.attr("y")+10;
var k = 5;
// Create a zoom transform from d3.zoomIdentity
var transform = d3.zoomIdentity
.translate(250,150)
.scale(k)
.translate(-x,-y);
// Apply the zoom and trigger a zoom event:
svg.call(zoom.transform, transform);
}
rect {
stroke-width: 1px;
stroke: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
当我通过单击鼠标缩放到特定位置然后尝试平移时,或者当我使用鼠标滚轮时,缩放行为会跳跃。似乎我的缩放级别正在恢复,就像单击鼠标之前一样。
这是我的事件处理程序:
function click(d) {
var x, y, k;
if (d && centered !== d) {
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
svgContainer.transition()
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")");
}
这就是我 "activate" 缩放和平移功能的方式。
var svgContainer = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.zoom().on("zoom", function () {
svgContainer.attr("transform", d3.event.transform)
}))
.append("g")
...
svgContainer.selectAll(null)
.data(feat.features)
.enter()
.append("path")
.on("click", click)
...
背景
您在单击函数中操纵应用于您的元素的变换,但未更新缩放状态以反映这一点。 d3.zoom 根本不跟踪元素的变换。因此,当您独立于 d3.zoom 修改元素的变换属性时,d3.zoom 不再 "knows" 该元素的变换是什么 - 它保持不变。 D3.zoom 确实跟踪缩放状态 - 在内部独立于任何元素的变换属性。
d3.zoom 不跟踪元素的变换可能看起来很奇怪,但这是有充分理由的。 d3.zoom 并不总是用于操纵元素的变换,它可能会改变元素宽度或比例之类的东西,而该元素的变换保持不变。这是我的 bl.ock,其中 d3.zoom 仅操纵 canvas.
上的圆半径问题
由于您没有在点击事件中更新缩放状态,d3.zoom 会在应用新的缩放事件时从上次离开的位置拾取,这解释了您的症状:"It seems that my zoom level being restored like it was before the mouse click."
解决方案
那么,你需要做什么?您需要将缩放转换传递给 d3.zoom 并触发缩放事件。这样 d3.zoom 就会知道当前的缩放状态。幸运的是,有一种方法可以做到这一点。我们采用 d3.zoomIdentity (k=1,x=0,y=0) 并根据需要进行平移和缩放,我们可以平移、缩放然后再次平移,就像您所做的那样:
// Create a zoom transform from d3.zoomIdentity
var transform = d3.zoomIdentity
.translate(250,150)
.scale(k)
.translate(-x,-y);
然后我们通过调用 zoom.transform 来应用缩放,根据文档:
sets the current zoom transform of the selected elements to the specified transform, instantaneously emitting start, zoom and end events. If selection is a transition, defines a “zoom” tween to the specified transform using d3.interpolateZoom, emitting a start event when the transition starts, zoom events for each tick of the transition, and then an end event when the transition ends (or is interrupted). (link)
我们可以通过以下方式调用 zoom.transform:
// Apply the zoom and trigger a zoom event with a provided zoom transform:
svg.call(zoom.transform, transform);
因此,如果这与您所拥有的类似:
var zoom = d3.zoom()
.scaleExtent([1,8])
.translateExtent([[0,0],[500,300]])
.on("zoom",zoomed);
var svg = d3.select("div")
.append("svg")
.attr("width",500)
.attr("height",300)
.call(zoom);
var g = svg.append("g");
var rects = g.selectAll(null)
.data(d3.range(750))
.enter()
.append("rect")
.attr("width",17)
.attr("height",17)
.attr("fill","#eee")
.attr("y", function(d) { return Math.floor(d/50) * 20; })
.attr("x", function(d) { return d%50 * 20; })
.on("click", click);
function zoomed() {
g.attr("transform", d3.event.transform);
}
function click(d) {
rects.attr("fill","#eee");
var clicked = d3.select(this);
clicked.attr("fill","orange");
var x = +clicked.attr("x")+10;
var y = +clicked.attr("y")+10;
var k = 5;
var transform = "translate(" + 250 + "," + 150 + ")scale(" + k + ")translate(" + -x + "," + -y + ")";
g.transition()
.attr("transform",transform)
.duration(1000);
}
rect {
stroke-width: 1px;
stroke: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
这可能与您的解决方案类似:
var zoom = d3.zoom()
.scaleExtent([1,8])
.translateExtent([[0,0],[500,300]])
.on("zoom",zoomed);
var svg = d3.select("div")
.append("svg")
.attr("width",500)
.attr("height",300)
.call(zoom);
var g = svg.append("g");
var rects = g.selectAll(null)
.data(d3.range(750))
.enter()
.append("rect")
.attr("width",17)
.attr("height",17)
.attr("fill","#eee")
.attr("y", function(d) { return Math.floor(d/50) * 20; })
.attr("x", function(d) { return d%50 * 20; })
.on("click", click);
function zoomed() {
g.attr("transform", d3.event.transform);
}
function click(d) {
rects.attr("fill","#eee");
var clicked = d3.select(this);
clicked.attr("fill","orange");
var x = +clicked.attr("x")+10;
var y = +clicked.attr("y")+10;
var k = 5;
// Create a zoom transform from d3.zoomIdentity
var transform = d3.zoomIdentity
.translate(250,150)
.scale(k)
.translate(-x,-y);
// Apply the zoom and trigger a zoom event:
svg.call(zoom.transform, transform);
}
rect {
stroke-width: 1px;
stroke: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>