在D3圆形包布局中沿着圆形排列文本

5
我正在尝试在一个“圆形包装布局”中为一些圆标记文本,使其沿着圆自身流动。以下是一个实验性的jsfiddle

enter image description here

正如您所看到的,可以在圆周上呈现文本,以其顶部为中心。尽管浏览器对曲线SVG文本的渲染效果很糟糕。但是假设我们不关心它。

这里是另一个jsfiddle

enter image description here

我想在这个图表上放置曲线标签,并满足以下条件:
  • 圆表示一个省份(只有depth==1)(BRITISH COLUMBIA,ALBERTA等)
  • 该省所有子节点的大小之和(即分配的议席数)大于5。
  • 省份的名称应全部大写。
您可以在代码中看到我的一些尝试。我已经尝试了几个小时。我的主要问题是,圆圈现在在X Y空间的某个地方,而在第一个jsfiddle中,所有圆圈的中心都在坐标系原点。
也许你可以通过重新审视这个问题来帮助我。
基础数据基于这个表:

enter image description here

(注意:这与我前几天提出的'将圆包作为D3力布局的节点'问题有些相关,但这是一个独立的实验。)

我决定使用常规的SVG弧线而不是d3.svg.arc()。我仍然认为这是正确的决定。然而,现在我有了这个::) jsfiddle

enter image description here


一个快速的提示:由于将cx和cy属性分配给var arc,所以会抛出异常。如果您注释掉这两个属性,那么标签就会全部显示在左上角(您提到的坐标问题)。这可能是显而易见的,并且您已经通过实验知道了这一点,但作为一个理智的检查,我想指出来。 - FernOfTheAndes
是的,这是一种“开发”示例,所以这很正常。 :) 我知道所有文本都捆绑在那个角落里。这实际上是我的问题 - 如何将其放在正确的位置。感谢您提醒我,现在其他人也会知道发生了什么... :) - VividD
关于属性 startOffset = "<length>"文档中提到:如果给出的是一个除百分比以外的 <length>(这就是你所给出的),那么 startOffset 表示在当前用户坐标系中沿路径测量的距离。按照文档,我们可以找到 路径上的距离。目前看来,这似乎是 textPath 元素和指定 x、y 坐标之间唯一的联系。希望这不是一条兔子洞。 - FernOfTheAndes
我认为这里的d3.svg.arc()是一个障碍。它不能被定义为除了在(0,0)之外的中心。在我看来,切换到常规的SVG弧线会起作用。 - VividD
1
我在你修复之前迅速fork了你最新的fiddle!我会将其存储以便可能绘制有关巴西Cangaço的任何未来故事。这里是一张著名图片关于那些家伙 :) 抱歉,只是一点轻松...工作太辛苦了... - FernOfTheAndes
显示剩余2条评论
2个回答

8

注意(因为我正在回答我的问题):如果您已经花时间解决了这个问题并找到了另一种解决方案,请发布它,我将接受您的答案。感谢@FernOfTheAndes参与找到这个解决方案的过程,因为这个过程充满了使用svg弧线的痛苦和煎熬。

这是解决方案的jsfiddle

enter image description here

正如评论中提到的,关键部分是将弧形生成为普通的svg弧形,而不是通过d3.svg.arc()。

SVG定义弧线的规则很清晰,但有点难以管理。这里有一个用于交互式探索svg弧形语法的工具

此外,在定义正确的弧线过程中,这两个函数对我很有帮助:

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    var angleInRadians = (angleInDegrees-90) * Math.PI / 180.0;
    return {
        x: centerX + (radius * Math.cos(angleInRadians)),
        y: centerY + (radius * Math.sin(angleInRadians))
    };
}

function describeArc(x, y, radius, startAngle, endAngle){
    var start = polarToCartesian(x, y, radius, endAngle);
    var end = polarToCartesian(x, y, radius, startAngle);
    var arcSweep = endAngle - startAngle <= 180 ? "0" : "1";
    var d = [
        "M", start.x, start.y, 
        "A", radius, radius, 0, 1, 1, end.x, end.y
    ].join(" ");
    return d;       
}

这是实际直接生成曲线标签的代码:
var arcPaths = vis.append("g")
    .style("fill","navy");
var labels = arcPaths.append("text")
    .style("opacity", function(d) {
        if (d.depth == 0) {
            return 0.0;
        }
        if (!d.children) {
            return 0.0;
        }
        var sumOfChildrenSizes = 0;
        d.children.forEach(function(child){sumOfChildrenSizes += child.size;});
        //alert(sumOfChildrenSizes);
        if (sumOfChildrenSizes <= 5) {
            return 0.0;
        }
        return 0.8;
    })
    .attr("font-size",10)
    .style("text-anchor","middle")
    .append("textPath")
    .attr("xlink:href",function(d,i){return "#s"+i;})
    .attr("startOffset",function(d,i){return "50%";})
    .text(function(d){return d.name.toUpperCase();})

幸运的是,在弧线上居中文本只需要设置正确的属性。


我会在这里发布。与你的情况类似,还有其他各种琢磨。为了让文本仍然保持正确的方向,同时又在圆形内部,我将路径半径缩小了一点,小于圆形半径。我试图使用“textLength”属性来压缩文本以适应,但它似乎不适用于<textpath>元素。http://jsfiddle.net/2hM3s/4/ - AmeliaBR
@AmeliaBR 我刚刚把 .style("font-size",12) 改成了 .attr("font-size",12),结果就是这样的:(http://jsfiddle.net/VividD/vCG6w/)(错误一直存在于我的例子中) - VividD
@AmeliaBR 我认为你应该将这个作为答案发布,这样未来的读者可以看到两种不同方法。 - VividD
我的微小贡献是为了 @VividD 的最新 fiddle:根据不同的用户选择移动标签。可以通过过渡和其他修饰来改进,但你已经有了想法。 - FernOfTheAndes
.style("font-size",12).attr("font-size",12)的主要区别在于属性样式可以被CSS覆盖。此外,似乎第一个被解释为点大小,第二个被解释为像素大小(如果您没有指定单位)。这并不改变长名称在较小圆圈中被截断的方式--对于文本和tspan元素,您可以通过设置textLength属性来强制文本压缩到给定长度,而不管字体大小如何。但是,您可以计算路径长度和文本长度,并使用它来直接设置字体大小和字距。 - AmeliaBR
显示剩余2条评论

2

刚刚在更新VividD的代码。

describeArc函数对于小于180度的弧线没有正确工作,并且在函数和调用时翻转了起始和结束变量。

这里是一个更新后的describeArc函数,可以处理所有类型的弧线,包括小弧线和倒置弧线:

function describeArc(x, y, radius, startAngle, endAngle) {
  var start = polarToCartesian(x, y, radius, startAngle);
  var end = polarToCartesian(x, y, radius, endAngle);
  var arcLength = endAngle - startAngle;
  if (arcLength < 0) arcLength += 360;
  var longArc = arcLength >= 180 ? 1 : 0;
  var d = [
    "M", start.x, start.y,
    "A", radius, radius, 0, longArc, 1, end.x, end.y
  ].join(" ");
  return d;
}

通过这个更改,应该使用反转后的起始和结束值来调用函数,这样更有意义:

describeArc(d.x, d.y, d.r, -160, 160);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接