如何使用voronoi多边形修改d3力布局以触发分组元素上的事件? [英] How to modify a d3 force layout with voronoi polygons to trigger events on grouped elements?

查看:94
本文介绍了如何使用voronoi多边形修改d3力布局以触发分组元素上的事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目标是将d3力模拟,g元素和voronoi多边形相结合,以使节点上的触发事件更容易,例如拖动,鼠标悬停,工具提示等,并可以通过 d3圆拖IV示例的示例.

The goal is to combine d3 force simulation, g elements, and voronoi polygons to make trigger events on nodes easier, such as dragging, mouseovers, tooltips and so on with a graph that can be dynamically modified. This follows the d3 Circle Dragging IV example.

在以下代码中,将clip path属性添加到g元素和clippath元素时:

In the following code, when adding the clip path attribute to the g element and clippath elements:

  • 为什么拖动不触发单元格?
  • 为什么节点变得模糊并且 路径失去了边缘的样式?
  • 如何解决此问题以拖动节点并在其上触发事件,例如鼠标悬停?
  • Why does dragging not trigger on the cells?
  • Why do the nodes become obscured and the paths lose their styles on edges?
  • How can this be fixed to drag the nodes and trigger events on them like mouseovers?

var data = [
  {
    "index" : 0,
      "vx" : 0,
        "vy" : 0,
          "x" : 842,
            "y" : 106
  },
    {
      "index" : 1,
        "vx" : 0,
          "vy" : 0,
            "x" : 839,
              "y" : 56
    },
     {
        "index" : 2,
          "vx" : 0,
            "vy" : 0,
              "x" : 771,
                "y" : 72
      }
]

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height");
  
var simulation = d3.forceSimulation(data)
	.force("charge", d3.forceManyBody())
	.force("center", d3.forceCenter(width / 2, height / 2))
	.on("tick", ticked);
  
var nodes = svg.append("g").attr("class", "nodes"),
    node = nodes.selectAll("g"),
    paths = svg.append("g").attr("class", "paths"),
    path = paths.selectAll("path");

var voronoi = d3.voronoi()
	.x(function(d) { return d.x; })
	.y(function(d) { return d.y; })
	.extent([[0, 0], [width, height]]);
  
var update = function() {

  node = nodes.selectAll("g").data(data);
    var nodeEnter = node.enter()
  	.append("g")
  	.attr("class", "node")
    .attr("clip-path", function(d, i) { return "url(#clip-" + i + ")"; });
  nodeEnter.append("circle");
  nodeEnter.append("text")
    .text(function(d, i) { return i; });  
  node.merge(nodeEnter); 
  
  path = paths.selectAll(".path")
  .data(data)
  .enter().append("clipPath")
    .attr("id", function(d, i) { return "clip-" + i; })
    .append("path")
    .attr("class", "path");
  
  simulation.nodes(data);
  simulation.restart();

}();
  
function ticked() {
	var node = nodes.selectAll("g");
  var diagram = voronoi(node.data()).polygons();
  
  paths.selectAll("path")
    .data(diagram)
    .enter()
    .append("clipPath")
    .attr("id", function(d, i) { return "clip-" + i; })
    .append("path")
    .attr("class", "path");

  paths.selectAll("path")
    .attr("d", function(d) { return d == null ? null : "M" + d.join("L") + "Z"; });
  
  node.call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));  

  node
    .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" });
}

function dragstarted(d) {
  if (!d3.event.active) simulation.alphaTarget(0.3).restart();
  d.fx = d.x;
  d.fy = d.y;
}

function dragged(d) {
  d.fx = d3.event.x;
  d.fy = d3.event.y;
}

function dragended(d) {
  if (!d3.event.active) simulation.alphaTarget(0);
  d.fx = null;
  d.fy = null;
}

svg {
  border: 1px solid #888888;  
}

circle {
  r: 3;
  cursor: move;
  fill: black;
}

.node {
  pointer-events: all;
}

