Mat*_*nis 6 javascript svg d3.js
我已经在链接上构建了一个带有文本标签的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});
Run Code Online (Sandbox Code Playgroud)
我知道我需要以某种方式确定每个路径的当前角度,然后相应地设置文本位置,但我不知道如何.
以下是基于此问题的块的链接:http://blockbuilder.org/MattDionis/5f966a5230079d9eb9f4
下面的答案让我大约90%的方式.以下是我的原始可视化效果,文本长度超过几位数:
......以下是利用以下答案中的提示:
因此,虽然文本现在是"正面向上",但它不再遵循弧形.
您绘制的弧线使得它们在中间的切线正好是文本基线的方向,并且它也与用于分隔两个树节点的矢量共线.
我们可以用它来解决问题.
需要一点数学.首先,让我们定义一个函数,该函数返回向量v相对于水平轴的角度:
function xAngle(v) {
return Math.atan(v.y/v.x) + (v.x < 0 ? Math.PI : 0);
}
Run Code Online (Sandbox Code Playgroud)
然后,在每个刻度线处,让我们通过减去其基线的角度来旋转文本.首先,一些实用功能:
function isFiniteNumber(x) {
return typeof x === 'number' && (Math.abs(x) < Infinity);
}
function isVector(v) {
return isFiniteNumber(v.x) && isFiniteNumber(v.y);
}
Run Code Online (Sandbox Code Playgroud)
然后,在你的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
+ ')';
});
});
Run Code Online (Sandbox Code Playgroud)
编辑:添加图片:
编辑2使用直线而不是弯曲的弧线(只是<text>在<textPath>内部而不是内部<text>),您可以替换与此相关的tick函数部分linkText:
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
+ ')')
: '';
});
Run Code Online (Sandbox Code Playgroud)
这就是你得到的:
注意当链接从更多指向右边指向更多指向左边时,文本如何被翻转.
这个问题是文本最终在链接下面.这可以修复如下:
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) + ')')
: '';
});
Run Code Online (Sandbox Code Playgroud)
现在结果如下:
正如您所看到的,即使链接指向左侧,文本也能很好地位于行顶部.
最后,为了解决问题,同时保持弧线并使文本右侧向上弯曲,我认为你需要构建两个<textPath>元素.一个从去source到target,一个是要以相反的方式.当链接向右(delta.x >= 0)时你会使用第一个,当链接向左()时你会使用第二个delta.x < 0,我认为结果看起来更好,代码不一定比原来复杂,只是添加了更多的逻辑.
| 归档时间: |
|
| 查看次数: |
1029 次 |
| 最近记录: |