我正在尝试在强制布局节点内创建一个强制布局
我从数据中创建了三个数组,其中两个用于外部节点和链接,最后一个用于内部节点。并绘制了外部图表,但我不确定如何 为每个节点创建内部图表。 我应该为每个内部节点创建一个单独的模拟吗?
我正在添加当前代码,对您的帮助将不胜感激!
data.json
{
"id": "group1",
"members": [
{"id": "member1"},
{"id": "member2"}
],
"children": [
{
"id": "group2",
"members": [
{"id": "member1"},
{"id": "member2"}
],
},
{
"id": "group3",
"members": [
{"id": "member1"},
{"id": "member2"},
{
"id": "member3",
"children": [
{ "id": "group4" },
{ "id": "group5" }
]
}
]
}
]
}
main.js
const width = window.innerWidth;
const height = window.innerHeight;
const svg = d3.select("body")
.append('svg')
.attr('width', width)
.attr('height', height)
.call(d3.zoom()
.scaleExtent([1 / 2, 8])
.on('zoom', zoomed)
);
const outerNodes = [];
const outerLinks = [];
const innerNodes = [];
const canvas = svg.append("g");
const simulation = d3.forceSimulation()
.force('center', d3.forceCenter(width / 2, height / 2))
.force('charge', d3.forceManyBody().strength(-500))
.force('link', d3.forceLink().id(d => d.id).distance(80).strength(1));
let outerGroup, node, link;
d3.json('data.json').then(data => {
flatten(data);
link = canvas.append('g')
.attr('class', 'links')
.selectAll('line')
.data(outerLinks)
.enter().append('line')
.attr('stroke', '#777');
outerGroup = canvas.append('g')
.attr('class', 'nodes')
.selectAll('path')
.data(outerNodes)
.enter().append('g')
.attr('id', d => d.id)
.call(d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd)
);
node = outerGroup.append('path')
.attr('d', generateShapePath())
.attr('fill', d => d.children ? 'blue' : 'green')
simulation
.nodes(outerNodes)
.on('tick', ticked);
simulation.force('link')
.links(outerLinks);
});
function flatten(node) {
outerNodes.push(node);
if (node.members) {
innerNodes.push({parent: node.id, nodes: node.members})
}
if (node.children) {
node.children.forEach(child => {
outerLinks.push({ source: node.id, target: child.id });
flatten(child);
});
}
}
function ticked() {
link
.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
node.attr('d', generateShapePath);
}
答案 0 :(得分:0)
您需要解耦所有单个仿真。相对于父位置对内部仿真进行建模。
g
。此g
将转换为tick()
g
建模的0,0
g
和相对的五边形path
width
和height
d3.tree
的用途是什么?
如果要使用select
,请不要使用each
parent.select(d => {
d.updateMembers = () => {
innerSimulation.force('center', d3.forceCenter(d.x, d.y));
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<script>
run();
function run() {
const data = {
"id": "group1",
"members": [
{"id": "member1"},
{"id": "member2"}
],
"children": [
{"id": "group2"},
{"id": "group3"},
{"id": "group4"},
{
"id": "group5",
"children": [
{"id": "group6"},
{"id": "group7"}
]
}
]
};
const width = 500; // window.innerWidth;
const height = 400; // window.innerHeight;
const svg = d3.select("body")
.append('svg')
.attr('width', width)
.attr('height', height)
.call(d3.zoom()
.scaleExtent([1 / 2, 8])
.on("zoom", zoomed));
const outerNodes = [];
const outerLinks = [];
const innerNodes = [];
const canvas = svg.append("g");
const color = d3.scaleOrdinal(d3.schemeCategory10);
const tree = d3.tree().size(height, width);
const simulation = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-500))
.force("link", d3.forceLink().id(d => d.id).distance(80).strength(1));
const innerSimulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(5))
.force('collision', d3.forceCollide().radius(10))
.force('center', d3.forceCenter());
let link, node, nodeGroup;
onLoad(data);
function onLoad(data) {
flatten(data);
link = canvas.append('g')
.attr('class', 'links')
.selectAll('line')
.data(outerLinks)
.enter().append('line')
.attr('stroke', '#eee');
nodeGroup = canvas.append('g')
.attr('class', 'nodes')
.selectAll('path')
.data(outerNodes)
.enter().append('g')
.attr('id', d => d.id)
.call(d3.drag()
.on('start', onDragStart)
.on('drag', onDrag)
.on('end', onDragEnd)
);
nodeGroup.append('path')
.attr('d', generatePentagonPath(25))
.attr('fill', d => d.children ? 'blue' : 'green');
innerNodes.map(member => {
const parent = canvas.select(`#${member.parent}`);
const members = parent
.selectAll('.member')
.data(member.nodes)
.enter()
.append('g')
.attr('class', 'member')
.call(d3.drag()
.on('start', (d) => {
if (!d3.event.active) {
innerSimulation.alphaTarget(.3).restart();
}
members.each(d => {
d.fx = d.x;
d.fy = d.y
})
})
.on('drag', function (d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
})
.on('end', (d) => {
if (!d3.event.active) {
innerSimulation.alphaTarget(0).restart();
}
d.fx = d.fy = null;
})
);
members
.append('path')
.attr('d', generatePentagonPath(5))
.attr('fill', 'orange');
innerSimulation
.nodes(member.nodes)
.on('tick', () => {
members.attr("transform", d => `translate(${d.x},${d.y})`);
});
});
simulation
.nodes(outerNodes)
.on('tick', ticked);
simulation.force('link')
.links(outerLinks);
}
function onDragStart(d) {
if (!d3.event.active) {
simulation.alphaTarget(0.5).restart();
}
nodeGroup.each(d => {
d.fx = d.x;
d.fy = d.y;
});
}
function onDrag(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function onDragEnd(d) {
if (!d3.event.active) {
simulation.alphaTarget(0).restart();
}
d.fx = null;
d.fy = null;
}
function zoomed() {
canvas.attr("transform", d3.event.transform);
}
function flatten(node) {
outerNodes.push(node);
if (node.members) {
innerNodes.push({parent: node.id, nodes: node.members});
}
if (node.children) {
node.children.forEach(child => {
outerLinks.push({ source: node.id, target: child.id });
flatten(child);
});
}
}
function generatePentagonPath(radius = 25, x = 0, y = 0) {
const numPoints = 5;
const points = d3.range(numPoints)
.map(i => {
const angle = i / numPoints * Math.PI * 2 + Math.PI;
return [Math.sin(angle) * radius + x, Math.cos(angle) * radius + y];
});
return d3.line()(points);
}
function ticked() {
link
.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
nodeGroup.attr("transform", d => `translate(${d.x},${d.y})`);
}
}
</script>
</body>
</html>