如何使用 d3.js 在弧内沿 textPath 居中(水平和垂直)文本?

Posted

技术标签:

【中文标题】如何使用 d3.js 在弧内沿 textPath 居中(水平和垂直)文本?【英文标题】:How to center (horizontal and vertical) text along an textPath inside an arc using d3.js? 【发布时间】:2013-12-25 04:10:48 【问题描述】:

在玩了一段时间 d3.js 并查看了很多示例之后,我能够绘制多条弧线。每一个都以特定的度数和给定的半径开始和结束。

var dataset = 
    "2":["degree1":0,                 "degree2":1.5707963267949,
          "label":"Sample Text Test",    
         "degree1":1.5707963267949,   "degree2":3.1415926535898,
          "label":"Lorem ipsum sample text",
         "degree1":3.1415926535898,   "degree2":4.7123889803847,
          "label":"Sample Text Text",
         "degree1":4.7123889803847,   "degree2":6.2831853071796,
          "label":"Lorem ipsum"],
    "1":["degree1":0,                 "degree2":3.1415926535898,
          "label":"Sample",
         "degree1":3.1415926535898,   "degree2":6.2831853071796,
          "label":"Text"],
    "0":["degree1":0,                 "degree2":6.2831853071796,
          "label":""]
    ,
    width   = 450,
    height  = 450,
    radius  = 75;

// Helper methods
var innerRadius = function(d, i, j) 
    return 1 + radius * j;
;

var outerRadius = function(d, i, j) 
    return radius * (j + 1);
;

var startAngle = function(d, i, j) 
    return d.data.degree1;
;

var endAngle = function(d, i, j) 
    return d.data.degree2;
;

var pie = d3.layout.pie()
    .sort(null);

var arc = d3.svg.arc()
    .innerRadius(innerRadius)
    .outerRadius(outerRadius)
    .startAngle(startAngle)
    .endAngle(endAngle);

var svg = d3.select('body').append('svg')
    .attr('width', width)
    .attr('height', height)
    .append('g')
    .attr('transform', 'translate(' + (width >> 1) + ',' + (height >> 1) + ')');

var level = svg.selectAll('g')
    .data(function(d) 
        return d3.values(dataset);
    )
    .enter()
    .append('g');

var entry = level.selectAll('g')
    .data(function(d, i) 
        return pie(d);
    )
    .enter()
    .append('g');

entry.append('path')
    .attr('fill', '#aaa')
    .attr('d', arc)
    .attr('id', function(d, i, j) 
        return 'arc' + i + '-' + j;
    );

var label = entry.append('text')
    .style('font-size', '20px')
    .attr('dx', function(d, i, j) 
        return Math.round((d.data.degree2 - d.data.degree1) * 180 / Math.PI);
    )
    .attr('dy', function(d, i, j) 
        return ((radius * (j + 1)) - (1 + radius * j)) >> 1;
    );


label.append('textPath')
    .attr('xlink:href', function(d, i, j) 
        return '#arc' + i + '-' + j;
    )
    .style('fill', '#000')
    .text(function(d) 
        return d.data.label;
    );

见http://jsfiddle.net/3FP6P/2/:

但还是有一些问题:

    如何在由 innerRadius、outerRadius、startAngle 和 endAngle 描述的弧内沿任意长度的文本路径居中(水平和垂直)文本? 文本有时会显示为粗体,有时不是。为什么? 字符间距似乎与写在 .有些字母像其他字母一样粘在一起。为什么? 字母不直接位于路径上。有些似乎有一点向上或向下滑动。为什么?

【问题讨论】:

【参考方案1】:

垂直对齐

您可以使用另一个半径为(innerRadius + outerRadius) / 2 的弧并将其用作标签的textPath

请注意,即使您设置了innerRadius == outerRadius,D3 也会绘制一条顺时针然后逆时针移动的路径,使其自身翻倍。这在试图找出路径的水平中心时变得很重要:它位于25%75% 点,而0%50% 点位于路径的两个尖端弧线。

水平对齐

在文本元素上使用text-anchor: middle,并在textPath 上将startOffset 设置为25%(或75%)。

Demo.

这比手动计算dxdy 更可靠。

您应该尝试 Lars 的建议以进一步提高文本的质量和居中,例如您可能想将text-rendering 设置为optimizeLegibility 并稍微调整一下基线。

【讨论】:

您可能指的是startOffset,而不是offsetStart,请参阅w3.org/TR/SVG11/text.html#TextPathElementStartOffsetAttribute。 @ErikDahlström 谢谢。修好了。 这很整洁。谢谢!【参考方案2】:

问题 2-4 是因为字体渲染。在我的浏览器中,间距和字符大小等是一致的。您可以尝试使用text-rendering attribute 来改进。

要使文本居中,您需要设置alignment-baseline 和/或dominant-baseline 属性。

如果这仍然无法为您提供所需的结果,请尝试减小字体大小。这可能会有所帮助,因为角色的轻微旋转会不太明显。

【讨论】:

垂直对齐使用alignment-baseline 对我有用。

以上是关于如何使用 d3.js 在弧内沿 textPath 居中(水平和垂直)文本?的主要内容,如果未能解决你的问题,请参考以下文章

使用textpath将ImageMagick SVG添加到JPG

D3.js:d3-delaunay - 如何开始?

D3.js 使用函数时如何应用多个类

如何使用 d3.js 选择器删除处理程序

如何使用 d3.js 选择器删除处理程序

寻找将确定点是否在弧的左/右的算法