在同一点击中创建和拖动

create and drag in same click

我想创建一个应用程序,如 scratchnode-red,使用 D3.js,我的意思是通过单击 'button list' 创建一些 svg 元素来创建一个元素,然后将它们拖到一个区域上进行排列。

这个想法适用于我下面的代码。我可以单击以创建形状(svg 组)。创建后,我可以(再次)单击它们并将其拖到 svg 区域上。

但是,我想通过将新的 svg 元素与用于创建它的相同单击。一言以蔽之。但我不知道如何在创建的元素上以编程方式启动拖动行为。这是我的工作代码。

var svg = d3.select("body").append("svg")
  .attr("width", 1500)
  .attr("height", 800);
addButton(svg, 'ADD');

function addShape(svg, x, y) {
  var dotContainer = svg.append("g")
    .attr("class", "dotContainer")
    .datum({
      x: x,
      y: y
    })
    .attr("transform", function(d) {
      return 'translate(' + d.x + ' ' + d.y + ')';
    })
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));
  var text = dotContainer.append("text")
    .datum({
      x: 20,
      y: 20
    })
    .attr("x", function(d) {
      return d.x;
    })
    .attr("y", function(d) {
      return d.y;
    })
    .text('Title');
  var rectangle = dotContainer.append("rect")
    .attr("width", 200)
    .attr("height", 100)
    .attr("x", 0)
    .attr("y", 0)
    .attr('style', "opacity:1;fill:#ffffff;fill-opacity:0;stroke:#000000;stroke-width:5;stroke-opacity:1")
    .attr("ry", 8);
  return dotContainer;
}

function dragstarted(d) {
  let xCoord = d3.event.dx - d3.select(this).attr('x')
  let yCoord = d3.event.dy - d3.select(this).attr('y')
}

function dragged(d) {
  d3.select(this).select("text").text(d.x + ';' + d.y);
  d.x += d3.event.dx;
  d.y += d3.event.dy;
  d3.select(this).attr("transform", function(d, i) {
    return "translate(" + [d.x, d.y] + ")"
  });
}

function dragended(d) {
  d3.select(this).attr("transform", function(d, i) {
    return "translate(" + [d.x, d.y] + ")"
  });
}

function addButton(area, title) {
  var group = area.append("g");
  group.append("rect")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 100)
    .attr("height", 50)
    .attr('style', 'fill:rgb(255,0,0);stroke-width:1;stroke:rgb(200,200,200)');
  group.append("text")
    .attr('x', 20)
    .attr('y', 20)
    .text(title);
  group.on('mousedown', function() {
    var grp = addShape(area, 0, 0);

    //START DRAG ON grp HERE ???
  });
}
<script src="https://d3js.org/d3.v5.min.js"></script>

所以,我的问题是我不知道如何从 svg 组 dotContainer 外部调用 dragstarted() ,因为 dragstarted 使用 thisd,它指的是 svg 组。还是使用完全不同的方式来实现这一目标?我在这里迷路了.... 谢谢,

你可以在用于创建新形状的按钮上收听 mousedown。在事件侦听器中,您创建一个新形状并创建一个新的 mousedown 事件,您立即在新元素上分派该事件。这个新的 mousedown 事件将触发拖动行为,触发一次拖动开始侦听器,并持续触发拖动侦听器,直到鼠标被抬起。这可能看起来像:

select.on("mousedown", function(event,d) {

   // create some new shape:
   var aNewShape = container.append("shape")
     .attr(...)
     ....

   // create a new event and dispatch it on the new shape
   var e = document.createEvent("MouseEvents");
   e.initMouseEvent("mousedown", true,true,window,0,0,0,event.x,event.y)
   aNewShape.node().dispatchEvent(e)

 })

可能看起来像:

var svg = d3.select("body")
  .append("svg")
  .attr("width",400)
  .attr("height", 300);
  
var data = [
  {shape: d3.symbolCross, y: 0, cy: 25, cx: 25},
  {shape: d3.symbolWye, y: 60, cy: 85, cx: 25 },
  {shape: d3.symbolDiamond, y: 120, cy: 145, cx: 25}
]

