带有HTML元素的d3-drag初始位置问题

时间:2018-09-13 20:49:57

标签: javascript d3.js

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

我尝试使用非svg元素查找其他示例,但找不到任何示例,而d3-drag的 documentation 并没有说明无法正确拖动非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);
}
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;
}
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>

1 个答案:

答案 0 :(得分:1)

这归结为拖动主题。

示例中的拖动不知道该拖动相对于什么,这意味着该拖动不影响您将鼠标放在其上的框上的位置(d3.event.x将是此位置上的鼠标x位置)大小写,无论您将鼠标放在哪里)。 每当完成拖动时,根据拖动事件x,y定位框的左上方。如果在框的左上角开始拖动,则效果会比在框的右下角开始拖动效果差。

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

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

在问题的示例中,我们没有提供基准,因此dnull,并且d3.event.xd3.event.y用于计算拖动的参考点。 / p>

相反,让我们给主题指定一个基准,以便我们可以计算拖动到框的相对运动并更新基准以反映任何拖动事件的结果。

更新该框的附加内容:

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

并更新拖动功能:

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

现在,我们有了一个在执行拖动时就更新的基准,并且根据应用于该基准的相对运动来定位该框:

var box = d3.select("body").append("div")
    .attr("class", "box")
    .datum({x:0,y:0});

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) {
    d3.select(this)
        .style("left", (d.x = d3.event.x) + "px")
        .style("top", (d.y = d3.event.y) + "px")
}

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

div.box {
    position: relative;
    width:40px;
    padding: 20px 20px 20px 20px;
    margin: 5px 5px 5px 5px;
   height: 40px;
    background: crimson;
}
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>

d3.drag的默认功能预期拖动基准具有x和y属性以进行定位。


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

在没有拖动主题的情况下会发生什么(在此处使用x值):

 d3.event.x = subject.x + mouseUp.x - mouseDown.x

鼠标向下/向上是拖动开始/结束,mouseUp.x - mouseDown.x是在拖动期间x的变化

没有主题数据,它简化为:

 d3.event.x = mouseUp.x 

在拖动开始时将subject.x设置为mouseDown.x

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

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

 d3.event.x = topLeft.x + mouseUp.x - mouseDown.x

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