有向网络中的衰落网络连接 - d3js [英] Fading network connections in directed network - d3js
问题描述
我有一个使用 d3.layout.force
的定向网络。适应此答案,如果已连接(连接方向无关紧要),我已设法获取节点和链接淡出。
I have a directed network using the d3.layout.force
. Adapting this answer, I have managed to get nodes and links to fade if connected (direction of connection doesn't matter).
我遇到的问题是能够在它们所在的路径使用 mouseover
事件改变其不透明度时更改标记的不透明度。
What I am having trouble with is to be able to change the opacity of markers when the path they are on is having its opacity altered with a mouseover
event.
这是包含 isConnected
函数的脚本,用于确定连接的节点:
This is the script including the isConnected
function for determining what nodes are connected:
<script>
function bar() {
console.log("click");
force.stop();
force.start();
}
var links = [
{source: "A", target: "D", type: "high"},
{source: "A", target: "K", type: "high"},
{source: "B", target: "G", type: "high"},
{source: "C", target: "A", type: "low"},
{source: "D", target: "K", type: "low"},
{source: "E", target: "A", type: "low"},
{source: "F", target: "B", type: "low"},
{source: "K", target: "J", type: "low"},
{source: "F", target: "A", type: "low"},
{source: "F", target: "I", type: "low"},
{source: "G", target: "H", type: "low"},
{source: "E", target: "K", type: "high"},
{source: "E", target: "G", type: "low"},
{source: "E", target: "F", type: "high"},
{source: "D", target: "E", type: "high"}
];
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
var width = 960,
height = 700;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(105)
.charge(-775)
.on("tick", tick)
.start();
force.on("start", function () {
console.log("start");
});
force.on("end", function () {
console.log("end");
});
R=18
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// add defs-marker
svg.append('svg:defs')
.append('svg:marker')
.attr('id', 'end-arrow')
.attr('viewBox', '0 0 10 10')
.attr('refX', 2+R)
.attr('refY', 5)
.attr('markerWidth', 4)
.attr('markerHeight', 4)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,0 L0,10 L10,5 z');
var link = svg.selectAll(".link")
.data(force.links())
.enter()
.append("line")
.attr("class", "link")
.attr('marker-end', 'url(#end-arrow)')
;
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", R)
.on("mouseover", fade(.1))
.on("mouseout", fade(1))
;
node.append("text")
.attr("x", 0)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
function tick() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; })
;
node
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
var linkedByIndex = {};
links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
function fade(opacity) {
return function(d) {
node.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
marker.style("opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
};
}
</script>
一个切线相关的问题是如何缩短路径,以便当节点和链接的不透明度消失时,走到每个节点中间的线路不明显。
A tangentially related question would be how to shorten the path so that when the opacity of nodes and links fade, that the line going to the middle of each node is not noticeable.
推荐答案
由于标记方式,您的方法不可行实例被渲染。我自己的一个答案和引用SVG规范:
Your approach is not feasible because of the way marker instances are rendered. Cannibalizing one of my own answers and quoting the SVG spec:
11.6.4有关标记如何呈现的详细信息
[...]
a的渲染效果标记就好像引用的'marker'元素的内容被深深地克隆到标记的每个实例的单独的非暴露DOM树中。因为克隆的DOM树是非暴露的,所以SVG DOM不显示标记的克隆实例。
The rendering effect of a marker is as if the contents of the referenced ‘marker’ element were deeply cloned into a separate non-exposed DOM tree for each instance of the marker. Because the cloned DOM tree is non-exposed, the SVG DOM does not show the cloned instance of the marker.
仅原始标记元素,即声明< marker>
元素,可使用CSS设置样式,而通过属性引用的克隆实例 marker-start
, marker-mid
或 marker-end
无法访问,因此无法单独设置样式。
Only the original marker elements, i.e. the declaring <marker>
elements, are stylable using CSS, whereas the cloned instances referenced via properties marker-start
, marker-mid
, or marker-end
are not accessible and therefore not individually stylable.
CSS2选择器可以应用于原始(即引用)元素,因为它们是正式文档结构的一部分。 CSS2选择器不能应用于(概念上)克隆的DOM树,因为它的内容不是正式文档结构的一部分。
CSS2 selectors can be applied to the original (i.e., referenced) elements because they are part of the formal document structure. CSS2 selectors cannot be applied to the (conceptually) cloned DOM tree because its contents are not part of the formal document structure.
为了规避这些约束,你可以使用两个定义的标记
元素,第二个是第一个克隆版本,不透明度降低。
To circumvent these constraints you could use two defining marker
elements, the second being a cloned version of the first with reduced opacity.
// add defs-markers
svg.append('svg:defs').selectAll("marker")
.data([{id:"end-arrow", opacity:1}, {id:"end-arrow-fade", opacity:0.1}])
.enter().append('marker')
.attr('id', function(d) { return d.id; })
.attr('viewBox', '0 0 10 10')
.attr('refX', 2+R)
.attr('refY', 5)
.attr('markerWidth', 4)
.attr('markerHeight', 4)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,0 L0,10 L10,5 z')
.style("opacity", function(d) { return d.opacity; });
在你的 fade()
函数中,你是然后能够切换行' marker-end
属性以引用相应标记的ID:
Within your fade()
function you are then able to switch the lines' marker-end
properties to refer to the appropriate marker's id:
link.attr("marker-end", function(o) {
return opacity === 1 || o.source === d || o.target === d
? 'url(#end-arrow)' : 'url(#end-arrow-fade)';
});
查看以下代码片段以了解正常工作:
Have a look at the following snippet for a working demo:
function bar() {
console.log("click");
force.stop();
force.start();
}
var links = [
{source: "A", target: "D", type: "high"},
{source: "A", target: "K", type: "high"},
{source: "B", target: "G", type: "high"},
{source: "C", target: "A", type: "low"},
{source: "D", target: "K", type: "low"},
{source: "E", target: "A", type: "low"},
{source: "F", target: "B", type: "low"},
{source: "K", target: "J", type: "low"},
{source: "F", target: "A", type: "low"},
{source: "F", target: "I", type: "low"},
{source: "G", target: "H", type: "low"},
{source: "E", target: "K", type: "high"},
{source: "E", target: "G", type: "low"},
{source: "E", target: "F", type: "high"},
{source: "D", target: "E", type: "high"}
];
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
var width = 600,
height = 600;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(105)
.charge(-775)
.on("tick", tick)
.start();
force.on("start", function () {
console.log("start");
});
force.on("end", function () {
console.log("end");
});
R=18
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// add defs-markers
svg.append('svg:defs').selectAll("marker")
.data([{id:"end-arrow", opacity:1}, {id:"end-arrow-fade", opacity:0.1}])
.enter().append('marker')
.attr('id', function(d) { return d.id; })
.attr('viewBox', '0 0 10 10')
.attr('refX', 2+R)
.attr('refY', 5)
.attr('markerWidth', 4)
.attr('markerHeight', 4)
.attr('orient', 'auto')
.append('svg:path')
.attr('d', 'M0,0 L0,10 L10,5 z')
.style("opacity", function(d) { return d.opacity; });
var link = svg.selectAll(".link")
.data(force.links())
.enter()
.append("line")
.attr("class", "link")
.attr('marker-end', 'url(#end-arrow)');
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", R)
.on("mouseover", fade(.1))
.on("mouseout", fade(1))
;
node.append("text")
.attr("x", 0)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
function tick() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
var linkedByIndex = {};
links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
function fade(opacity) {
return function(d) {
node.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
link.attr("marker-end", function(o) {
return opacity === 1 || o.source === d || o.target === d
? 'url(#end-arrow)' : 'url(#end-arrow-fade)';
});
};
}
.node circle {
fill: #DDD;
stroke: #777;
stroke-width: 2px;
}
.node text {
font-family: sans-serif;
text-anchor: middle;
pointer-events: none;
user-select: none;
-webkit-user-select: none;
}
.link {
stroke: #88A;
stroke-width: 4px;
}
text {
font: 18px sans-serif;
pointer-events: none;
}
#end-arrow {
fill: #88A;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
这篇关于有向网络中的衰落网络连接 - d3js的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!