【问题标题】:create and drag in same click在同一点击中创建和拖动
【发布时间】:2020-11-29 18:39:52
【问题描述】:

我想用 D3.js 创建一个像 scratchnode-red 这样的应用程序,我的意思是通过单击“按钮”来创建一些 svg 元素list' 来创建一个元素,然后将它们拖到一个区域上来排列它们。

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

但是,我想模仿相同应用程序 node-redscratch 的行为,方法是使用用于创建它的相同单击拖动新的 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 em> 使用 thisd,指的是 svg 组。或者使用完整的其他方式来实现这一点?我在这里迷路了...... 谢谢,

【问题讨论】:

  • 嗨,我不是 100% 确定,但我相信你应该导入库文件,例如d3.v5.min.js 之前你自己的代码
  • 除此之外,如果您将grp 对象作为变量传递给dragstarted,会发生什么?
  • Mousedown → 克隆元素。 Mouseleave → 保持克隆活着。而 mousemove && mousedown → 移动克隆的元素。 Mouseup → 放置克隆的元素。巴姆。

标签: javascript svg d3.js drag


【解决方案1】:

如有疑问,您可以随时返回原生 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>

【讨论】:

  • 感谢 Ruben,我没想过要触发另一个鼠标事件。我是新手,没有足够的 javascript 知识。继续学习...
【解决方案2】:

你可以在用于创建新形状的按钮上收听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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-11
    • 1970-01-01
    • 2012-04-14
    • 2016-02-10
    相关资源
    最近更新 更多