【问题标题】:Show D3 link text right-side up正面朝上显示 D3 链接文本
【发布时间】:2015-12-07 19:55:07
【问题描述】:

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

我放置路径和文本的代码如下所示:

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% 的工作。这是我的原始可视化效果,其中文本长度超过了几个数字:

...这是使用以下答案中的提示的样子:

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

【问题讨论】:

标签: javascript d3.js svg


【解决方案1】:

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

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

需要一点数学知识。首先,让我们定义一个函数,它返回向量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 函数中,添加

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
        + ')';
    });
});

编辑:添加图片:

edit 2 用直线代替弧线(只需&lt;text&gt; 代替&lt;textPath&gt;&lt;text&gt; 内),您可以替换tick 函数中与@ 相关的部分987654336@ 这个:

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) + ')')
        : '';
});

现在结果如下所示:

如您所见,文本很好地位于行的顶部,即使链接指向左侧。

最后,为了解决问题,同时保持弧形和文本右侧向上沿弧形弯曲,我认为您需要构建两个 &lt;textPath&gt; 元素。一个用于从sourcetarget,一个用于相反的方向。当链接向右(delta.x &gt;= 0)时,您将使用第一个,而当链接向左(delta.x &lt; 0)时,您将使用第二个,我认为结果会更好,代码不一定会更多比原来复杂,只是增加了一点逻辑。

【讨论】:

  • 谢谢!这是一个很棒的解释,让我走了大约 90% 的路。我在原始问题中添加了更多图片以突出显示剩余的问题。我的目标是让文本标签位于每个弧上。
  • 我以为我昨天从你那里看到了关于我的第二期的答案,但现在看起来它已经消失了。我仍在努力,愿意将曲线链接更改为直线,如果这样更容易解决,因为这只是一个次要的设计决定。
  • @MattDionis 好吧,我很高兴您说您不关心弯曲的文本,因为该要求会使解决方案更难实现。如果你允许文本是直的,代码会变得更简单,我的意思是比上面的第一个解决方案更简单,因为不需要旋转。当delta 向量向左(delta.x &lt; 0)时,只需翻转文本。我会尽可能写代码。
  • @MattDionis OK 终于解决了链接直线的问题。见编辑。
  • 非常感谢您不仅回答了这个问题,而且详细地回答了这个问题,这样我才能真正理解这个解决方案是如何工作的。我会在 24 小时内向你奖励 +50 赏金:)
猜你喜欢
  • 1970-01-01
  • 2016-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多