在这个d3.drag示例中,什么保留了本地偏移量?

时间:2018-03-08 18:03:41

标签: d3.js drag

this d3 example中拖动任何圆圈时,是什么阻止圆圈的中心对齐鼠标?

换句话说:当你通过点击圆圈外边缘附近的某个来启动圆圈拖动时,代码中的内容会保留拖拽开始时隐含的偏移(相对于圆的中心) ?

我看到这些.attr()来电:

.attr("cx", d.x = d3.event.x)
.attr("cy", d.y = d3.event.y)

但是我希望d3.event.x(和.y)是鼠标的坐标 - 不考虑偏移 - 因此我认为圆的中心会(从UX点不正确) (视图)最终在鼠标下方。

1 个答案:

答案 0 :(得分:4)

我相信d3拖动主题方法会发生这种情况:

  

如果指定了subject,则将主题访问者设置为指定的   对象或函数并返回拖动行为。如果主题不是   指定,返回当前主题访问者,默认为:

     

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

     

拖动手势的主题代表被拖动的东西。它   在接收到发起输入事件时计算,例如a   在拖动手势开始之前,mousedown或touchstart。   然后,主题在后续拖动事件中作为event.subject公开   这个手势。 (link

我们可以看到,如果我们不提供主题功能而且我们也没有提供带有x和y属性的数据,那么拖动事件将导致圆圈的居中/捕捉到拖动起点:



var svg = d3.select("body")
  .append("svg")
  .attr("width",500)
  .attr("height",300);
  
var datum = {x:250,y:150}
  
var g = svg.append("g")
    
  g.append("rect")
  .attr("width",500)
  .attr("height",300)
  .attr("fill","#ddd");
  
  g.append("circle")
  .datum(datum)
  .attr("cx",function(d) { return d.x; })
  .attr("cy",function(d) { return d.y; })
  .attr("r",10);
    
g.call(d3.drag().on("drag", dragged))
  
function dragged(d) {
  d3.select(this)
    .select("circle")
    .attr("cx", d3.event.x)
    .attr("cy", d3.event.y);
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
&#13;
&#13;
&#13;

采用相同的示例,并将基准分配到父g元素允许拖动访问主题的x和y属性(在上面的示例中不存在)。这里的拖动相对于初始数据(保持未修改),并且使用在基准中指定的初始x和y属性作为每个拖动的起始点(拖动多次以查看)重新居中节点:

&#13;
&#13;
var svg = d3.select("body")
  .append("svg")
  .attr("width",500)
  .attr("height",300);
  
var datum = {x:250,y:150}
  
var g = svg.append("g")
  .datum(datum);
  
  g.append("rect")
  .attr("width",500)
  .attr("height",300)
  .attr("fill","#ddd");
  
  g.append("circle")
  .attr("cx",function(d) { return d.x; })
  .attr("cy",function(d) { return d.y; })
  .attr("r",10);
    
g.call(d3.drag().on("drag", dragged))
  
function dragged(d) {
  d3.select(this)
    .select("circle")
    .attr("cx", d3.event.x)
    .attr("cy", d3.event.y);
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
&#13;
&#13;
&#13;

然后我们可以更新主题的数据,这使得每个拖动事件相对于圆的当前位置而不是初始位置:

&#13;
&#13;
var svg = d3.select("body")
  .append("svg")
  .attr("width",500)
  .attr("height",300);
  
var datum = {x:250,y:150}
  
var g = svg.append("g")
  .datum(datum);
  
  g.append("rect")
  .attr("width",500)
  .attr("height",300)
  .attr("fill","#ddd");
  
  g.append("circle")
  .attr("cx",function(d) { return d.x; })
  .attr("cy",function(d) { return d.y; })
  .attr("r",10);
    
g.call(d3.drag().on("drag", dragged))
  
function dragged(d) {
  d3.select(this)
    .select("circle")
    .attr("cx", d.x = d3.event.x)
    .attr("cy", d.y = d3.event.y);
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
&#13;
&#13;
&#13;

稍微深入拖拽代码我们可以看到,当拖动开始并且没有为主题方法提供任何功能时,会计算拖拽x,y start和主题x,y之间的差异:

  dx = s.x - p[0] || 0;
  dy = s.y - p[1] || 0;

其中p是起始鼠标位置。 s是主题。

这解释了为什么当没有提供x或y属性时,圆圈会捕捉到拖动开始的位置。计算输出时,d3将x和y值设置为:

p[0] + dx,
p[1] + dy

其中p是当前鼠标位置。

因此d3.event.x / .y不应该是鼠标的绝对位置,而应该是由于拖动指定的位置的相对变化而得到的圆的绝对位置。通过主题,鼠标位置的相对变化被转换为被拖动项目的绝对位置。

以下是自定义主题的示例,其中拖动将相对于[100,100],并且圆圈将在每个拖动事件开始时对齐:

&#13;
&#13;
var svg = d3.select("body")
  .append("svg")
  .attr("width",500)
  .attr("height",300);
  
var datum = {x:250,y:150}
  
var g = svg.append("g")
  .datum(datum);
  
  g.append("rect")
  .attr("width",500)
  .attr("height",300)
  .attr("fill","#ddd");
  
  g.append("circle")
  .attr("cx",function(d) { return d.x; })
  .attr("cy",function(d) { return d.y; })
  .attr("r",10);
    
g.call(d3.drag()
   .on("drag", dragged)
   .subject({x:100,y:100})
   )
  
function dragged(d) {
  d3.select(this)
    .select("circle")
    .attr("cx", d3.event.x)
    .attr("cy", d3.event.y);
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
&#13;
&#13;
&#13;