显示D3链接文本正面朝上

时间:2015-12-07 19:55:07

标签: javascript d3.js svg

我已经在链接上构建了一个带有文本标签的D3力导向可视化。我遇到的一个问题是,当链接位于其源节点的左侧时,这些标签会颠倒显示。示例:

enter image description here

我定位路径和文本的代码如下所示:

var nodes = flatten(data);
var links = d3.layout.tree().links(nodes);

var path = vis.selectAll('path.link')
  .data(links, function(d) {
    return d.target.id;
  });

path.enter().insert('svg:path')
  .attr({
    class: 'link',
    id: function(d) {
      return 'text-path-' + d.target.id;
    },
    'marker-end': 'url(#end)'
  })
  .style('stroke', '#ccc');

var linkText = vis.selectAll('g.link-text').data(links);

linkText.enter()
  .append('text')
    .append('textPath')
      .attr('xlink:href', function(d) {
        return '#text-path-' + d.target.id;
      })
      .style('text-anchor', 'middle')
      .attr('startOffset', '50%')
      .text(function(d) {return d.target.customerId});

我知道我需要以某种方式确定每条路径的当前角度,然后相应地设置文本位置,但我不知道该怎么做。

以下是基于此问题的块的链接:http://blockbuilder.org/MattDionis/5f966a5230079d9eb9f4

下面的答案让我大约90%的方式。以下是我的原始可视化效果,文本长度超过几位数:

enter image description here

...以下是利用以下答案提示的内容:

enter image description here

因此,虽然文本现在是“正面向上”,但它不再遵循弧形。

1 个答案:

答案 0 :(得分:6)

您绘制的弧线使得它们在中间的切线正好是文本基线的方向,并且它也与用于分隔两个树节点的矢量共线。

我们可以用它来解决问题。

需要一点数学。首先,让我们定义一个函数,该函数返回向量.menu-header-bottom-items ul{ list-style:none; padding:0; margin:0; width:100%; height:105px; z-index:9999; } .menu-header-bottom-items li{ float:left; width:auto; color:#fff; text-align:center; } .menu-header-bottom-items li a{ color:#fff; padding:47px 43px; display: block; font-size:14px; font-weight:300; text-transform:uppercase; text-decoration:none; } .menu-header-bottom-items li a:hover{ background:#00a94e;} .menu-header-bottom-items li.current-menu-item{ background:#00a94e; margin-top:-20px; padding-bottom:20px;} .menu-item-has-children{ position:relative;} .menu-item-has-children ul{ position:absolute; display:none; flex:0; } .menu-item-has-children:hover > ul{display:block;} .menu-item-has-children li a{ padding:15px; text-align:left; } .menu-item-has-children li { padding:0; float:none; background:url(http://s2.postimg.org/tlyo9809h/bg_header_strip.png);} .menu-item-has-children ul ul { left:100%;top:0;} ul.sub-menu li.menu-item-has-children{ background:url(http://s28.postimg.org/ilizrjzax/arrow_right.png) no-repeat 95% center,url(http://s2.postimg.org/tlyo9809h/bg_header_strip.png);} ul.sub-menu li.menu-item-has-children:hover{ background:#00a94e url(http://s28.postimg.org/ilizrjzax/arrow_right.png) no-repeat 95% center;} li.current-menu-item > ul{ margin-top:20px;} .sub-menu li a:hover{ background:#00a94e;} ul.sub-menu li.menu-item-has-children a:hover{ background:#00a94e url(http://s28.postimg.org/ilizrjzax/arrow_right.png) no-repeat 95% center;} .menu-header-bottom-items li:hover{ background:#00a94e;} 相对于水平轴的角度:

v

然后,在每个刻度线处,让我们通过减去其基线的角度来旋转文本。首先,一些实用功能:

function xAngle(v) {
    return Math.atan(v.y/v.x) + (v.x < 0 ? Math.PI : 0);
}

然后,在您的function isFiniteNumber(x) { return typeof x === 'number' && (Math.abs(x) < Infinity); } function isVector(v) { return isFiniteNumber(v.x) && isFiniteNumber(v.y); } 函数中添加

tick

编辑:添加了图片:

example result

编辑2 使用直线代替弯曲弧(linkText.attr('transform', function (d) { // Checks just in case, especially useful at the start of the sim if (!(isVector(d.source) && isVector(d.target))) { return ''; } // Get the geometric center of the text element var box = this.getBBox(); var center = { x: box.x + box.width/2, y: box.y + box.height/2 }; // Get the tangent vector var delta = { x: d.target.x - d.source.x, y: d.target.y - d.source.y }; // Rotate about the center return 'rotate(' + (-180/Math.PI*xAngle(delta)) + ' ' + center.x + ' ' + center.y + ')'; }); }); 内只需<text>而不是<textPath>),您可以替换{的部分关于<text>的{​​1}}函数:

tick

这就是你得到的:

rotated text

注意当链接从更多指向右边指向更多指向左边时,文本如何被翻转。

问题在于文本最终位于链接下方。这可以修复如下:

linkText

现在结果如下:

enter image description here

正如您所看到的,即使链接指向左侧,文本也会很好地位于该行的顶部。

最后,为了解决问题,同时保持弧线并使文本右侧向上弯曲,我认为你需要构建两个linkText.attr('transform', function(d) { if (!(isVector(d.source) && isVector(d.target))) { return ''; } // Get the geometric center of this element var box = this.getBBox(); var center = { x: box.x + box.width / 2, y: box.y + box.height / 2 }; // Get the direction of the link along the X axis var dx = d.target.x - d.source.x; // Flip the text if the link goes towards the left return dx < 0 ? ('rotate(180 ' + center.x + ' ' + center.y + ')') : ''; }); 元素。一个用于从linkText.attr('transform', function(d) { if (!(isVector(d.source) && isVector(d.target))) { return ''; } // Get the geometric center of this element var box = this.getBBox(); var center = { x: box.x + box.width / 2, y: box.y + box.height / 2 }; // Get the vector of the link var delta = { x: d.target.x - d.source.x, y: d.target.y - d.source.y }; // Get a unitary vector orthogonal to delta var norm = Math.sqrt(delta.x * delta.x + delta.y * delta.y); var orth = { x: delta.y/norm, y: -delta.x/norm }; // Replace this with your ACTUAL font size var fontSize = 14; // Flip the text and translate it beyond the link line // if the link goes towards the left return delta.x < 0 ? ('rotate(180 ' + center.x + ' ' + center.y + ') translate(' + (orth.x * fontSize) + ' ' + (orth.y * fontSize) + ')') : ''; }); 转到<textPath>,另一个用于相反的方式。当链接向右(source)时,您将使用第一个;当链接向左(target)时,您将使用第二个,我认为结果看起来更好,代码将不一定比原版更复杂,只需添加更多逻辑。