d3力导图绘制节点间多条关系平行线的方法


之前用d3做了多条线之间的绘图是曲线表示的,现在产品要求改成平行线的样式,经过在网上的调研和自己的尝试,实践出一个可用的方法,分享给大家,先展示下结果:

事先声明,本方法是在以下参考网站上进行的结合和更改:
d3力导图节点间多条线的绘图方法
d3.js Force Layout: drawing multiple straight, parallel links between two nodes
force layout with multiple links between nodes

如果图示的样式是你想要的,那么直接粘贴代码看吧!代码可直接运行。


<!DOCTYPE html>

<head>
  <meta charset="utf-8">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script>
  <script src="https://d3js.org/d3.v3.min.js"></script>
  <style>
    body {
      margin: 0;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }

    path {
      fill: none;
      stroke-width: 3;
    }

    circle {
      stroke: white;
      stroke-width: 2;
    }
  </style>
</head>

<body>
  <script>

    // DATA
    var nodes = [{}, {}];
    var links = [

      // links 可更改,自己改数量显示不同的条数
      { source: 0, target: 1 },
      { source: 0, target: 1 },
      { source: 0, target: 1 },
      { source: 0, target: 1 },
      { source: 0, target: 1 }

    ];

    _.each(links, function (link) {
      var same = _.filter(links, {
        source: link.source,
        target: link.target
      });
      var sameAlt = _.filter(links, {
        source: link.target,
        target: link.source
      });

      var sameAll = same.concat(sameAlt);
      _.each(sameAll, function (s, i) {
        s.sameIndex = i + 1;
        s.sameTotal = sameAll.length;
        s.sameTotalHalf = s.sameTotal / 2;
        s.sameUneven = s.sameTotal % 2 !== 0;
        s.sameMiddleLink =
          s.sameUneven === true && Math.ceil(s.sameTotalHalf) === s.sameIndex;
        s.sameLowerHalf = s.sameIndex <= s.sameTotalHalf;
        s.sameArcDirection = s.sameLowerHalf ? 0 : 1;
        s.sameIndexCorrected = s.sameLowerHalf
          ? s.sameIndex
          : s.sameIndex - Math.ceil(s.sameTotalHalf);
      });
    });

    var maxSame = _.chain(links)
      .sortBy(function (x) {
        return x.sameTotal;
      })
      .last()
      .value().sameTotal;

    _.each(links, function (link) {
      link.maxSameHalf = Math.floor(maxSame / 3);
    });

    // FORCE

    var width = 960,
      height = 500;

    var force = d3.layout.force()
      .nodes(nodes)
      .links(links)
      .size([width, height])
      .linkDistance(400)
      .charge(-2000)
      .on('tick', tick)
      .start();

    var svg = d3.select("body").append("svg")
      .attr("width", width)
      .attr("height", height);

    var path = svg.append("g").selectAll("path")
      .data(force.links())
      .enter().append("path")
      .style("stroke", function (d) {
        return d3.scale.category20().range()[d.sameIndex - 1];
      });

    var circle = svg.append("g").selectAll("circle")
      .data(force.nodes())
      .enter().append("circle")
      .attr("r", 30)
      .call(force.drag);

    // TICK

    function tick(d) {
      circle.attr("transform", function (d) {
        return "translate(" + d.x + "," + d.y + ")";
      });
      path.attr("d", linkArc);
    }


    function calcTranslationExact(targetDistance, source, target) {

      var x1_x0 = target.x - source.x,
        y1_y0 = target.y - source.y,
        x2_x0, y2_y0;
      if (y1_y0 === 0) {
        x2_x0 = 0;
        y2_y0 = targetDistance;
      } else {
        let angle = Math.atan((x1_x0) / (y1_y0));
        x2_x0 = -targetDistance * Math.cos(angle);
        y2_y0 = targetDistance * Math.sin(angle);
      }
      return {
        dx: x2_x0,
        dy: y2_y0
      };
    }
    // some more info: http://stackoverflow.com/questions/11368339/drawing-multiple-edges-between-two-nodes-with-d3
    function linkArc(d) {
      var dx = (d.target.x - d.source.x),
        dy = (d.target.y - d.source.y),
        dr = Math.sqrt(dx * dx + dy * dy),
        arc = 0;

      // 上下不一样的间隔
      let dis1 = calcTranslationExact(d.sameIndex * 12, d.source, d.target)
      let dis2 = calcTranslationExact(-(d.sameIndex - Math.ceil(d.sameTotalHalf)) * 12, d.source, d.target)

      let dx1 = dis1.dx;
      let dy1 = dis1.dy

      let dx2 = dis2.dx;
      let dy2 = dis2.dy

      // 表示奇数的时候,中间的那条
      if (Math.ceil(d.sameTotalHalf) === d.sameIndex && d.sameUneven === true) {
        dx1 = 0;
        dx2 = 0;
        dy1 = 0;
        dy2 = 0;
      }

      if (d.sameArcDirection === 0) {
        return "M" + (d.source.x + dx1) + "," + (d.source.y + dy1) + "A" + arc + "," + arc + " 0 0," + 0 + " " + (d.target.x + dx1) + "," + (d.target.y + dy1);
      } else {
        return "M" + (d.source.x + dx2) + "," + (d.source.y + dy2) + "A" + arc + "," + arc + " 0 0," + 0 + " " + (d.target.x + dx2) + "," + (d.target.y + dy2);
      }
    }
  </script>
</body>


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM