以编程方式缩放后使用鼠标滚轮时 D3.zoom 跳转 [英] D3.zoom jumps when using mouse wheel after programmatically zoom

查看:21
本文介绍了以编程方式缩放后使用鼠标滚轮时 D3.zoom 跳转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我通过单击鼠标缩放到特定位置然后尝试平移或使用鼠标滚轮时,缩放行为会跳跃.似乎我的缩放级别正在恢复,就像单击鼠标之前一样.

这是我的事件处理程序:

function click(d) {变量 x, y, k;如果 (d && 居中 !== d) {var centroid = path.centroid(d);x = 质心[0];y = 质心 [1];k = 4;居中 = d;} 别的 {x = 宽度/2;y = 高度/2;k = 1;居中 = 空;}svgContainer.transition().attr("transform", "translate(" + width/2 + "," + height/2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")");}

这就是我激活"缩放和平移功能的方式.

var svgContainer = d3.select("body").append("svg").attr("宽度", 宽度).attr("高度", 高度).call(d3.zoom().on("zoom", function () {svgContainer.attr("转换", d3.event.transform)})).append("g")...svgContainer.selectAll(null).data(feat.features).进入().append("路径").on("点击",点击)...

解决方案

背景

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

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

问题

由于您没有在单击事件中更新缩放状态,因此 d3.zoom 在应用新的缩放事件时会选择上次离开的位置,这解释了您的症状:似乎我的缩放级别正在恢复在鼠标点击之前."

解决方案

那么,你需要做什么?您需要将缩放变换传递给 d3.zoom 并触发缩放事件.这样 d3.zoom 就被告知当前的缩放状态.幸运的是,有一种方法可以做到这一点.我们采用 d3.zoomIdentity (k=1,x=0,y=0) 并根据需要进行平移和缩放,我们可以像您一样进行平移、缩放,然后再次平移:

//从 d3.zoomIdentity 创建缩放变换var 变换 = d3.zoomIdentity.translate(250,150).scale(k).translate(-x,-y);

然后我们根据文档调用 zoom.transform 来应用缩放:

<块引用>

将所选元素的当前缩放变换设置为指定变换,即刻发出开始、缩放和结束事件.如果选择是一个过渡,则定义一个缩放"补间到使用 d3.interpolateZoom 指定变换,发出开始事件当过渡开始时,每个刻度的缩放事件过渡,然后是过渡结束时的结束事件(或中断).(链接)

我们可以调用zoom.transform:

//应用缩放并使用提供的缩放变换触发缩放事件:svg.call(zoom.transform, 变换);

因此,如果这与您所拥有的相似:

var zoom = d3.zoom().scaleExtent([1,8]).translateExtent([[0,0],[500,300]]).on("zoom",zoomed);var svg = d3.select("div").append("svg").attr("宽度",500).attr("高度",300).call(缩放);var g = svg.append("g");var rects = g.selectAll(null).data(d3.range(750)).进入().append("rect").attr("宽度",17).attr("高度",17).attr("填充","#eee").attr("y", function(d) { return Math.floor(d/50) * 20; }).attr("x", function(d) { return d%50 * 20; }).on("点击", 点击);函数缩放(){g.attr("transform", d3.event.transform);}功能点击(d){rects.attr("fill","#eee");var clicked = d3.select(this);clicked.attr("填充","橙色");var x = +clicked.attr("x")+10;var y = +clicked.attr("y")+10;无功k = 5;var transform = "translate(" + 250 + "," + 150 + ")scale(" + k + ")translate(" + -x + "," + -y + ")";g.transition().attr(转换",转换).duration(1000);}

rect {笔画宽度:1px;中风:#ccc;}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

这可能与您的解决方案类似:

var zoom = d3.zoom().scaleExtent([1,8]).translateExtent([[0,0],[500,300]]).on("zoom",zoomed);var svg = d3.select("div").append("svg").attr("宽度",500).attr("高度",300).call(缩放);var g = svg.append("g");var rects = g.selectAll(null).data(d3.range(750)).进入().append("rect").attr("宽度",17).attr("高度",17).attr("填充","#eee").attr("y", function(d) { return Math.floor(d/50) * 20; }).attr("x", function(d) { return d%50 * 20; }).on("点击", 点击);函数缩放(){g.attr("transform", d3.event.transform);}功能点击(d){rects.attr("fill","#eee");var clicked = d3.select(this);clicked.attr("填充","橙色");var x = +clicked.attr("x")+10;var y = +clicked.attr("y")+10;无功k = 5;//从 d3.zoomIdentity 创建缩放变换var 变换 = d3.zoomIdentity.translate(250,150).scale(k).translate(-x,-y);//应用缩放并触发缩放事件:svg.call(zoom.transform, 变换);}

rect {笔画宽度:1px;中风:#ccc;}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

When I zoom to a specific location on a mouse click and then try to pan or when I'm using the mouse wheel, the zoom behavior jumps. It seems that my zoom level is being restored like it was before the mouse click.

Here is my event handler:

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 + ")");
}

And this is how I "activate" my zoom and pan functionalities.

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

解决方案

Background

You are manipulating the transform applied to your elements in the click function but not updating the zoom state to reflect this. d3.zoom does not track an element's transform at all. So when you modify the transform attribute of an element independently of d3.zoom, d3.zoom no longer "knows" what the transform is of that element - it remains unchanged. D3.zoom does track zoom state - internally and independently of any element's transform attribute.

It might seem odd that d3.zoom doesn't track an element's transform, but there is good reason. d3.zoom isn't always used to manipulate the transform of an element, it may alter something like element width or a scale while that element's transform remains unchanged. Here's a bl.ock of mine where d3.zoom here manipulates only the radius of circles on canvas.

Problem

As you don't update the zoom state in your click event, d3.zoom picks up where it was last left when applying a new zoom event, which explains your symptom: "It seems that my zoom level being restored like it was before the mouse click."

Solution

So, what do you need to do? You need to pass your zoom transform to d3.zoom and trigger a zoom event. This way d3.zoom is apprised of the current zoom state. Luckily there is a method for this. We take a d3.zoomIdentity (k=1,x=0,y=0) and translate and scale as appropriate, we can translate, scale, and then translate again as you have done too:

   // Create a zoom transform from d3.zoomIdentity
   var transform = d3.zoomIdentity
    .translate(250,150)
    .scale(k)
    .translate(-x,-y);

And then we apply the zoom by calling zoom.transform which according to the docs:

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)

We can call zoom.transform with:

   // Apply the zoom and trigger a zoom event with a provided zoom transform:
   svg.call(zoom.transform, transform);

So if this is analagous to what you have:

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>

This can be analagous to your solution:

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>

这篇关于以编程方式缩放后使用鼠标滚轮时 D3.zoom 跳转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