• D3力布图绘制--在曲线路径上添加文本标记


    今天遇到一个在曲线路径上标识文本标记的问题,找到一个比较好的解决思路,在这里分享下:

    使用d3建立的Force Layout,加上自定义的箭头形状,将多条连接线线改成弧线(https://www.cnblogs.com/webhmy/p/10906268.html)。现需沿弧线加上文字

    var edgelabels = svg.selectAll(".edgelabel")
            .data(dataset.edges)
            .enter()
            .append('text')
            .attr(...);
    
        edgelabels.append('textPath')
            .attr('xlink:href',function(d,i) {return '#edgepath'+i})
            .text(function(d,i){return 'label '+i});
    

    使用text元素定义文字,然后为text元素添加textPath子元素,通过指定textPath的属性,将文字的定位与其他路径关联起来。定义弧线路径需要添加id属性

    var path = svg.append('svg:g').selectAll('path')
            .data(force.links())
            .enter().append('svg:path')
            .attr('class', function(d) { return 'link ' + d.type; })
            .attr(...)
            .attr('id', function(d,i){return 'edgepath'+i;});
    

    问题出在SVG文字路径的方向性上。注意图中上下颠倒的"68万",因为它所从属的有向弧是自右向左从『渠道』连向『资金』,文字也跟着上下左右颠倒了。
    要想让文字的方向正过来,弧的方向需要人为的反过来。这本身并不困难,只要在指定弧的路径的时候检查起点和终点的x值,始终把较小的一方作为起点就好。

    然而如果直接这样做,图中的箭头也会反过来,图的含义就变了

    先回顾一下箭头的画法,是定义了一个名为"end"的小三角形标记(marker),然后将这个marker指定为弧的结束标记("marker-end"属性):

    // build the arrow.
    svg.append("svg:defs").selectAll("marker")
        .data(["end"])
      .enter().append("svg:marker")
        .attr("id", String)
        .attr(...)
      .append("svg:path")
        .attr("d", "M0,-5L10,0L0,5");
    
    // add the links and the arrows
    var path = svg.append("svg:g").selectAll("path")
        .data(force.links())
      .enter().append("svg:path")
        .attr("class", function(d) { return "link " + d.type; })
        .attr("marker-end", "url(#end)");
    

    现在因为必须把弧反过来画,就得在起点画一个形状相反的marker,来冒充原来的end marker。所以稍微修改一下代码,增加一个新的marker:

    // build the arrows
    var defs = svg.append('svg:defs');
    defs.append('svg:marker')
            .attr('id', 'end')
            .attr(...)
        .append('svg:path')
            .attr('d', 'M0,-5L10,0L0,5');
    
    defs.append('svg:marker')
            .attr('id', 'start')
            .attr(...)
        .append('svg:path')
            .attr('d', 'M0,0L10,-5L10,5');
    

    因为d3的Force Layout自带进场动画,弧的开始和结束点在动画进行中会一直变化,需要动态决定绘制方向和使用marker-start还是marker-end属性,就不能再像例子中那样直接静态指定,而是要放到负责动画的tick()函数中:

    function tick() {
        path.attr('d', function(d) {
            var x1 = ..., y1 = ...,
                x2 = ..., y2 = ...,
                r = ...;
            if (x1 < x2) {
                return 'M' +
                    x1 + ',' + y1 + 'A' +
                    r + ',' + r + ' 0 0,1 ' +
                    x2 + ',' + y2;
            } else {
                return 'M' +
                    x2 + ',' + y2 + 'A' +
                    r + ',' + r + ' 0 0,0 ' +
                    x1 + ',' + y1;
            }
        })
        .attr('marker-end', function(d) {
            if (d.source.x < d.target.x) {
                return 'url(#end)';
            }
            return '';
        })
        .attr('marker-start', function(d) {
            if (d.source.x >= d.target.x) {
                return 'url(#start)';
            }
            return '';
        });
        ...
    }
    

    这样得到的结果是

    文字的上下左右方向正确了。但对所有伪装成功的弧而言,文字都被标记在了弧的内侧。这是因为文字默认的定位锚点(anchor point)是按基线(baseline)算起,因此始终会在弧的上方。有了上面的经验,如法炮制,在tick()函数中加上一段:

    function tick() {
        path.attr('d', function(d) {
            ...
        })
        .attr('marker-end', function(d) {
            ...
        })
        .attr('marker-start', function(d) {
            ...
        });
    
        edgelabels.attr('dominant-baseline', function(d) {
            if (d.source.x < d.target.x) {
                return 'text-after-edge';
            }
            return 'text-before-edge';
        });
        ...
    }
    

    总结

    这里的主要思路是,文字是跟随线的方向。如果是反向的弧线,字会颠倒,为了使文字显示正确,这里使用了一个hack
    对箭头做了个处理,每条线都加了开始和结束的箭头
    根据数据做驱动,当源数据的x坐标小于目标数据的x坐标的时候,显示end箭头

    当源数据的x坐标大于目标数据的x坐标的时候,显示start箭头

    这个思路很好的解决了我的问题,因此分享下,本文转自 https://zhuanlan.zhihu.com/p/20706807

  • 相关阅读:
    小刘同学的第一百五十二篇日记
    小刘同学的第一百五十一篇日记
    小刘同学的第一百五十篇日记
    小刘同学的第一百五十篇日记
    小刘同学的第一百四十九篇日记
    小刘同学的第一百四十八篇日记
    小刘同学的第一百四十七篇日记
    小刘同学的第一百四十六篇日记
    小刘同学的第一百四十五篇博文
    自定义CollectionViewLayout
  • 原文地址:https://www.cnblogs.com/webhmy/p/10985540.html
Copyright © 2020-2023  润新知