D3.zoom以编程方式缩放后使用鼠标滚轮时会跳转

时间:2019-04-02 14:46:19

标签: d3.js svg 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 + ")");
}

这就是我“激活”缩放和平移功能的方式。

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)
     ...

1 个答案:

答案 0 :(得分:1)

背景

您正在操纵单击功能中应用于元素的变换,但没有更新缩放状态以反映这一点。 d3.zoom根本不跟踪元素的变换。因此,当您独立于d3.zoom修改元素的变换属性时,d3.zoom不再“知道”该元素的变换是什么-它保持不变。 D3.zoom可以在内部且独立于任何元素的transform属性来跟踪缩放状态。

d3.zoom不能跟踪元素的变换似乎很奇怪,但是有充分的理由。 d3.zoom并不总是用于操纵元素的变换,它可能会在元素的变换保持不变的情况下改变元素的宽度或比例等内容。这是我的bl.ock,其中d3.zoom仅在画布上操纵圆的半径。

问题

由于您没有在click事件中更新缩放状态,因此d3.zoom会在应用新的缩放事件时选择它最后离开的位置,这说明了您的症状:“似乎我的缩放级别正在恢复在鼠标单击之前。”

解决方案

那么,您需要做什么?您需要将缩放变换传递给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来应用缩放

  

将所选元素的当前缩放变换设置为   指定的变换,立即发出开始,缩放和结束   事件。如果选择是过渡,则定义到之间的“缩放”补间   使用d3.interpolateZoom指定的变换,发出开始事件   过渡开始时,针对   转换,然后是转换结束时的结束事件(或   中断)。 (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>