javascript - D3.js v4 元素在拖动后会回到初始位置

标签 javascript d3.js

我有一个 SVG 有一些组和拖动行为。它工作得很好,但是当我拖动元素两次时,它们会从“初始”位置重新开始。我需要新鲜的眼睛来解决这个问题!

示例 : jsfiddle

还有一个片段:

var items = [{name:"A", x:50, y:50}, {name:"B", x:150, y:50}];
var size = 96;

var svg = d3.select("#container")
              .append("svg");
              
 var items = svg.selectAll("g")
 								.data(items)
                .enter()
                		.append("g")
                    .attr("transform", function(d, i){
                    		return "translate(" + (10+i*size+10*i) + "," + 10 + ")";
                    });
                  
                    
     items.call(d3.drag()
    	 // .subject(function(d) { return d; })
       // .on("start", dragstarted)
          .on("drag", draggedGroup)
          .subject(function() { 
       				 var t = d3.select(this);
       				 return {x: t.attr("x"), y: t.attr("y")};
    })
        //.on("end", dragended)
      );
                    
                    
    items
    		 .append("rect")
         .attr("x",function(d){return d.x;})
         .attr("y",function(d){return d.y;})
         .attr("height",size)
         .attr("width", size)
         .style("stroke", "#0F0")
         .style("fill", "transparent")
         .style("stroke-width", 3.5);
         
    items.append("line")
    			.style("stroke", "#0FF")
         .attr("x1", function(d){return d.x;})
         .attr("y1", function(d){return d.y;})
         .attr("x2", function(d){return d.x+size;})
         .attr("y2", function(d){return d.y+size;})
         
     items.append("line")
    		 .style("stroke", "#0FF")
         .attr("x1", function(d){return d.x+size;})
         .attr("y1", function(d){return d.y;})
         .attr("x2", function(d){return d.x;})
         .attr("y2", function(d){return d.y+size;})
         
    items .append("text")
     			.attr("x", function(d){return d.x+size/2})
     			.attr("y", function(d){return d.y+size/2})
       							//.attr("text-anchor", "start")
       		.style("fill", "white")
       		.text(function(d){
          		return d.name;
           })
      
         
function dragstarted(d){
	console.log("Moving "+d.name);
//  d3.select(this).raise().classed("active", true);
} 

function draggedGroup(d, i, a){

		const pos   = [d3.event.x , d3.event.y];
		const pdist = [d3.event.dx , d3.event.dy];
		d3.select(this).attr("transform", "translate("+pos[0]+","+pos[1]+")")
    
} 

function dragended(d){
 // d3.select(this).raise().classed("active", false);
  console.log("Stop moving "+d.name)
} 
body{
  background-color:black;
  padding:10px;
}

svg{
  
   width:500px;
  height:400px;
}

#container{
  width:500px;
  height:400px;
  background-color: #00F;
  
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="container"></div>


我的 分组 项目:
var items = svg.selectAll("g")
                .data(items)
                .enter()
                    .append("g")
                    .attr("transform", function(d, i){
                        return "translate(" + d.x + "," + d.y + ")";
                    });


items.append("rect")
     .attr("x",function(d){return d.x;})
     .attr("y",function(d){return d.y;})
     .attr("height",size)
     .attr("width", size)
     .style("stroke", "#0F0")
     .style("fill", "transparent")
     .style("stroke-width", 3.5);

items.append("line")
            .style("stroke", "#0FF")
     .attr("x1", function(d){return d.x;})
     .attr("y1", function(d){return d.y;})
     .attr("x2", function(d){return d.x+size;})
     .attr("y2", function(d){return d.y+size;})

 items.append("line")
         .style("stroke", "#0FF")
     .attr("x1", function(d){return d.x+size;})
     .attr("y1", function(d){return d.y;})
     .attr("x2", function(d){return d.x;})
     .attr("y2", function(d){return d.y+size;})

items .append("text")
            .attr("x", function(d){return d.x+size/2})
            .attr("y", function(d){return d.y+size/2})
                            //.attr("text-anchor", "start")
        .style("fill", "white")
        .text(function(d){
            return d.name;
       })

行为
   items.call(d3.drag()
      .on("drag", draggedGroup)
      .subject(function() { 
         var t = d3.select(this);
        return {x: t.attr("x"), y: t.attr("y")};
       })
  );

拖拽功能
function draggedGroup(d, i, a){
    const pos   = [d3.event.x , d3.event.y];
    d3.select(this).attr("transform", "translate("+pos[0]+","+pos[1]+")")  
 }

为什么当我第二次尝试拖动元素时它从第一个位置重新开始?请参阅 jsfiddle 上的示例以查看实际操作。

