美文网首页儿童启迪诗集
D3学习系列(二) 弦图绘制

D3学习系列(二) 弦图绘制

作者: 卷福不卷 | 来源:发表于2017-03-31 22:14 被阅读0次
    Chord

    「什么时候用弦图」

    Chord Diagram主要是用来表示多个节点之间的关系,假设我们要表示5个节点之间的关系,那么输入的矩阵是下面这个样子,且必须是方阵。节点A的长度是元素A所在行的总和,就是(A, A)、(A, B)、(A, C)、(A, D)、(A, E)的和。图中C与D之间的弦表示C和D相关,与C相接的弧长实际上是(C, D)的值。

    「绘制弦图」

    导入初始数据

    var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港"  ];
    var population = [
              [ 1000,  3045 , 4567 , 1234 , 3714 ],
              [ 3214,  2000 , 2060 , 124  , 3234 ],
              [ 8761,  6545 , 3000 , 8045 , 647  ],
              [ 3211,  1067 , 3214 , 4000 , 1006 ],
              [ 2146,  1034 , 6745 , 4764 , 5000 ]
            ];
    

    布局(转换数据)

    // 弦布局
    var chord_layout = d3.layout.chord()
                            .padding(0.03)
                            .sortSubgroups(d3.descending)
                            .matrix(population);
    // 布局转化数据
    var groups = chord_layout.groups();
    var chords = chord_layout.chords();
    

    padding(0.03)表示弧与弧之间的间隔,population是之前输入的人口数据。经过布局之后会生成两块,一块是groups,表示节点;另一块是chords,表示弦(连线),chords里面还会分source与target,表示连线的两端。

    绘制画布SVG

    // svg画布
    var width = 600;
    var height = 600;
    var svg = d3.select("body")
                .append("svg")
                .attr("width",width)
                .attr('height', height)
                .append("g")
                .attr('transform', 'translate(' + width/2 + "," + height/2 + ")");
    var color20 = d3.scale.category20();
    

    先创建一个SVG元素,里面添加一个g元素同时设置平移属性(用来确定弦图的中心)。然后再在g元素添加2个g元素,分别用来装节点与弦,结构如下所示:

    <svg>
     --<g>
     ---- <g>``</g>
     ---- <g>``</g>
     --</g>
    </svg>

    绘制节点(弧)

    // 弧生成器
    var innerRadius = width/2 * 0.7;
    var outerRadius = innerRadius * 1.1;
    var outer_arc = d3.svg.arc()
                        .innerRadius(innerRadius)
                        .outerRadius(outerRadius);
    // 绘制节点
    var g_outer = svg.append("g");
    g_outer.selectAll("path")
            .data(groups)
            .enter()
            .append("path")
            .style("fill",function(d) {
                return color20(d.index);
            })
            .style("stroke",function(d) {
                color20(d.index);
            })
            .attr("d",outer_arc)   // 此处调用了弧生成器
            ;
    // 节点文字
    g_outer.selectAll("text")
            .data(groups)
            .enter()
            .append("text")
            .each(function(d,i) {   // 对每个绑定的数据添加两个变量
                d.angle = (d.startAngle + d.endAngle) / 2;
                d.name = city_name[i];
            })
            .attr("dy",".35em")
            .attr('transform', function(d) {    // 平移属性
                var result =  "rotate(" + (d.angle*180/Math.PI) + ")";
                result += "translate(0," + -1 * (outerRadius + 10) + ")";
                if (d.angle > Math.PI * 3 / 4 && d.angle < Math.PI * 5 / 4 )
                    result += "rotate(180)";
                return result;
            })
            .text(function(d) {
                return d.name;
            });
    

    在标记文字的地方要注意:

    1. each():表示对任何一个绑定数据的元素,都执行后面的无名函数 function(d,i) ,计算文字的角度与内容
    2. transform():不仅需要考虑文字的旋转角度与平移距离,还要考虑如果文字在下方是会是倒写的情况。

    生成如下图:


    绘制连线(弦)

    // 弦生成器
    var inner_chord = d3.svg.chord()
                            .radius(innerRadius);
    
    // 绘制内部弦
    var g_inner = svg.append("g")
                    .attr("class","chord");
    
    g_inner.selectAll("path")
            .data(chords)
            .enter()
            .append("path")
            .attr("d",inner_chord)  // 调用弦的路径值
            .style("fill",function(d) {
                return color20(d.source.index);
            })
            .style("opacity",1)
            ;
    

    这样就生成了首页的弦图,但是当数据多了之后,会看不清节点与节点之间的关系,我们可以添加一些交互式的操作解决。如当鼠标移到该节点,只会显示与该节点相接的弦,其他的会被隐藏。这里我们定义一个fade函数,并在节点(弧)上通过mouseovermouseout添加动作

    function fade(opacity){
        return function(g,i){
            g_inner.selectAll("path")
                    .filter(function(d) {
                        return d.source.index != i && d.target.index != i;
                    })
                    .transition()
                    .style("opacity",opacity);
        }
    }
    
    g_outer.selectAll("path")
            .on("mouseover",fade(0.0))  // 0.0完全透明
            .on("mouseout",fade(1.0))   // 1.0完全不透明
            ;
    

    效果如图:

    「源代码」

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8">
            <title></title>
            <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
            <style>
                .chord path{
                    fill-opacity: 0.67;
                    stroke: #000;
                    stroke-width: 0.5px;
                }
            </style>
        </head>
    
        <body>
            <script>
            // 初始数据
            var city_name = [ "北京" , "上海" , "广州" , "深圳" , "香港"  ];
            var population = [
                      [ 1000,  3045 , 4567 , 1234 , 3714 ],
                      [ 3214,  2000 , 2060 , 124  , 3234 ],
                      [ 8761,  6545 , 3000 , 8045 , 647  ],
                      [ 3211,  1067 , 3214 , 4000 , 1006 ],
                      [ 2146,  1034 , 6745 , 4764 , 5000 ]
                    ];
    
            // 弦布局
            var chord_layout = d3.layout.chord()
                                    .padding(0.03)
                                    .sortSubgroups(d3.descending)
                                    .matrix(population);
    
            // 布局转化数据
            var groups = chord_layout.groups();
            var chords = chord_layout.chords();
            console.log(groups);
            console.log(chords);
    
            // svg画布
            var width = 600;
            var height = 600;
            var svg = d3.select("body")
                        .append("svg")
                        .attr("width",width)
                        .attr('height', height)
                        .append("g")
                        .attr('transform', 'translate(' + width/2 + "," + height/2 + ")");
    
            var color20 = d3.scale.category20();
    
            // 弧生成器
            var innerRadius = width/2 * 0.7;
            var outerRadius = innerRadius * 1.1;
            var outer_arc = d3.svg.arc()
                                .innerRadius(innerRadius)
                                .outerRadius(outerRadius);
    
            // 绘制节点
            function fade(opacity){
                return function(g,i){
                    g_inner.selectAll("path")
                            .filter(function(d) {
                                return d.source.index != i && d.target.index != i;
                            })
                            .transition()
                            .style("opacity",opacity);
                }
            }
    
            var g_outer = svg.append("g");
            g_outer.selectAll("path")
                    .data(groups)
                    .enter()
                    .append("path")
                    .style("fill",function(d) {
                        return color20(d.index);
                    })
                    .style("stroke",function(d) {
                        color20(d.index);
                    })
                    .attr("d",outer_arc)   // 此处调用了弧生成器
                    .on("mouseover",fade(0.0))  // 0.0完全透明
                    .on("mouseout",fade(1.0))   // 1.0完全不透明
                    ;
    
            g_outer.selectAll("text")
                    .data(groups)
                    .enter()
                    .append("text")
                    .each(function(d,i) {   // 对每个绑定的数据添加两个变量
                        d.angle = (d.startAngle + d.endAngle) / 2;
                        d.name = city_name[i];
                    })
                    .attr("dy",".35em")
                    .attr('transform', function(d) {    // 平移属性
                        var result =  "rotate(" + (d.angle*180/Math.PI) + ")";
                        result += "translate(0," + -1 * (outerRadius + 10) + ")";
                        if (d.angle > Math.PI * 3 / 4 && d.angle < Math.PI * 5 / 4 )
                            result += "rotate(180)";
                        return result;
                    })
                    .text(function(d) {
                        return d.name;
                    });
    
            // 弦生成器
            var inner_chord = d3.svg.chord()
                                    .radius(innerRadius);
    
            // 绘制内部弦,一共有5*5=25条
            var g_inner = svg.append("g")
                            .attr("class","chord");
    
            g_inner.selectAll("path")
                    .data(chords)
                    .enter()
                    .append("path")
                    .attr("d",inner_chord)  // 调用弦的路径值
                    .style("fill",function(d) {
                        return color20(d.source.index);
                    })
                    .style("opacity",1)
                    ;
            </script>
        </body>
    </html>
    
    

    相关文章

      网友评论

        本文标题:D3学习系列(二) 弦图绘制

        本文链接:https://www.haomeiwen.com/subject/wsavottx.html