path {
  fill: none;
  stroke: #999;
  pointer-events: all;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.1/d3.js"></script>
<svg width="400" height="400"></svg>

(单独的问题,但是将路径嵌套在圆拖动IV"元素中的g元素中会导致路径不希望地定位到图形的侧面.)

(Separate question, but nesting the paths in the g elements as in the Circle Dragging IV element causes undesired positioning of the paths off to the side of the graph.)

相关问题,使用多边形而不是路径和剪切路径,我可以将其拖到工作中,但是我试图使用剪切路径版本作为比较,但不确定有什么区别,除了剪切路径似乎更可取由Mike Bostock(d3创作者)完成.

In a related question, using polygons instead of paths and clippaths, I can get the dragging to work, but am trying to use the clippath version as a comparison and not sure what are the differences, other than clippath seems to be preferred by Mike Bostock (d3 creator).

推荐答案

Block 版本.

  • 为什么拖动不会在单元格上触发?
    • 因为单元格属性具有fill:none,那么它必须具有指针事件:all.
    • Why does dragging not trigger on the cells?
      • Because if the cell attribute has fill:none, then it must have pointer-events:all.
      • 因为剪辑路径的目标是g元素位置而不是圆形位置.
      • 使用路径attr指针事件:全部path { pointer-events: all; }
      • 在拖动或打勾事件中选择所需的子元素,例如圆圈或文本,以定位parent.select(child).attr('d' function(d) { ..do stuff.. });
      • 使用节点ID作为参考来简化数据阵列的更新或删除node.data(data, function(d) { return d.id; })
      • use path attr pointer-events: all, path { pointer-events: all; }
      • select the desired child element such as circle, or text, in the drag or tick event for positioning parent.select(child).attr('d' function(d) { ..do stuff.. });
      • use node id's for references to simplify data array updates or deletions node.data(data, function(d) { return d.id; })

      感谢安德鲁·里德(Andrew Reid)的帮助.

      Thanks Andrew Reid for your help.

      var svg = d3.select("svg"),
          width = +svg.attr("width"),
          height = +svg.attr("height"),
          color = d3.scaleOrdinal(d3.schemeCategory10);
      
      var a = {id: "a"},
          b = {id: "b"},
          c = {id: "c"},
          data = [a, b, c],
          links = [];
      
      var simulation = d3.forceSimulation(data)
          .force("charge", d3.forceManyBody().strength(-10))
          .force("link", d3.forceLink(links).distance(200))
      		.force("center", d3.forceCenter(width / 2, height / 2))
          .alphaTarget(1)
          .on("tick", ticked);
      
      var link = svg.append("g").attr("class", "links").selectAll(".link"),
          node = svg.append("g").attr("class", "nodes").selectAll(".node");
          
      var voronoi = d3.voronoi()
      	.x(function(d) { return d.x; })
      	.y(function(d) { return d.y; })
      	.extent([[-1, 1], [width + 1, height + 1]]);
      
      update();
      
      d3.timeout(function() {
        links.push({source: a, target: b}); // Add a-b.
        links.push({source: b, target: c}); // Add b-c.
        links.push({source: c, target: a}); // Add c-a.
        update();
      }, 1000);
      
      d3.interval(function() {
        data.pop(); // Remove c.
        links.pop(); // Remove c-a.
        links.pop(); // Remove b-c.
        update();
      }, 5000, d3.now());
      
      d3.interval(function() {
        data.push(c); // Re-add c.
        links.push({source: b, target: c}); // Re-add b-c.
        links.push({source: c, target: a}); // Re-add c-a.
        update();
      }, 5000, d3.now() + 1000);
      
      function update() {
      
        node = node.data(data, function(d) { return d.id; });
        node.exit().remove();
        var nodeEnter = node.enter().append("g")
        	.attr("class", "node")
          .on("mouseover", mouseover)
          .on("mouseout", mouseout);
        nodeEnter.append("circle").attr("fill", function(d) { return color(d.id); }).attr("r", 8);
        nodeEnter.append("text")
          .attr("dx", 12)
          .attr("dy", ".35em")
        	.text(function(d) { return d.id; });
        nodeEnter.append("path").attr("class", "path");
        nodeEnter.call(d3.drag()
                       .on("start", dragstarted)
                       .on("drag", dragged)
                       .on("end", dragended));
        node = node.merge(nodeEnter);
      
      
        // Apply the general update pattern to the links.
        link = link.data(links, function(d) { return d.source.id + "-" + d.target.id; });
        link.exit().remove();
        link = link.enter().append("line").merge(link);
      
        // Update and restart the simulation.
        simulation.nodes(data);
        simulation.force("link").links(links);
        simulation.alpha(1).restart();
      }
      
      function mouseover(d) {
      	d3.select(this).raise().classed("active", true);
      }
      
      function mouseout(d) {
      	d3.select(this).raise().classed("active", false);
      }
      
      function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart();
        d.fx = d.x;
        d.fy = d.y;
      }
      
      function dragged(d) {
        d3.select(this).select("circle").attr("cx", d.fx = d3.event.x).attr("cy", d.fy = d3.event.y);
      }
      
      function dragended(d) {
        if (!d3.event.active) simulation.alphaTarget(0);
        d.fx = null;
        d.fy = null;
      }
      
      function ticked() {
        node.select("circle")
        	.attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
          
        node.select("path")
        	.data(voronoi.polygons(data))
          .attr("d", function(d) { return d == null ? null : "M" + d.join("L") + "Z"; });
          
        node.select("text")
        	.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" });
      
        link.attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });
      }

      path {
        pointer-events: all;
        fill: none;
        stroke: #666;
        stroke-opacity: 0.2;
      }
      
      .active path {
        fill: #111;  
        opacity: 0.05;
      }
      
      .active text {
        visibility: visible;
      }
      
      .active circle {
        stroke: #000;
        stroke-width: 1.5px;
      }
      
      svg {
        border: 1px solid #888;  
      }
      
      .links {
        stroke: #000;
        stroke-width: 1.5;
      }
      
      .nodes {
        stroke-width: 1.5;
      }
      
      text {
        pointer-events: none;
        font: 1.8em sans-serif;
        visibility: hidden;
      }

      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
      <svg width="400" height="400"></svg>

      这篇关于如何使用voronoi多边形修改d3力布局以触发分组元素上的事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