// Add some buttons:
var g = svg.selectAll("null")
  .data(data)
  .enter()
  .append("g")
  .attr("transform",function(d,i) {
    return "translate("+[0,d.y]+")";
  })

g.append("rect")
  .attr("width", 50)
  .attr("height", 50)
  .attr("fill", "#ddd");
  
g.append("path")
  .attr("d", function(d) { return  d3.symbol().type(d.shape).size(100)(); })
  .attr("transform","translate(25,25)")
  .attr("fill", "#aaa");

// Some sort of drag function
var drag = d3.drag()
  .on("drag", function(event,d) {
    d.x = event.x;
    d.y = event.y;
    d3.select(this).attr("transform", "translate("+[d.x,d.y]+")");
    
  })
  .on("start", function() {
    d3.select(this).transition()
       .attr("fill","steelblue")
       .duration(1000);
  })

// Mouse down event:
g.on("mousedown", function(event,d) {
   
   
   var shape = svg.append("path")
       .datum({type:d.shape,x:d.cx,y:d.cy})
       .attr("d", d3.symbol().type(d.shape).size(300)())
       .attr("transform", function(d) { return "translate("+[d.x,d.y]+")" })
       .attr("fill","black")
       .call(drag);
       
   var e = document.createEvent("MouseEvents");
   e.initMouseEvent("mousedown", true,true,window,0,0,0,event.x,event.y)
   shape.node().dispatchEvent(e);

})
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>

如有疑问,您可以随时返回原版 JavaScript。在这种情况下,您可以使用 d3.event 对象作为属性字典来调度 custom MouseDown event,本质上是克隆元素。

然后,MouseMove 事件接管并无缝处理:

var svg = d3.select("body").append("svg")
  .attr("width", 1500)
  .attr("height", 800);
addButton(svg, 'ADD');

function addShape(svg, x, y) {
  var dotContainer = svg.append("g")
    .attr("class", "dotContainer")
    .datum({
      x: x,
      y: y
    })
    .attr("transform", function(d) {
      return 'translate(' + d.x + ' ' + d.y + ')';
    })
    .call(d3.drag()
      .on("start", dragstarted)
      .on("drag", dragged)
      .on("end", dragended));
  var text = dotContainer.append("text")
    .datum({
      x: 20,
      y: 20
    })
    .attr("x", function(d) {
      return d.x;
    })
    .attr("y", function(d) {
      return d.y;
    })
    .text('Title');
  var rectangle = dotContainer.append("rect")
    .attr("width", 200)
    .attr("height", 100)
    .attr("x", 0)
    .attr("y", 0)
    .attr('style', "opacity:1;fill:#ffffff;fill-opacity:0;stroke:#000000;stroke-width:5;stroke-opacity:1")
    .attr("ry", 8);
  return dotContainer;
}

function dragstarted(d) {
  let xCoord = d3.event.dx - d3.select(this).attr('x')
  let yCoord = d3.event.dy - d3.select(this).attr('y')
}

function dragged(d) {
  d3.select(this).select("text").text(d.x + ';' + d.y);
  d.x += d3.event.dx;
  d.y += d3.event.dy;
  d3.select(this).attr("transform", function(d, i) {
    return "translate(" + [d.x, d.y] + ")"
  });
}

function dragended(d) {
  d3.select(this).attr("transform", function(d, i) {
    return "translate(" + [d.x, d.y] + ")"
  });
}

function addButton(area, title) {
  var group = area.append("g");
  group.append("rect")
    .attr("x", 0)
    .attr("y", 0)
    .attr("width", 100)
    .attr("height", 50)
    .attr('style', 'fill:rgb(255,0,0);stroke-width:1;stroke:rgb(200,200,200)');
  group.append("text")
    .attr('x', 20)
    .attr('y', 20)
    .text(title);
  group.on('mousedown', function() {
    var grp = addShape(area, 0, 0);

    grp.node().dispatchEvent(new MouseEvent(
      "mousedown",
      d3.event
    ));
  });
}
<script src="https://d3js.org/d3.v5.js"></script>