错误似乎在这行拖动函数中:
 d3.select(this).attr("transform", "translate("+(pos[0])+","+(pos[1])+")")

编辑

我尝试更新 停止函数将起始值更新为
    d3.select(this).attr("transform", function(d){

             d.x += d3.event.x;
             d.y += d3.event.y;

        return "translate("+d3.event.x+","+d3.event.y+")"
})

现在,如果我在拖动开始时将 d.x, d.y 放在控制台中,输出显示

x: 640 y: 299



但盒子总是在初始位置开始。我糊涂了

谢谢你的帮助!

最佳答案

您正在通过变换和 x/y 属性进行定位,这可能会导致在查看问题时遇到一些困难。更重要的是,问题是基于索引的定位和基于基准的定位的结合。

问题

如果删除 g 上的初始转换:

"translate(" + (10+i*size+10*i) + "," + 10 + ")";

你得到每组元素/g没有变换:

enter image description here

矩形 A 锚定在 50,50,矩形 B 锚定在 [150,150](使用 x,y 属性设置)。线条和文本也基于 anchor 以类似方式定位。上图中每个 g 的转换为 [0,0] .

现在让我们看看你的拖动主题:
     .subject(function() { 
        var t = d3.select(this);
        return {x: t.attr("x"), y: t.attr("y")};
     })

我会注意到 g s(this 上面)没有 x 或 y 属性(数据确实有 x 和 y 属性,但是您传递的是 HTML 元素的 x 和 y 属性)。写作 return {x:null,y:null}产生相同的结果

D3 可以通过将主题视为 [0,0] 来处理此拖动主题。因此,所有阻力都相对于 [0,0],这意味着 g 的所有阻力元素是相对于上图的,无论最后一次拖到哪里,g :一个像素的拖动将其中一个框从上图中的位置移动一个像素,而不是从拖动开始时的任何位置。

解决问题

由于您定位数据的方式,这有点棘手。您设置了一个独立于绑定(bind)数据但依赖于索引的初始翻译。然后,绑定(bind)数据用于在此之上应用 x,y 属性。通常我们会简单地使用绑定(bind)数据来表示有关元素位置的所有内容。

让我们使用 example 中的模式您首先在评论中链接到,然后对其进行调整。

此模式使用拖动的默认主题:
function subject(d) {
  return d == null ? {x: d3.event.x, y: d3.event.y} : d;
}

如果 d已定义,就是这样,我们使用被拖动元素的绑定(bind)基准作为使用 x 和 y 属性进行拖动的引用点。然后,在拖动过程中,我们更新基准以反射(reflect)其新位置,如下所示:
function dragged(d) {
  d3.select(this)
    .attr("x", d.x = d3.event.x)
    .attr("y", d.y = d3.event.y); 
}

这看起来像:

var svg = d3.select("body")
  .append("svg")
  .attr("width",500)
  .attr("height", 300);
  
var rect = svg.append("rect")
  .datum({x:50,y:50})
  .attr("width", 50)
  .attr("height", 50)
  .attr("x", function(d) { return d.x; })
  .attr("y", function(d) { return d.y; })
  .call(d3.drag()
    .on("drag", function(d) {
      d3.select(this)
        .attr("x", d.x = d3.event.x)
        .attr("y", d.y = d3.event.y)
    })  
  )
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>


但是,我们在使用您的代码时遇到了问题:

var items = [{name:"A", x:50, y:50}, {name:"B", x:150, y:50}];
var size = 96;

var svg = d3.select("#container")
   .append("svg");
              
var items = svg.selectAll("g")
 	.data(items)
  .enter()
  .append("g")
  .attr("transform", function(d, i){
     return "translate(" + (10+i*size+10*i) + "," + 10 + ")";
  });
                  
                    
items.call(d3.drag()
  .on("drag", draggedGroup)
  )
                    
items
   .append("rect")
   .attr("x",function(d){return d.x;})
   .attr("y",function(d){return d.y;})
   .attr("height",size)
   .attr("width", size)
   .style("stroke", "#0F0")
   .style("fill", "transparent")
   .style("stroke-width", 3.5);
         
items.append("line")
   .style("stroke", "#0FF")
   .attr("x1", function(d){return d.x;})
   .attr("y1", function(d){return d.y;})
   .attr("x2", function(d){return d.x+size;})
   .attr("y2", function(d){return d.y+size;})
         
items.append("line")
   .style("stroke", "#0FF")
   .attr("x1", function(d){return d.x+size;})
   .attr("y1", function(d){return d.y;})
   .attr("x2", function(d){return d.x;})
   .attr("y2", function(d){return d.y+size;})
         
