Vin*_*ent 4 javascript d3.js reactjs
我一直试图在React中制作一个可拖动的d3力布局一段时间了.React必须能够与图中的节点进行交互.例如,当您单击节点时,React应该能够返回节点的id onClick.
我根据Shirley Wu的一个例子制作了4个组件.一个App组件,用于保存图形数据的状态并呈现Graph组件.图形组件呈现节点和链接组件.这样,可点击节点部分就可以解决了.
当页面呈现时,节点将只能拖动几秒钟.渲染页面后,您可以立即拖动节点,然后突然,被拖动的节点完全停在一个位置.此时,其他节点也不能再被拖动.我希望能够随时拖动节点.
我可以在网上找到一些关于在图形后面创建画布,设置填充和指针事件的提示.关于letting或d3或React进行渲染和计算的讨论也很多.我尝试使用React的所有生命周期方法,但我无法使用它.
你可以在这里找到一个实时样本:https://codepen.io/vialito/pen/WMKwEr
请记住,圈子只能点击几秒钟.然后他们会留在同一个地方.所有浏览器和每次刷新后的行为都是相同的.当您记录拖动功能时,您会看到它在拖动时会分配新的坐标,但圆圈不会显示在它的新位置.
我非常渴望了解这个问题的原因,如果你甚至可以提出解决方案,那将是非常酷的.
App.js
class App extends React.Component {
constructor(props){
super(props)
this.state = {
data : {"nodes":
[
{"name": "fruit", "id": 1},
{"name": "apple", "id": 2},
{"name": "orange", "id": 3},
{"name": "banana", "id": 4}
],
"links":
[
{"source": 1, "target": 2},
{"source": 1, "target": 3}
]
}
}
}
render() {
return (
<div className="graphContainer">
<Graph data={this.state.data} />
</div>
)
}
}
class Graph extends React.Component {
componentDidMount() {
this.d3Graph = d3.select(ReactDOM.findDOMNode(this));
var force = d3.forceSimulation(this.props.data.nodes);
force.on('tick', () => {
force
.force("charge", d3.forceManyBody().strength(-50))
.force("link", d3.forceLink(this.props.data.links).distance(90))
.force("center", d3.forceCenter().x(width / 2).y(height / 2))
.force("collide", d3.forceCollide([5]).iterations([5]))
const node = d3.selectAll('g')
.call(drag)
this.d3Graph.call(updateGraph)
});
}
render() {
var nodes = this.props.data.nodes.map( (node) => {
return (
<Node
data={node}
name={node.name}
key={node.id}
/>);
});
var links = this.props.data.links.map( (link,i) => {
return (
<Link
key={link.target+i}
data={link}
/>);
});
return (
<svg className="graph" width={width} height={height}>
<g>
{nodes}
</g>
<g>
{links}
</g>
</svg>
);
}
}
Run Code Online (Sandbox Code Playgroud)
Node.js的
class Node extends React.Component {
componentDidMount() {
this.d3Node = d3.select(ReactDOM.findDOMNode(this))
.datum(this.props.data)
.call(enterNode)
}
componentDidUpdate() {
this.d3Node.datum(this.props.data)
.call(updateNode)
}
handle(e){
console.log(this.props.data.id + ' been clicked')
}
render() {
return (
<g className='node'>
<circle ref="dragMe" onClick={this.handle.bind(this)}/>
<text>{this.props.data.name}</text>
</g>
);
}
}
Run Code Online (Sandbox Code Playgroud)
Link.js
class Link extends React.Component {
componentDidMount() {
this.d3Link = d3.select(ReactDOM.findDOMNode(this))
.datum(this.props.data)
.call(enterLink);
}
componentDidUpdate() {
this.d3Link.datum(this.props.data)
.call(updateLink);
}
render() {
return (
<line className='link' />
);
}
}
Run Code Online (Sandbox Code Playgroud)
D3Functions.js
const width = 1080;
const height = 250;
const color = d3.scaleOrdinal(d3.schemeCategory10);
const force = d3.forceSimulation();
const drag = () => {
d3.selectAll('g')
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragging)
.on("end", dragEnded));
};
function dragStarted(d) {
if (!d3.event.active) force.alphaTarget(0.3).restart()
d.fx = d.x
d.fy = d.y
}
function dragging(d) {
d.fx = d3.event.x
d.fy = d3.event.y
}
function dragEnded(d) {
if (!d3.event.active) force.alphaTarget(0)
d.fx = null
d.fy = null
}
const enterNode = (selection) => {
selection.select('circle')
.attr("r", 30)
.style("fill", function(d) { return color(d.name) })
selection.select('text')
.attr("dy", ".35em")
.style("transform", "translateX(-50%,-50%")
};
const updateNode = (selection) => {
selection.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
};
const enterLink = (selection) => {
selection.attr("stroke-width", 2)
.style("stroke","yellow")
.style("opacity",".2")
};
const updateLink = (selection) => {
selection
.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);
};
const updateGraph = (selection) => {
selection.selectAll('.node')
.call(updateNode)
.call(drag);
selection.selectAll('.link')
.call(updateLink);
};
Run Code Online (Sandbox Code Playgroud)
您可以在代码中定义两次力模拟.第一次 - 你的codepen中的字符串7和第二次 - 字符串113.你的dragStarted和dragEnded函数(全局定义)使用来自字符串7的强制模拟,但没有指定(你没有将节点,链接和其他参数传递给它).
您应该在定义和指定力模拟时将这些函数移动到方法中,因此组件的componentDidMount方法Graph应该如下所示(您还应该重写您的tick处理函数,并且只设置一次force params(现在您在每个tick上执行),检查我的笔叉):
componentDidMount() {
this.d3Graph = d3.select(ReactDOM.findDOMNode(this));
var force = d3.forceSimulation(this.props.data.nodes)
.force("charge", d3.forceManyBody().strength(-50))
.force("link", d3.forceLink(this.props.data.links).distance(90))
.force("center", d3.forceCenter().x(width / 2).y(height / 2))
.force("collide", d3.forceCollide([5]).iterations([5]))
function dragStarted(d) {
if (!d3.event.active) force.alphaTarget(0.3).restart()
d.fx = d.x
d.fy = d.y
}
function dragging(d) {
d.fx = d3.event.x
d.fy = d3.event.y
}
function dragEnded(d) {
if (!d3.event.active) force.alphaTarget(0)
d.fx = null
d.fy = null
}
const node = d3.selectAll('g.node')
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragging)
.on("end", dragEnded)
);
force.on('tick', () => {
this.d3Graph.call(updateGraph)
});
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1283 次 |
| 最近记录: |