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

时间:2013-12-07 21:48:05

标签: javascript text svg path d3.js

在与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/

enter image description here

但是仍然存在一些问题:

  1. 如何在innerRadius,outerRadius,startAngle和endAngle描述的弧内的任意长度的文本路径中居中(水平和垂直)文本?
  2. 文本有时是粗体,有时不是。为什么?
  3. 字符间距看起来与写在a中的字符间距不同。有些字母和其他字母一样粘在一起。为什么?
  4. 字母不直接位于路径上。有些人似乎有点滑倒或下滑。为什么?

2 个答案:

答案 0 :(得分:9)

垂直对齐

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

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

水平对齐

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

Demo

这比手动计算dxdy更为健全。

您应该尝试Lars的建议,以进一步提高文本的质量和中心性,例如:您可能希望将text-rendering设置为optimizeLegibility并稍微使用基线。

答案 1 :(得分:4)

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

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

如果这仍然无法提供您正在寻找的结果,请尝试减小字体大小。这可能有所帮助,因为角色的轻微旋转将不那么明显。