最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use d3.js drag properly? - Stack Overflow

programmeradmin2浏览0评论

I am creating a floor map using d3.js and 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. I am quite new to vue js and d3.js so I am using this as a reference. The button works and adds X amount of rectangles for X amount of presses. However, when I add the first doorway and try to drag the rectangle it is pletely 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")
      },

I am creating a floor map using d3.js and 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. I am quite new to vue js and d3.js so I am using this as a reference. The button works and adds X amount of rectangles for X amount of presses. However, when I add the first doorway and try to drag the rectangle it is pletely 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")
      },
Share Improve this question edited Oct 29, 2020 at 17:54 brennan asked Oct 29, 2020 at 17:12 brennanbrennan 1853 silver badges13 bronze badges 2
  • In the dragging function, where is event defined? Also you use the same value pointer(event)[0] for both x and y. Though, event.x and event.y may be appropriate if using d3v6. Is there a zoom applied to the SVG at all? Can you make a minimal snippet that replicates your issue? As is there is room for lots of suggestions that may or may not be useful. – Andrew Reid Commented Oct 29, 2020 at 17:17
  • @AndrewReid Thank you, I didnt even realize that I was using the same value for X and Y coor. Leason learned so thank you. Im using d3v5. No there is no zoom applied to anything. What do you mean by minimal snippet? – brennan Commented Oct 29, 2020 at 17:23
Add a ment  | 

1 Answer 1

Reset to default 4

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. 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:

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. 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. So, ideally, you can access this - don't use arrow functions for the listeners

  1. You apply a transform and position by x,y.

When initially appending the rectangles you position by x,y. When dragging you apply a translate, but don't remove the initial x,y positioning. 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). 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.


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.

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./ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Add Rect</button>

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./ajax/libs/d3/6.1.0/d3.min.js"></script>
<button>Add Rect</button>

发布评论

评论列表(0)

  1. 暂无评论