zoukankan      html  css  js  c++  java
  • 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- 3;
        }
    
        circle {
          stroke: white;
          stroke- 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>
    
  • 相关阅读:
    Redis和Memcache的区别分析
    javascript 与jquery为每个p标签增加onclick方法
    repeater单双行颜色不同,gridview repeater DataList 鼠标经过改变背景颜色
    sql 错误提示
    .net获取select控件中的文本内容
    您试图从目录中执行CGI、ISAPI 或其他可执行程序,但该目录不允许执行程序
    html5 调用摄像头
    openfire配置MSSQL说明(数据库设置)
    Openfire 的安装和配置
    JS 等前端学习。
  • 原文地址:https://www.cnblogs.com/webhmy/p/12464082.html
Copyright © 2011-2022 走看看