【问题标题】:D3 JS upside down path textD3 JS颠倒路径文本
【发布时间】:2013-05-21 14:29:37
【问题描述】:

在这种情况下是否可以显示不倒置的文本?

http://jsfiddle.net/paulocoelho/Hzsm8/1/

代码:

var cfg = {
    w:400,
    h:400
};

var g = d3.select("#testdiv").append("svg").attr("width", cfg.w).attr("height", cfg.h).append("g")

var arct = d3.svg.arc()
        .innerRadius(cfg.h / 5)
        .outerRadius(cfg.h / 3)
        .startAngle(Math.PI/2)
        .endAngle(Math.PI*1.5);

var path = g.append("svg:path")
    .attr("id","yyy")
    .attr("d", arct)
    .style("fill","blue")
    .attr("transform", "translate("+cfg.w/2+","+cfg.h/6+")");

var text = g.append("text")
            .style("font-size",30)
            .style("fill","#F8F8F8")
            .attr("dy",35)
            .append("textPath")
            .attr("xlink:href","#yyy")
            .attr("startOffset",50)
            .text("some text")
    ;

【问题讨论】:

    标签: svg d3.js


    【解决方案1】:

    Nadieh Bremer 的 Placing Texts on Arcs with D3.js 就是一个很好的例子。一个包含许多图像的冗长博客,摘录如下:

    翻转下半部分的文本

    你可能已经觉得它已经完成了。但我发现下半部分的标签是颠倒的,很难阅读。如果这些标签被翻转,我会更喜欢它,这样我就可以再次从左到右阅读它们。

    为此,我们需要沿下半部分切换当前圆弧路径的起点和终点坐标,以便从左到右绘制它们。此外,扫描标志必须设置为 0 才能获得从左到右以逆时针方式运行的弧

    所以对于最后一幕,让我们在 .each() 语句中添加几行代码

    //Create the new invisible arcs and flip the direction for those labels on the bottom half
    .each(function(d,i) {
        //Search pattern for everything between the start and the first capital L
        var firstArcSection = /(^.+?)L/;    
    
        //Grab everything up to the first Line statement
        var newArc = firstArcSection.exec( d3.select(this).attr("d") )[1];
        //Replace all the commas so that IE can handle it
        newArc = newArc.replace(/,/g , " ");
    
        //If the end angle lies beyond a quarter of a circle (90 degrees or pi/2) 
        //flip the end and start position
        if (d.endAngle > 90 * Math.PI/180) {
            var startLoc    = /M(.*?)A/,        //Everything between the capital M and first capital A
                middleLoc   = /A(.*?)0 0 1/,    //Everything between the capital A and 0 0 1
                endLoc      = /0 0 1 (.*?)$/;   //Everything between the 0 0 1 and the end of the string (denoted by $)
            //Flip the direction of the arc by switching the start and end point (and sweep flag)
            var newStart = endLoc.exec( newArc )[1];
            var newEnd = startLoc.exec( newArc )[1];
            var middleSec = middleLoc.exec( newArc )[1];
    
            //Build up the new arc notation, set the sweep-flag to 0
            newArc = "M" + newStart + "A" + middleSec + "0 0 0 " + newEnd;
        }//if
    
        //Create a new invisible arc that the text can flow along
        svg.append("path")
            .attr("class", "hiddenDonutArcs")
            .attr("id", "donutArc"+i)
            .attr("d", newArc)
            .style("fill", "none");
    });
    

    与上一节相比,唯一改变的是添加了 if 语句。要翻转开始和结束位置,我们可以使用更多的正则表达式。当前起始 x 和 y 位置由大写字母 M 和大写字母 A 之间的所有内容给出。当前半径由大写字母 A 和 x 轴旋转的 0 0 1 之间的所有内容表示,大圆弧标志和扫旗。最后,结束位置由 0 0 1 和字符串结尾之间的 all 给出(在正则表达式中用 $ 表示)。

    因此,我们将所有片段保存在不同的变量中,并使用 if 语句中的最后一行切换了开始和结束位置来构建/替换 newArc。

    textPath 部分需要稍作改动。对于下半部分弧线,dy 属性不应将标签抬高到弧形路径之上,而应将标签降低到弧形路径之下。所以我们需要一个小的 if 语句,它会产生两个不同的 dy 值。 (为了能够在 if 语句中使用 d.endAngle,我在 .data() 步骤中将 donutData 替换为 pie(donutData)。您仍然可以使用 d.data 而不是仅使用 d 来引用数据本身可以在 .text() 行代码中看到。)

    //Append the label names on the outside
    svg.selectAll(".donutText")
        .data(pie(donutData))
       .enter().append("text")
        .attr("class", "donutText")
        //Move the labels below the arcs for those slices with an end angle greater than 90 degrees
        .attr("dy", function(d,i) { return (d.endAngle > 90 * Math.PI/180 ? 18 : -11); })
       .append("textPath")
        .attr("startOffset","50%")
        .style("text-anchor","middle")
        .attr("xlink:href",function(d,i){return "#donutArc"+i;})
        .text(function(d){return d.data.name;});
    

    【讨论】:

      【解决方案2】:

      看起来当 d3 创建一个填充弧时,它实际上创建了一个填充路径形状,它总是(?)从右侧开始并顺时针进行 - 即使你颠倒了 startAngle 和 endAngle。

      如果您手动创建自己的圆弧路径,并将文本放在上面,您可以让它做正确的事情。

      var cfg = {
          w:400,
          h:400
      };
      
      var g = d3.select("#testdiv").append("svg").attr("width", cfg.w).attr("height", cfg.h).append("g")
      
      var arct = d3.svg.arc()
              .innerRadius(cfg.h / 5)
              .outerRadius(cfg.h / 3)
              .startAngle(Math.PI/2)
              .endAngle(Math.PI*1.5);
      
      var path = g.append("svg:path")
          .attr("id","yyy")
          .attr("d", arct)
          .style("fill","blue")
          .attr("transform", "translate("+cfg.w/2+","+cfg.h/6+")");
      
      // Radius of line text sits on. A value of 3.5 makes it slightly closer to the
      // outer radius (so text is placed in the middle of the blue line).
      var textpathRadius = (cfg.h / 3.5);
      
      // Make a path for the text to sit on that goes in an anti-clockwise direction.
      var textpath = g.append("svg:path")
          .attr("id","zzz")
          .style("display","none")
          .attr("d", "M -"+textpathRadius+" 0 A "+textpathRadius+" "+textpathRadius+" 0 0 0 "+textpathRadius+" 0")
          .attr("transform", "translate("+cfg.w/2+","+cfg.h/6+")");
      
      var text = g.append("text")
                  .style("font-size",30)
                  .style("fill","#F8F8F8")
                  .attr("dy",0)
                  .append("textPath")
                  .attr("xlink:href","#zzz")
                  .attr("startOffset","50%")
                  .style("text-anchor","middle")
                  .text("some text");
      

      我以前从未使用过 d3,因此可能有一种更简单或更简洁的方式来完成我所做的事情。但至少它应该给你一个开始的地方。

      更新小提琴:http://jsfiddle.net/3DfVD/

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-06-18
        • 2021-04-15
        • 2018-06-21
        • 1970-01-01
        • 2020-04-09
        • 2014-08-03
        相关资源
        最近更新 更多