拖动后,D3.js v4元素将返回到初始位置

我有一个具有某些组和拖动行为的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",function(d){return d.x+size;})
         .attr("y1",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",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",function(d){return d.x+size;})
     .attr("y1",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,a){
    const pos   = [d3.event.x,d3.event.y];
    d3.select(this).attr("transform","+pos[1]+")")  
 }

为什么第二次尝试拖动元素时它会从第一个位置重新开始?请参阅jsfiddle上的示例以了解实际效果。

错误似乎出在拖动功能的这一行:

 d3.select(this).attr("transform","translate("+(pos[0])+","+(pos[1])+")")

编辑

我尝试更新 STOP 函数以将起始值更新为

    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

,但框始终以初始位置开始。我很困惑

感谢您的帮助!

hanhjj 回答:拖动后,D3.js v4元素将返回到初始位置

您同时定位了transforms和x / y属性,这可能使您很难看到问题。更重要的是,这个问题是基于索引的定位和基于基准的定位的组合。

问题

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

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

您无需变换即可获得每组元素/ g

enter image description here

矩形A固定在50,50,矩形B固定在[150,150](使用x,y属性设置)。线条和文本也基于锚点类似地定位。上图中,每个g的翻译为[0,0]。

现在让我们看看您的拖动主题:

     .subject(function() { 
        var t = d3.select(this);
        return {x: t.attr("x"),y: t.attr("y")};
     })

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

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

解决问题

由于定位数据的方式,这有点棘手。您设置的初始转换独立于绑定数据,而是依赖于索引。然后,绑定的数据将用于在此之上应用x,y属性。通常,我们只使用绑定数据来表示有关元素位置的所有内容。

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

此模式使用默认的拖动主题:

function subject(d) {
  return d == null ? {x: d3.event.x,y: d3.event.y} : d;
}

如果定义了d,则使用x和y属性将拖动元素的绑定基准用作拖动的参考点。然后,在拖动过程中,我们使用以下方法更新基准面以反映其新位置:

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",function(d){return d.x+size;})
   .attr("y1",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模式:绑定的数据未用于定位表示数据的元素。让我们改变它。让我们使用以下数据结构:

{
  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设置矩形的锚点(矩形的x / y属性):

 var items = [{name:"A",x1:50,y1:50},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",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",i){
    return "translate(" + d.x + "," + d.y + ")";
  });
                    
                  
                    
items.call(d3.drag()
  .on("drag",function(d){return d.x1;})
   .attr("y",function(d){return d.y1;})
   .attr("height",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",function(d){return d.x1+size;})
   .attr("y1",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",a){
    d.x = d3.event.x 
    d.y = d3.event.y
		d3.select(this).attr("transform","+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>

本文链接:https://www.f2er.com/3153078.html

大家都在问