在this d3 example中拖动任何圆圈时,是什么阻止圆圈的中心对齐鼠标?
换句话说:当你通过点击圆圈外边缘附近的某个来启动圆圈拖动时,代码中的内容会保留拖拽开始时隐含的偏移(相对于圆的中心) ?
我看到这些.attr()
来电:
.attr("cx", d.x = d3.event.x)
.attr("cy", d.y = d3.event.y)
但是我希望d3.event.x
(和.y
)是鼠标的坐标 - 不考虑偏移 - 因此我认为圆的中心会(从UX点不正确) (视图)最终在鼠标下方。
答案 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;
采用相同的示例,并将基准分配到父g
元素允许拖动访问主题的x和y属性(在上面的示例中不存在)。这里的拖动相对于初始数据(保持未修改),并且使用在基准中指定的初始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")
.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;
然后我们可以更新主题的数据,这使得每个拖动事件相对于圆的当前位置而不是初始位置:
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;
稍微深入拖拽代码我们可以看到,当拖动开始并且没有为主题方法提供任何功能时,会计算拖拽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],并且圆圈将在每个拖动事件开始时对齐:
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;