在这个 d3.drag 示例中保留本地偏移量是什么? [英] What's preserving local offset in this d3.drag example?

查看:29
本文介绍了在这个 d3.drag 示例中保留本地偏移量是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个 d3 示例中拖动任何圆圈时,是什么阻止了圆圈的中心对齐到老鼠?

When dragging any circle in this d3 example, what prevents the circle's center from snapping to the mouse?

换句话说:当您通过单击圆外边缘附近的某处来启动圆拖动时,代码中的内容保留了拖动开始时隐含的偏移量(相对于圆的中心)?

In other words: when you initiate a circle drag by clicking somewhere near the outer edges of the circle, what in the code preserves the offset (relative to the circle's center) that's implied at drag start?

我看到这些 .attr() 调用:

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

但我希望 d3.event.x(和 .y)是鼠标的坐标——不考虑偏移——因此我认为圆的中心将(错误地,从 UX 的角度来看)最终位于鼠标正下方.

But I expect d3.event.x (and .y) to be the coordinates of the mouse — without accounting for the offset — and therefore I would think that the circle's center would (incorrectly, from a UX point-of-view) end up right under the mouse.

推荐答案

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

I believe this happens with the d3 drag subject method:

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

If subject is specified, sets the subject accessor to the specified object or function and returns the drag behavior. If subject is not specified, returns the current subject accessor, which defaults to:

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

拖动手势的主题代表被拖动的事物.它在接收到启动输入事件时计算,例如mousedown 或 touchstart,在拖动手势开始之前.然后在随后的拖动事件中将主题公开为 event.subject对于这个手势.(链接)

The subject of a drag gesture represents the thing being dragged. It is computed when an initiating input event is received, such as a mousedown or touchstart, immediately before the drag gesture starts. The subject is then exposed as event.subject on subsequent drag events for this gesture. (link)

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

We can see that if we don't provide a subject function and we also don't provide a datum with x and y properties, then the drag events will result in a circle's centering/snapping to the drag start point:

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>

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

Taking the same example, and moving assigning the datum to the parent g element allows the drag to access the subject's x and y properties (which were not present in the above example). Here the drag is relative to the initial datum (which remains unmodified), and the node will be re-centered usingn the initial x and y properties specified in the datum as the starting point for each drag (drag more than once to see):

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

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

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

Then we can update the datum of the subject, which makes each drag event relative to the circle's current position rather than the initial position:

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

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

深入拖动代码我们可以看到,当拖动开始时,如果没有为主题方法提供函数,则计算拖动 x,y start 和主题 x,y 之间的差异:

Diving into the drag code a little bit we can see that when a drag is started and if no function has been provided to the subject method, the difference between the drag x,y start and the subject x,y is calculated:

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

其中 p 是鼠标的起始位置.s 是主语.

Where p is the starting mouse position. And s is the subject.

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

Which explains why when no x or y attributes are provided, the circle snaps to wherever the drag began. When calculating the output, d3 sets the x and y values as:

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

其中 p 是当前鼠标位置.

where p is the current mouse position.

所以 d3.event.x/.y 不应该是鼠标的绝对位置,而是给定由拖动指定的位置的相对变化的圆的绝对位置.正是通过主题将鼠标位置的相对变化转化为被拖动项目的绝对位置.

So d3.event.x/.y should not be the absolute position of the mouse, but rather the absolute position of the circle given a relative change in position specified by the drag. It is through the subject that the relative change in mouse position is translated into an absolute position for the item being dragged.

这是一个自定义主题的示例,其中拖动将相对于 [100,100] 并且圆圈将在每个拖动事件开始时捕捉到那里:

Here's an example with a custom subject, where the drag will be relative to [100,100] and the circle will snap there at the beginning of each drag event:

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

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

这篇关于在这个 d3.drag 示例中保留本地偏移量是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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