简体   繁体   English

如何正确使用d3.js拖动?

[英]How to use d3.js drag properly?

I am creating a floor map using d3.js and Vue js.我正在使用 d3.js 和 Vue js 创建楼层地图。 I want to add a feature where the user clicks a button which creates a rectangle (a doorway) that the user can change the scale and X,Y positions.我想添加一个功能,用户单击一个按钮,该按钮创建一个矩形(门口),用户可以更改比例和 X、Y 位置。 I am quite new to vue js and d3.js so I am using this as a reference.我对 vue js 和 d3.js 很陌生,所以我将用作参考。 The button works and adds X amount of rectangles for X amount of presses.该按钮起作用并为 X 次按压添加 X 量矩形。 However, when I add the first doorway and try to drag the rectangle it is completely off from the user's mouse coordinates.但是,当我添加第一个门口并尝试拖动矩形时,它完全脱离了用户的鼠标坐标。 Further, when I add the second, third, fourth, etc rectangle I can't change the position of those, only the first rectangle.此外,当我添加第二个、第三个、第四个等矩形时,我无法更改它们的位置,只能更改第一个矩形。 I think this is because I'm only selecting one rectangle in the dragging function.我想这是因为我在拖动功能中只选择了一个矩形。 However Im more concerned about getting the dragging right on the rectangles.但是我更关心在矩形上正确拖动。 Would someone be able to point me in the right direction please?有人能指出我正确的方向吗?

<template>
  <div id = "app">

    <header>
      <Navbar />
    </header>

      <div class = "floor"> 
        <div id = "tooltip"></div>
        <svg width = "850" height = "800">
        </svg>
      </div>
    
    <div class = "buttons"> 
      <button v-on:click="addRec()"> Add Doorway </button>
    </div>

    
  </div>
</template>
addRec(){

        console.log("Doorway Added")
        var doorGroup = d3.selectAll("svg")
          .append("g")

        //setting attributes for doorway
        var doorway = doorGroup.append("rect")
          .attr("width",100)
          .attr("height", 25)
          .attr("x", 150)
          .attr("y", 200)

        //setting style attributes for doorway
          .attr("fill","#F5F5F5")
            
          .attr("stroke", "black")
            .attr("stroke-width", 2.5)
          .attr("cursor", "move")
          
          //binding drag method onto rectangle
          .call(d3.drag()
            .on('start', dragStart)
            .on('drag', dragging)
            .on('end', dragEnd)
          )
          
      },
      //this is to be called once used has clicked on the doorway
      dragStart(d,i,nodes){
        d3.select(nodes[i])
          .style("stroke", "red")
      },
      /*
      this is to be called once used has started dragging the doorway
      dragging(d) upadtes the position of the doorway, from the mouse X,Y Coors
      */
      dragging(d,i,nodes){
        var width = 850
        var height = 800
        var xCoor = d3.pointer(event)[0]
        var yCoor = d3.pointer(event)[1]

        // d3.select(nodes[i])
        //   .attr("transform", "translate(" + (xCoor - 200) + "," + (yCoor - 300) + ")")

        d3.select(nodes[i])
          .attr("x", xCoor)
          .attr("y", yCoor)
      },
      //this is to be called once user has clicked off the doorway
      dragEnd(d,i,nodes){
        d3.select(nodes[i])
          .style("stroke", "black")
      },

There are a two issues in your code:您的代码中有两个问题:

  1. You only ever select the same rectangle:您只能选择相同的矩形:

All your drag functions use:您所有的拖动功能都使用:

d3.select("rect")

This will select the first matching element in the DOM.这将选择 DOM 中的第一个匹配元素。 This will always be the same rectangle, it doesn't correspond to the rectangle being dragged.这将始终是同一个矩形,它与被拖动的矩形不对应。 Instead, you can access the proper rectangle with:相反,您可以通过以下方式访问正确的矩形:

function dragFunction() {
  d3.select(this);
}

In d3v5 and earlier, if this isn't available you can use:在 d3v5 及更早版本中,如果this不可用,您可以使用:

dragFunction(d,i,nodes) => d3.select(nodes[i])

There isn't a great way to get your rectangle if you can't access this yet in d3v6 - the migration guide shows a "old" way to get this that involves filtering elements for a datum match.如果您还不能在 d3v6 中访问this则没有获得矩形的好方法 -迁移指南显示了一种“旧”方法来获取this ,其中涉及过滤数据匹配的元素。 I've never seen that approach used in d3v5 or earlier, d3.select(nodes[i]) was always the fall back if this wasn't available.我从来没有见过在d3v5使用的方法或更早, d3.select(nodes[i])一直在回落,如果this是不可用。 So, ideally, you can access this - don't use arrow functions for the listeners因此,理想情况下,您可以访问this - 不要为侦听器使用箭头函数

  1. You apply a transform and position by x,y.您通过 x,y 应用变换和位置。

When initially appending the rectangles you position by x,y.最初附加按 x,y 定位的矩形时。 When dragging you apply a translate, but don't remove the initial x,y positioning.拖动时应用平移,但不要删除初始 x,y 定位。 This results in the x,y coordinates being applied twice, once with their original values, and once with their dragged values relative to the origin (not their starting position).这导致 x,y 坐标被应用两次,一次使用它们的原始值,一次使用它们相对于原点(而不是它们的起始位置)的拖动值。 You should modify the same property for initial placement and for drag - either x,y attributes or the transform, but don't use both for placement.您应该为初始放置和拖动修改相同的属性 - x,y 属性或变换,但不要将两者都用于放置。


D3v5 D3v5

Along with using d3.event.x and d3.event.y, (d3.pointer was added in d3v6), I've made the above changes and believe I've created the intended effect below.随着使用 d3.event.x 和 d3.event.y,(d3.pointer 被添加到 d3v6 中),我已经进行了上述更改,并且相信我已经创建了下面的预期效果。

 var svg = d3.select("body") .append("svg") .attr("width", 500) .attr("height", 300); d3.select("button") .on("click", function() { svg.append("rect") .attr("width",10) .attr("height", 25) .attr("x", 150) .attr("y", 100) .attr("fill","#1b1c3b") .attr("opacity", ".5") .attr("stroke", "black") .attr("stroke-width", "2") .attr("cursor", "move") .call(d3.drag() .on('start', dragStart) .on('drag', dragging) .on('end', dragEnd) ) function dragStart(d,i,nodes){ d3.select(nodes[i]) .style("stroke", "red") } function dragging(d,i,nodes){ var xCoor = d3.event.x; var yCoor = d3.event.y; d3.select(nodes[i]) .attr("x", xCoor) .attr("y", yCoor); } function dragEnd(d,i,nodes){ d3.select(nodes[i]) .style("stroke", "black") } })
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <button>Add Rect</button>

D3v6 D3v6

 var svg = d3.select("body") .append("svg") .attr("width", 500) .attr("height", 300); d3.select("button") .on("click", function() { svg.append("rect") .attr("width",10) .attr("height", 25) .attr("x", 150) .attr("y", 100) .attr("fill","#1b1c3b") .attr("opacity", ".5") .attr("stroke", "black") .attr("stroke-width", "2") .attr("cursor", "move") .call(d3.drag() .on('start', dragStart) .on('drag', dragging) .on('end', dragEnd) ) function dragStart(event,d){ d3.select(this) .style("stroke", "") } function dragging(event,d){ var xCoor = event.x; var yCoor = event.y; d3.select(this) .attr("x", xCoor) .attr("y", yCoor); } function dragEnd(event,d){ d3.select(this) .style("stroke", "black") } })
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.0/d3.min.js"></script> <button>Add Rect</button>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM