d3-drag 与 html 元素初始位置问题

Rob*_*son 4 javascript d3.js

在下面的代码片段中div,当我第一次开始拖动 div 时,它会稍微向右跳动,这让我认为它会重新计算鼠标所在位置的初始起始位置,而不是添加到原始位置。

我尝试查找使用非 svg 元素的其他示例,但找不到任何示例,并且d3-drag 的文档没有说明无法正确拖动非 svg 元素,不确定我缺少什么。

这是代码:

var box = d3.select("body").append("div")
    .attr("class", "box")

d3.select(".box").call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended))

function dragstarted(d) {
    d3.select(this).raise().classed("active", true);
}

function dragged(d) {
	console.log(d3.event.x)
    d3.select(this)
        .style("left", d3.event.x + "px")
        .style("top", d3.event.y + "px")
}

function dragended(d, i) {
    d3.select(this).classed("active", false);
}
Run Code Online (Sandbox Code Playgroud)
body {
    padding-top: 25px;
    margin: auto;
    width: 600px;
}

div.box {
    position: relative;
    width: auto;
    padding: 20px 20px 20px 20px;
    margin: 5px 5px 5px 5px;
    background: crimson;
}
Run Code Online (Sandbox Code Playgroud)
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

And*_*eid 5

这归结为拖动主题。

您的示例中的拖动不知道拖动应该相对于什么,这意味着拖动不考虑您将鼠标放在框中的位置(在这种情况下,d3.event.x 将是鼠标 x 位置,无论什么)鼠标按下的位置)。每当您完成拖动时,框的左上角都会根据拖动事件 x,y 进行定位。您可以看到这种效果,如果您在左上角开始拖动,则漂移比您在框的右下角开始拖动要差一些。

让我们利用 d3-drag 中默认的拖动主题功能:

function subject(d) {
  return d == null ? {x: d3.event.x, y: d3.event.y} : d;
}
Run Code Online (Sandbox Code Playgroud)

在问题的示例中,我们没有提供数据,因此d使用nulld3.event.xd3.event.y来计算拖动的参考点。

相反,让我们为主体提供一个数据,以便我们可以计算拖动到框的相对移动并更新数据以反映任何拖动事件的结果。

更新框的附加内容:

  var box = d3.select("body").append("div")
    .attr("class", "box")
    .datum({x:0,y:0});    // starting position
Run Code Online (Sandbox Code Playgroud)

并更新拖动功能:

 function dragged(d) {
    d3.select(this)
        .style("left", (d.x = d3.event.x) + "px")
        .style("top", (d.y = d3.event.y) + "px")
}    
Run Code Online (Sandbox Code Playgroud)

现在,我们有了一个每当进行拖动时都会更新的数据,并且该框根据应用于该数据的相对移动来定位:

function subject(d) {
  return d == null ? {x: d3.event.x, y: d3.event.y} : d;
}
Run Code Online (Sandbox Code Playgroud)
  var box = d3.select("body").append("div")
    .attr("class", "box")
    .datum({x:0,y:0});    // starting position
Run Code Online (Sandbox Code Playgroud)
 function dragged(d) {
    d3.select(this)
        .style("left", (d.x = d3.event.x) + "px")
        .style("top", (d.y = d3.event.y) + "px")
}    
Run Code Online (Sandbox Code Playgroud)

d3.drag 的默认功能预计拖动基准具有用于定位的 x 和 y 属性。


更深入的解释(但为了简单起见,假设拖动事件仅包含开始和结束):

没有拖动主体时会发生什么(此处使用 x 值):

 d3.event.x = subject.x + mouseUp.x - mouseDown.x
Run Code Online (Sandbox Code Playgroud)

鼠标向下/向上是拖动开始/结束,并且是拖动持续时间内的mouseUp.x - mouseDown.x变化x

如果没有主题数据,这可以简化为:

 d3.event.x = mouseUp.x 
Run Code Online (Sandbox Code Playgroud)

assubject.x设置为mouseDown.x拖动开始时:

function subject(d) {
  return d == null ? {x: d3.event.x, y: d3.event.y} : d;
}
Run Code Online (Sandbox Code Playgroud)

但是通过将主题数据设置为代表左上角的某个初始值,我们得到:

 d3.event.x = topLeft.x + mouseUp.x - mouseDown.x
Run Code Online (Sandbox Code Playgroud)

现在我们对 x plus 进行了更改topLeft.x,这正是我们想要相对于鼠标移动来定位框的位置。