Dev*_*vin 5 javascript d3.js force-layout
我有一个带有弯曲链接的 d3 (v3) 力网络,如下所示:
我想要完成的是让链接的textPath元素是水平的,因为它们是数字,并且“81”需要看起来与“18”不同。我也希望有某种白色阴影/外发光/背景,因为我将它们直接放在链接上。我现在有一个白色的笔画,但它的效果不太好,因为有时一个数字的笔画会侵入它旁边的数字。
这里有一个可重复的例子,我承认它是从其他 SO 答案拼凑而成的:https : //jsfiddle.net/2gbekL7m/
代码的相关部分是:
var link_label = svg.selectAll(".link_label")
.data(links)
.enter()
.append("text")
.attr("class", "link_label")
.attr("paint-order", "stroke")
.attr("stroke", "white")
.attr("stroke-width", 4)
.attr("stroke-opacity", 1)
.attr("stroke-linecap", "butt")
.attr("stroke-linejoin", "miter")
.style("fill", "black")
.attr("dy", 5)
.append("textPath")
.attr("startOffset", "50%")
.attr("xlink:href", function(d, i) {
return "#link_" + i;
})
.text(function(d, i) {
return d.n;
});
Run Code Online (Sandbox Code Playgroud)
有谁知道如何通过固定方向和添加背景框来提高链接标签的可读性?
首先,因为你没有张贴你的代码,我的答复解决了代码的jsfiddle您共享。
让我们分别解决您的两个问题:
在我看来,您希望将标签放在链接的中间,并且始终是水平的,作为常规文本。在这种情况下,解决方案是删除textPath,这实际上会使您的选择更简单:
var link_label = svg.selectAll(".link_label")
.data(links)
.enter()
.append("text")
.text(function(d, i) {
return d.n;
});
Run Code Online (Sandbox Code Playgroud)
现在只需要获取这些路径的中间位置,我们可以在函数内部使用getTotalLength()和执行getPointAtLength()此操作tick:
link_label.attr("x", function(d, i) {
var pathLength = d3.select("#link_" + i).node().getTotalLength();
d.point = d3.select("#link_" + i).node().getPointAtLength(pathLength / 2);
return d.point.x
})
.attr("y", function(d) {
return d.point.y
})
Run Code Online (Sandbox Code Playgroud)
在这里,我将值存储在一个属性中,因为 x 和 y 位置的值都相同。这样我们就避免了不必要的重复计算。此外,有人可能会争辩说计算应该放在tick函数之外,以便只获得一次路径的长度:不幸的是,这里不是这种情况,因为在模拟过程中,路径的长度不断变化。
这里有几种不同的方法。一个简单的,可能不是最漂亮的,就是使用text-shadow。这是一个简单的上下左右的白色阴影:
.shadow {
text-shadow: 2px 2px 0 #fff, 2px -2px 0 #fff, -2px 2px 0 #fff, -2px -2px 0 #fff
}
Run Code Online (Sandbox Code Playgroud)
该 CSS 适用于 Chrome 和 FireFox,但不适用于 Safari。
总之,这是演示:
var link_label = svg.selectAll(".link_label")
.data(links)
.enter()
.append("text")
.text(function(d, i) {
return d.n;
});
Run Code Online (Sandbox Code Playgroud)
link_label.attr("x", function(d, i) {
var pathLength = d3.select("#link_" + i).node().getTotalLength();
d.point = d3.select("#link_" + i).node().getPointAtLength(pathLength / 2);
return d.point.x
})
.attr("y", function(d) {
return d.point.y
})
Run Code Online (Sandbox Code Playgroud)
.shadow {
text-shadow: 2px 2px 0 #fff, 2px -2px 0 #fff, -2px 2px 0 #fff, -2px -2px 0 #fff
}
Run Code Online (Sandbox Code Playgroud)
编辑:看看Xavier 的回答,白色圆圈看起来比文字阴影更糟糕。
重新使用赫拉尔的答案用的使用getPointAtLength(),这里是基于一个白色圆圈的问题的第二部分的替代,关于标签的阴影/背景:
var nodes = [{
"ix": 0
},
{
"ix": 1
},
{
"ix": 2
},
{
"ix": 3
}
];
var links = [{
"source": 0,
"target": 2,
"n": 12
},
{
"source": 0,
"target": 1,
"n": 34
},
{
"source": 1,
"target": 2,
"n": 56
},
{
"source": 1,
"target": 0,
"n": 78
},
{
"source": 0,
"target": 3,
"n": 90
}
];
var w = 400,
h = 400;
var force = d3.layout.force()
.size([w, h])
.nodes(nodes)
.links(links)
.gravity(1)
.linkDistance(30)
.charge(-20000)
.linkStrength(1);
force.start();
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h)
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 " + w + " " + h)
.append("g");
var marker = svg.selectAll("marker")
.append("svg:defs")
.data(["end-arrow"])
.enter()
.append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -3 6 6")
.attr("refX", 11.3)
.attr("refY", -0.2)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-3L6,0L0,3");
var link = svg.selectAll("line.link")
.data(links)
.enter()
.append("svg:path")
.attr("id", function(d, i) {
return "link_" + i;
})
.style("stroke", "black")
.style("stroke-width", 2)
.style("fill", "none")
.attr("marker-end", "url(#end-arrow)");
var link_label_shadow = svg.selectAll(".link_label_shadow")
.data(links)
.enter()
.append("circle")
.attr("r", 10)
.style("fill", "white");
var link_label = svg.selectAll(".link_label")
.data(links)
.enter()
.append("text")
.style("text-anchor", "middle")
.style("dominant-baseline", "central")
.attr("class", "shadow")
.text(function(d, i) {
return d.n;
});
var node = svg.selectAll(".node")
.data(force.nodes())
.enter()
.append("svg:g")
.attr("class", "node");
node.append("svg:circle")
.attr("r", 10)
.style("fill", "black");
node.call(force.drag);
force.on("tick", function() {
link.attr("d", function(d) {
var dx = d.target.x - d.source.x;
var dy = d.target.y - d.source.y;
var dr = Math.sqrt(dx * dx + dy * dy);
return "M" +
d.source.x +
"," +
d.source.y +
"A" +
dr +
"," +
dr +
" 0 0,1 " +
d.target.x +
"," +
d.target.y;
});
link_label.attr("x", function(d, i) {
var pathLength = d3.select("#link_" + i).node().getTotalLength();
d.point = d3.select("#link_" + i).node().getPointAtLength(pathLength / 2);
return d.point.x
})
.attr("y", function(d) {
return d.point.y
})
node.attr("transform", function(d) {
return ("translate(" + d.x + "," + d.y + ")");
});
link_label_shadow.attr("cx", function(d, i) {
var pathLength = d3.select("#link_" + i).node().getTotalLength();
d.point = d3.select("#link_" + i).node().getPointAtLength(pathLength / 2);
return d.point.x
})
.attr("cy", function(d) {
return d.point.y
})
node.attr("transform", function(d) {
return ("translate(" + d.x + "," + d.y + ")");
});
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>Run Code Online (Sandbox Code Playgroud)
与使用 设置标签的方式相同getPointAtLength(),我们可以在链接和文本标签之间的同一位置包含一个白色圆圈。
由于在其关联链接之后创建了白色圆圈,因此它们将位于链接上方,从而隐藏其中间部分。然后只插入标签,因此位于白色圆圈上方。