我已经在链接上构建了一个带有文本标签的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%的方式。以下是我的原始可视化效果,文本长度超过几位数:
...以下是利用以下答案提示的内容:
因此,虽然文本现在是“正面向上”,但它不再遵循弧形。
答案 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
编辑:添加了图片:
编辑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
这就是你得到的:
注意当链接从更多指向右边指向更多指向左边时,文本如何被翻转。
问题在于文本最终位于链接下方。这可以修复如下:
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
+ ')')
: '';
});
元素。一个用于从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
)时,您将使用第二个,我认为结果看起来更好,代码将不一定比原版更复杂,只需添加更多逻辑。