• D3力布图绘制--节点间的多条关系连接线的方法(转)


    在项目中遇到这样的场景,在使用D3.js绘制力布图的过程中,需要在2个节点间绘制多条连接线,找到一个不错的算法,在此分享下。
    效果图:

    HTML中要连接

    <!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>
    </head>
    <body>
    </body>
    
        // 下面是JS部分的代码,使用前请连接`https://d3js.org/d3.v3.min.js` 和 `https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js`
        var nodes = [{}, {}, {}, {}, {}, {}];
        var links = [      
            // one link
            { source: 0, target: 1 },
          
            // two links
            { source: 2, target: 1 }, 
          	{ source: 1, target: 2 },
          
            //three links
            { source: 3, target: 2 }, 
          	{ source: 2, target: 3 }, 
            { source: 2, target: 3 },
          
            // four links
            { source: 3, target: 4 }, 
            { source: 3, target: 4 }, 
            { source: 3, target: 4 }, 
          	{ source: 3, target: 4 },
          
            // five links
            { source: 4, target: 5 }, 
            { source: 4, target: 5 }, 
            { source: 4, target: 5 }, 
            { source: 4, target: 5 }, 
            { source: 4, target: 5 }
        ];
    
        // DATA FORMATTING
        _.each(links, function(link) {
            var same = _.where(links, {
                'source': link.source,
                'target': link.target
            });
            var sameAlt = _.where(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));
            });
    
            let sameStandard = sameAll[0];
            let sourceStandard = sameStandard.source;
            let targetStandard = sameStandard.target;
            _.each(sameAll,function(s,i){
              if(s.source === targetStandard && s.target === sourceStandard && s.sameTotal > 1){
                s.sameArcDirection = s.sameArcDirection === 0 ? 1 : 0
              }
            })
        });  
    
        var maxSame = _.chain(links)
            .sortBy(function(x) {
                return x.sameTotal;
            })
            .last()
            .value().sameTotal;
    
        _.each(links, function(link) {
            link.maxSameHalf = Math.floor(maxSame / 2);
        });    
    
        var width = 960,
            height = 500;
        var force = d3.layout.force()
            .nodes(nodes)
            .links(links)
            .size([width, height])
            .linkDistance(100)
            .charge(-200)
            .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", 8)
            .call(force.drag);
    
        function tick(d) {
            circle.attr("transform", function(d) {
                return "translate(" + d.x + "," + d.y + ")";
            });
            path.attr("d", linkArc);
        }
    
        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),
                unevenCorrection = (d.sameUneven ? 0 : 0.5),
                arc = ((dr * d.maxSameHalf) / (d.sameIndexCorrected - unevenCorrection));
    
            if (d.sameMiddleLink) {
                arc = 0;
            }
    
            return "M" + d.source.x + "," + d.source.y + "A" + arc + "," + arc + " 0 0," + d.sameArcDirection + " " + d.target.x + "," + d.target.y;
        }
    
    

    本文转自: http://bl.ocks.org/thomasdobber/9b78824119136778052f64a967c070e0

  • 相关阅读:
    进程池线程池
    线程与其操作方法
    生产者消费者模型
    Java反射机制详解
    ajax跨域原理以及解决方案
    数据库连接池的选择 Druid
    新目标
    让webstorm支持avalon语法自动补全
    使用IDEA和gradle搭建Spring MVC和MyBatis开发环境
    使用IDEA自带的rest client参数传递乱码问题
  • 原文地址:https://www.cnblogs.com/webhmy/p/10906268.html
Copyright © 2020-2023  润新知