过滤和停用强制布局中的链接和文本
Filter and deactivate links and text in the force-layout
我在D3.js中使用力布局。
现在我可以通过使用过滤器从任何移动中停用一些节点。在我的例子中,我过滤掉的节点是循环 else
部分中的蓝色节点。
现在,当单击 "button" 时,所有红色节点都会增加 forceCollide 值的大小。
借助下面 if
部分中的循环,如果 firstTime
为 "true" ,则可以使所有节点正常移动。
现在,我的问题是,我还没有找到如何停用连接到已过滤蓝色节点的链接和文本?这意味着,我希望被过滤节点的文本也被过滤并被停用,链接也是如此。那么,我怎样才能从按属性过滤的节点中获取链接和文本的连接呢?
谢谢大家!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<div class="centre jumbotron">
</div>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click me</button>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var g = svg.append("g");
var firstTime = true;
var colours = ["blue", "red", "green", "yellow"];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collide", d3.forceCollide(function(d) {
return d.r + 1;
}));
d3.json("https://jsonblob.com/api/6e520635-d35c-11e6-b16a-6b255c15b1a3", function(error, graph) {
if (error) throw error;
var link = g.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", 1);
var node = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", (d, i) => colours[i % 2])
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var text = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.style("fill", (d, i) => colours[i % 2])
.text(function(d) { return d.group });
d3.select("button").on("click", function(d) {
firstTime = false;
node.filter(function() {
return d3.select(this).attr("fill") === "red"
}).each(d => d.r = 60);
simulation.nodes(graph.nodes);
simulation.alpha(0.8).restart();
})
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
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;});
if (firstTime) {
node.attr("cx", d => d.x).attr("cy", d => d.y);
} else {
node.filter(function(e) {
return d3.select(this).attr("fill") != "blue"
}).attr("cx", d => d.x).attr("cy", d => d.y);
}
text
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
我在 tick
函数中为你提出的过滤器写了最后一个问题的答案,因为我不知道你也有链接和文本。现在你有一个不同的问题,你不能简单地将另一个问题的答案改编为当前的问题:它们是不同的问题。
对于这个,简单的解决方案是设置 fx
和 fy
属性。根据 API:
To fix a node in a given position, you may specify two additional properties:
- fx - the node’s fixed x-position
- fy - the node’s fixed y-position
At the end of each tick, after the application of any forces, a node with a defined node.fx has node.x reset to this value and node.vx set to zero; likewise, a node with a defined node.fy has node.y reset to this value and node.vy set to zero. To unfix a node that was previously fixed, set node.fx and node.fy to null, or delete these properties.
因此,您可以在 "click" 函数中执行类似的操作:
d3.select("button").on("click", function(d) {
//this will fix the blue nodes
node.filter(function() {
return d3.select(this).attr("fill") === "blue"
}).each(function(d) {
d.fx = d.x;
d.fy = d.y;
})
node.filter(function() {
return d3.select(this).attr("fill") === "red"
}).each(function(d) {
return d.r = 80
});
simulation.nodes(nodes);
simulation.alpha(0.8).restart();
})
这是一个演示。如果点击按钮,红色圆圈会推开另一个nodes/texts,但蓝色圆圈留在原位。文本和链接保持连接。拖动其中任何一个都会释放它。
var width = 400;
var height = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "foo",
color: "blue"
}, {
name: "bar",
color: "green"
}, {
name: "baz",
color: "red"
}, {
name: "foofoo",
color: "yellow"
}, {
name: "foobar",
color: "blue"
}, {
name: "foobaz",
color: "green"
}, {
name: "barfoo",
color: "red"
}, {
name: "barbar",
color: "yellow"
}, {
name: "barbaz",
color: "blue"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}, {
"source": 0,
"target": 3
}, {
"source": 1,
"target": 3
}, {
"source": 1,
"target": 4
}, {
"source": 2,
"target": 5
}, {
"source": 3,
"target": 6
}, {
"source": 1,
"target": 7
}, {
"source": 6,
"target": 8
}, {
"source": 0,
"target": 7
}, {
"source": 2,
"target": 6
}, {
"source": 3,
"target": 8
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody().strength(-50))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collide", d3.forceCollide(function(d) {
return d.r + 1;
}));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
.attr("r", function(d) {
return d.r = 10;
})
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d) {
return d.color
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));;
var text = svg.selectAll(null)
.data(nodes)
.enter()
.append("text")
.attr("pointer-events", "none")
.style("fill", "black")
.attr("dy", "-1em")
.attr("dx", "-1em")
.text(function(d) {
return d.name;
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
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("cx", function(d) {
return d.x
}).attr("cy", function(d) {
return d.y
});
text.attr("x", function(d) {
return d.x
}).attr("y", function(d) {
return d.y
});
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
d3.select("button").on("click", function(d) {
node.filter(function() {
return d3.select(this).attr("fill") === "blue"
}).each(function(d) {
d.fx = d.x;
d.fy = d.y;
})
node.filter(function() {
return d3.select(this).attr("fill") === "red"
}).each(function(d) {
return d.r = 80
});
simulation.nodes(nodes);
simulation.alpha(0.8).restart();
})
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click me</button>
<br>
我在D3.js中使用力布局。
现在我可以通过使用过滤器从任何移动中停用一些节点。在我的例子中,我过滤掉的节点是循环 else
部分中的蓝色节点。
现在,当单击 "button" 时,所有红色节点都会增加 forceCollide 值的大小。
借助下面 if
部分中的循环,如果 firstTime
为 "true" ,则可以使所有节点正常移动。
现在,我的问题是,我还没有找到如何停用连接到已过滤蓝色节点的链接和文本?这意味着,我希望被过滤节点的文本也被过滤并被停用,链接也是如此。那么,我怎样才能从按属性过滤的节点中获取链接和文本的连接呢?
谢谢大家!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<div class="centre jumbotron">
</div>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click me</button>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var g = svg.append("g");
var firstTime = true;
var colours = ["blue", "red", "green", "yellow"];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collide", d3.forceCollide(function(d) {
return d.r + 1;
}));
d3.json("https://jsonblob.com/api/6e520635-d35c-11e6-b16a-6b255c15b1a3", function(error, graph) {
if (error) throw error;
var link = g.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", 1);
var node = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", (d, i) => colours[i % 2])
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var text = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.style("fill", (d, i) => colours[i % 2])
.text(function(d) { return d.group });
d3.select("button").on("click", function(d) {
firstTime = false;
node.filter(function() {
return d3.select(this).attr("fill") === "red"
}).each(d => d.r = 60);
simulation.nodes(graph.nodes);
simulation.alpha(0.8).restart();
})
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
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;});
if (firstTime) {
node.attr("cx", d => d.x).attr("cy", d => d.y);
} else {
node.filter(function(e) {
return d3.select(this).attr("fill") != "blue"
}).attr("cx", d => d.x).attr("cy", d => d.y);
}
text
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>
我在 tick
函数中为你提出的过滤器写了最后一个问题的答案,因为我不知道你也有链接和文本。现在你有一个不同的问题,你不能简单地将另一个问题的答案改编为当前的问题:它们是不同的问题。
对于这个,简单的解决方案是设置 fx
和 fy
属性。根据 API:
To fix a node in a given position, you may specify two additional properties:
- fx - the node’s fixed x-position
- fy - the node’s fixed y-position
At the end of each tick, after the application of any forces, a node with a defined node.fx has node.x reset to this value and node.vx set to zero; likewise, a node with a defined node.fy has node.y reset to this value and node.vy set to zero. To unfix a node that was previously fixed, set node.fx and node.fy to null, or delete these properties.
因此,您可以在 "click" 函数中执行类似的操作:
d3.select("button").on("click", function(d) {
//this will fix the blue nodes
node.filter(function() {
return d3.select(this).attr("fill") === "blue"
}).each(function(d) {
d.fx = d.x;
d.fy = d.y;
})
node.filter(function() {
return d3.select(this).attr("fill") === "red"
}).each(function(d) {
return d.r = 80
});
simulation.nodes(nodes);
simulation.alpha(0.8).restart();
})
这是一个演示。如果点击按钮,红色圆圈会推开另一个nodes/texts,但蓝色圆圈留在原位。文本和链接保持连接。拖动其中任何一个都会释放它。
var width = 400;
var height = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "foo",
color: "blue"
}, {
name: "bar",
color: "green"
}, {
name: "baz",
color: "red"
}, {
name: "foofoo",
color: "yellow"
}, {
name: "foobar",
color: "blue"
}, {
name: "foobaz",
color: "green"
}, {
name: "barfoo",
color: "red"
}, {
name: "barbar",
color: "yellow"
}, {
name: "barbaz",
color: "blue"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}, {
"source": 0,
"target": 3
}, {
"source": 1,
"target": 3
}, {
"source": 1,
"target": 4
}, {
"source": 2,
"target": 5
}, {
"source": 3,
"target": 6
}, {
"source": 1,
"target": 7
}, {
"source": 6,
"target": 8
}, {
"source": 0,
"target": 7
}, {
"source": 2,
"target": 6
}, {
"source": 3,
"target": 8
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody().strength(-50))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("collide", d3.forceCollide(function(d) {
return d.r + 1;
}));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
.attr("r", function(d) {
return d.r = 10;
})
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d) {
return d.color
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));;
var text = svg.selectAll(null)
.data(nodes)
.enter()
.append("text")
.attr("pointer-events", "none")
.style("fill", "black")
.attr("dy", "-1em")
.attr("dx", "-1em")
.text(function(d) {
return d.name;
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
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("cx", function(d) {
return d.x
}).attr("cy", function(d) {
return d.y
});
text.attr("x", function(d) {
return d.x
}).attr("y", function(d) {
return d.y
});
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
d3.select("button").on("click", function(d) {
node.filter(function() {
return d3.select(this).attr("fill") === "blue"
}).each(function(d) {
d.fx = d.x;
d.fy = d.y;
})
node.filter(function() {
return d3.select(this).attr("fill") === "red"
}).each(function(d) {
return d.r = 80
});
simulation.nodes(nodes);
simulation.alpha(0.8).restart();
})
<script src="https://d3js.org/d3.v4.min.js"></script>
<button>Click me</button>
<br>