items .append("text")
   .attr("x", function(d){return d.x+size/2})
   .attr("y", function(d){return d.y+size/2})
   .style("fill", "white")
   .text(function(d){
      return d.name;
   })

function draggedGroup(d, i, a){
    d.x = d3.event.x;
    d.y = d3.event.y;
		d3.select(this).attr("transform", "translate("+d.x+","+d.y+")")
    
}
body{
  background-color:black;
  padding:10px;
}

svg{
  
   width:500px;
  height:400px;
}

#container{
  width:500px;
  height:400px;
  background-color: #00F;
  
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="container"></div>


除了第一次拖动时的初始跳跃外,一切正常。初始拖动跳跃,因为拖动平移是相对于 d.x 和 d.y,但 g元素是使用不基于 d.x 或 d.y 的初始平移绘制的,它是:"translate(" + (10+i*size+10*i) + "," + 10 + ")" .

这使得很难采用 D3 模式:绑定(bind)数据不用于定位表示数据的元素。让我们改变它。让我们使用以下数据结构:
{
  name: same as before,
  x: drag position X,
  y: drag position Y,
  x1: anchor point X of rectangle, // currently d.x
  y1: anchor point Y of rectangle  // currently d.y
}

当然我们需要更新 d.x/d.y 的每一个引用

现在我们可以使用数据的 x,y 属性来跟踪拖动平移。我们将这些设置为上面翻译中使用的初始值。我们使用 x1 和 y1 来设置矩形的 anchor (矩形的 x/y 属性):
 var items = [{name:"A", x1:50, y1:50}, {name:"B", x1:150, y1:50}];
 var size = 96;

 items.forEach(function(d,i) {
    d.x = 10+i*size+10*i;
    d.y = 10;
 })

现在我们可以使用 d.x/d.y 设置初始平移,使用默认拖动主题,并在每个拖动事件上更新 d.x 和 d.y。一切都应该是花花公子:

var items = [{name:"A", x1:50, y1:50}, {name:"B", x1:150, y1:50}];
var size = 96;

items.forEach(function(d,i) {
    d.x = 10+i*size+10*i;
    d.y = 10;
})

var svg = d3.select("#container")
   .append("svg");
              
var items = svg.selectAll("g")
 	.data(items)
  .enter()
  .append("g")
  .attr("transform", function(d, i){
    return "translate(" + d.x + "," + d.y + ")";
  });
                    
                  
                    
items.call(d3.drag()
  .on("drag", draggedGroup)
  )
                    
items
   .append("rect")
   .attr("x",function(d){return d.x1;})
   .attr("y",function(d){return d.y1;})
   .attr("height",size)
   .attr("width", size)
   .style("stroke", "#0F0")
   .style("fill", "transparent")
   .style("stroke-width", 3.5);
         
items.append("line")
   .style("stroke", "#0FF")
   .attr("x1", function(d){return d.x1;})
   .attr("y1", function(d){return d.y1;})
   .attr("x2", function(d){return d.x1+size;})
   .attr("y2", function(d){return d.y1+size;})
         
items.append("line")
   .style("stroke", "#0FF")
   .attr("x1", function(d){return d.x1+size;})
   .attr("y1", function(d){return d.y1;})
   .attr("x2", function(d){return d.x1;})
   .attr("y2", function(d){return d.y1+size;})
         
items .append("text")
   .attr("x", function(d){return d.x1+size/2})
   .attr("y", function(d){return d.y1+size/2})
   .style("fill", "white")
   .text(function(d){
      return d.name;
   })

function draggedGroup(d, i, a){
    d.x = d3.event.x 
    d.y = d3.event.y
		d3.select(this).attr("transform", "translate("+d.x+","+d.y+")")
    
}
body{
  background-color:black;
  padding:10px;
}

svg{
  
   width:500px;
  height:400px;
}

#container{
  width:500px;
  height:400px;
  background-color: #00F;
  
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="container"></div>

关于javascript - D3.js v4 元素在拖动后会回到初始位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58728054/

相关文章:

javascript - 如何增加jqGrid中的行号列宽

javascript - Handsontable:更改下拉菜单宽度

javascript - 通过 JavaScript 将表格插入 div

javascript - 如何检查对象数组中是否存在键

javascript - d3.js 中的转义字符

javascript - d3.js (v3) Enter() 在条形图更新中不起作用

d3.js - d3 和 webpack : __WEBPACK_IMPORTED_MODULE_0_d3__. scaleLinear 不是函数

javascript - d3.js:有没有办法为元素数组获取线性颜色输出?

javascript - 自下而上动画 d3 图

javascript - Visual Studio Code 中的 .ts 文件没有智能感知