React + D3 + Force-Directed Tree + Adjustable Link 强度
React + D3 + Force-Directed Tree + Adjustable Link Strength
我正在尝试使用 React 制作 Force-Directed Tree 并且它有效。但是如果我通过 props 将它传递到组件之外,我无法修改 "link strength"。
老实说,我可以更改 "strength",但我需要将 d3 svg 附加到我的 react ref div 之后才能看到更改。整个图将被重新绘制。
我找到了example by Mike Bostock。他建议通过使用 simulation.alpha 和 simulation.restart 重新加热模拟来修改力导向图的参数。但我不能让它与反应一起工作。没有任何反应。
这是我的代码:
export default function Hierarchy(props) {
const {
strength,
lineColor,
lineStroke,
width,
height,
nodeSize,
nodeColor,
} = props;
const root = d3.hierarchy(data);
const links = root.links();
const nodes = root.descendants();
const svg = d3.create("svg");
const link = svg
.append("g")
.selectAll("line")
.data(links)
.join("line");
const node = svg
.append("g")
.selectAll("circle")
.data(nodes)
.join("circle");
function applyStyle(selectionSVG) {
selectionSVG
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height]);
selectionSVG
.selectAll("circle")
.attr("r", nodeSize)
.attr("fill", nodeColor)
selectionSVG
.selectAll("line")
.attr("stroke", lineColor)
.attr("stroke-width", lineStroke);
}
applyStyle(svg);
const divRef = React.useRef(null);
const linkForce = d3
.forceLink(links)
.id(d => d.id)
.distance(0)
.strength(strength);
const simulation = d3
.forceSimulation(nodes)
.force("link", linkForce)
.force("charge", d3.forceManyBody().strength(-500))
.force("x", d3.forceX())
.force("y", d3.forceY());
simulation.on("tick", () => {
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("cx", d => d.x).attr("cy", d => d.y);
});
//ComponentDidMount
useEffect(() => {
//Append d3 svg to ref div
var div = d3.select(divRef.current);
if (div.node().firstChild) {
div.node().removeChild(div.node().firstChild);
}
div.node().appendChild(svg.node());
}, []);
//ComponentDidUpdate
useEffect(() => {
simulation.force("link").strength(strength);
simulation.alpha(1).restart();
}, [strength]);
//ComponentDidUpdate
useEffect(() => {
var div = d3.select(divRef.current);
applyStyle(div.select("svg"));
});
//Render
return <div id="hierarchyTree" ref={divRef} />;
}
这里是Sandbox.
如果有人感兴趣,我会找到解决方案。
事实是更新组件时没有保存模拟。所以我为它创建了 ref。
const simulationRef = React.useRef(simulation)
并在useEffect部分替换它
//ComponentDidUpdate
useEffect(() => {
simulationRef.current.force("link").strength(strength)
simulationRef.current.alpha(1).restart()
console.log(simulationRef.current)
}, [strength])
之后一切正常。
我正在尝试使用 React 制作 Force-Directed Tree 并且它有效。但是如果我通过 props 将它传递到组件之外,我无法修改 "link strength"。
老实说,我可以更改 "strength",但我需要将 d3 svg 附加到我的 react ref div 之后才能看到更改。整个图将被重新绘制。
我找到了example by Mike Bostock。他建议通过使用 simulation.alpha 和 simulation.restart 重新加热模拟来修改力导向图的参数。但我不能让它与反应一起工作。没有任何反应。
这是我的代码:
export default function Hierarchy(props) {
const {
strength,
lineColor,
lineStroke,
width,
height,
nodeSize,
nodeColor,
} = props;
const root = d3.hierarchy(data);
const links = root.links();
const nodes = root.descendants();
const svg = d3.create("svg");
const link = svg
.append("g")
.selectAll("line")
.data(links)
.join("line");
const node = svg
.append("g")
.selectAll("circle")
.data(nodes)
.join("circle");
function applyStyle(selectionSVG) {
selectionSVG
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height]);
selectionSVG
.selectAll("circle")
.attr("r", nodeSize)
.attr("fill", nodeColor)
selectionSVG
.selectAll("line")
.attr("stroke", lineColor)
.attr("stroke-width", lineStroke);
}
applyStyle(svg);
const divRef = React.useRef(null);
const linkForce = d3
.forceLink(links)
.id(d => d.id)
.distance(0)
.strength(strength);
const simulation = d3
.forceSimulation(nodes)
.force("link", linkForce)
.force("charge", d3.forceManyBody().strength(-500))
.force("x", d3.forceX())
.force("y", d3.forceY());
simulation.on("tick", () => {
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("cx", d => d.x).attr("cy", d => d.y);
});
//ComponentDidMount
useEffect(() => {
//Append d3 svg to ref div
var div = d3.select(divRef.current);
if (div.node().firstChild) {
div.node().removeChild(div.node().firstChild);
}
div.node().appendChild(svg.node());
}, []);
//ComponentDidUpdate
useEffect(() => {
simulation.force("link").strength(strength);
simulation.alpha(1).restart();
}, [strength]);
//ComponentDidUpdate
useEffect(() => {
var div = d3.select(divRef.current);
applyStyle(div.select("svg"));
});
//Render
return <div id="hierarchyTree" ref={divRef} />;
}
这里是Sandbox.
如果有人感兴趣,我会找到解决方案。
事实是更新组件时没有保存模拟。所以我为它创建了 ref。
const simulationRef = React.useRef(simulation)
并在useEffect部分替换它
//ComponentDidUpdate
useEffect(() => {
simulationRef.current.force("link").strength(strength)
simulationRef.current.alpha(1).restart()
console.log(simulationRef.current)
}, [strength])
之后一切正常。