【发布时间】:2018-09-20 05:06:17
【问题描述】:
我正在尝试使用react 和d3 创建强制布局。我被困在为什么我的链接没有被创建。点击和拖动效果不佳,但这是现在的另一个问题(如果它也能在这个线程上解决,我会很高兴)。我基本上是从here 复制粘贴强制布局的代码并将其转换为丑陋的React15 代码。我的代码是这样的
import PropTypes from "prop-types";
import React from "react";
import { render } from "react-dom";
import d3 from "d3";
import * as force from "d3-force";
import * as selection from "d3-selection";
import * as drag from "d3-drag";
const style = {
height: "200px",
width: "415px",
// padding: '15px',
boxShadow: "grey 0px 0px 3px 1px",
margin: "5px",
paddingLeft: "15px",
marginRight: "20px"
};
export default class ForcedGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
graphData: this.props.nodeLinkObject || {
nodes: [
{ id: "node1", group: 1 },
{ id: "node2", group: 2 },
{ id: "node3", group: 3 },
{ id: "node4", group: 2 },
{ id: "node5", group: 3 },
{ id: "node6", group: 3 }
],
links: [
{ source: "node1", target: "node2", value: 5 },
{ source: "node1", target: "node4", value: 10 },
{ source: "node2", target: "node3", value: 12 },
{ source: "node4", target: "node5", value: 9 },
{ source: "node4", target: "node6", value: 3 }
]
}
};
}
shouldComponentUpdate() {
// Prevents component re-rendering
return false;
}
setRef = component => {
// D3 Code to create the chart
// using this._rootNode as container
const svg = component;
const width = 960;
const height = 600;
const simulation = force
.forceSimulation()
.force("link", force.forceLink().id(d => d.id))
.force("charge", force.forceManyBody())
.force("center", force.forceCenter(width / 2, height / 2));
const graph = this.state.graphData;
this.drawGraph(svg, graph, simulation);
};
dragstarted = (d, simulation) => {
if (!selection.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
};
dragged = d => {
d.fx = selection.event.x;
d.fy = selection.event.y;
};
dragended = (d, simulation) => {
if (!selection.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
};
drawGraph = (svg, graph, simulation) => {
const colorMap = {
"1": "red",
"2": "blue",
"3": "green"
};
const node = selection
.select(svg)
.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter()
.append("circle")
.attr("r", 10)
.attr("fill", d => colorMap[d.group])
.attr("transform", "translate(200,100)")
.call(
drag
.drag()
.on("start", d => this.dragstarted(d, simulation))
.on("drag", d => this.dragged(d))
.on("end", d => this.dragended(d, simulation))
);
const link = selection
.select(svg)
.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter()
.append("line")
.attr("stroke-width", d => Math.sqrt(d.value))
.attr("fill", "red")
.attr("transform", "translate(200,100)");
node.append("title").text(d => d.id);
simulation.nodes(graph.nodes).on("tick", this.ticked(link, node));
simulation.force("link").links(graph.links);
};
ticked = (link, node) => {
node.attr("cx", d => d.x * 5).attr("cy", d => d.y * 5);
link
.attr("x1", d => d.source.x * 5)
.attr("y1", d => d.source.y * 5)
.attr("x2", d => d.target.x * 5)
.attr("y2", d => d.target.y * 5);
};
render() {
return (
<div className="columns large-12 small-12 medium-12" style={style}>
<svg width="960" height="600" ref={this.setRef} />
</div>
);
}
}
ForcedGraph.propTypes = {
nodeLinkObject: PropTypes.shape({
nodes: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
group: PropTypes.number
})
),
links: PropTypes.arrayOf(
PropTypes.shape({
source: PropTypes.string,
target: PropTypes.string,
value: PropTypes.number
})
)
}).isRequired
};
render(<ForcedGraph />, document.getElementById("root"));
这里的主要问题是我的链接没有获得x 和y 属性。我通过控制台记录我的this.ticked 方法发现了这么多。
ticked = (link, node) => {
node.attr("cx", d => d.x * 5).attr("cy", d => d.y * 5);
link
.attr("x1", d => {
console.log(d, d.source);
return d.source.x * 5;
})
.attr("y1", d => d.source.y * 5)
.attr("x2", d => d.target.x * 5)
.attr("y2", d => d.target.y * 5);
};
我在控制台日志记录中发现的内容非常奇特。
该对象确实具有 source.x 属性,但在访问时它不存在。所以不知何故,该对象在我访问它之后发生了变异。或者可能是别的东西。
我已经复制了我的问题here
如果这个问题得到解决,我会很高兴。它让我的大脑炸了一整天。
我通过与模块分开导入解决了所选答案和拖累的问题(或者至少我认为这改变了一切)
import PropTypes from 'prop-types';
import React from 'react';
import {render} from 'react-dom';
import {forceSimulation, forceLink, forceManyBody, forceCenter} from 'd3-force';
import {select} from 'd3-selection';
import {drag} from 'd3-drag';
export default class ForcedGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
graphData: this.props.nodeLinkObject || {
nodes: [
{id: 'node1', group: 1},
{id: 'node2', group: 2},
{id: 'node3', group: 3},
{id: 'node4', group: 2},
{id: 'node5', group: 3},
{id: 'node6', group: 3},
],
links: [
{source: 'node1', target: 'node2', value: 5},
{source: 'node1', target: 'node4', value: 10},
{source: 'node2', target: 'node3', value: 12},
{source: 'node4', target: 'node5', value: 9},
{source: 'node4', target: 'node6', value: 3},
],
},
};
}
shouldComponentUpdate() {
// Prevents component re-rendering
return false;
}
setRef = component => {
// D3 Code to create the chart
// using this._rootNode as container
const svg = component;
const width = 415;
const height = 200;
const simulation = forceSimulation()
.force('link', forceLink().id(d => d.id))
.force('charge', forceManyBody())
.force('center', forceCenter(width / 2, height / 2));
const graph = this.state.graphData;
this.drawGraph(svg, graph, simulation);
};
dragstarted = (simulation, d) => {
if (!getEvent().active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
};
dragged = d => {
d.fx = getEvent().x;
d.fy = getEvent().y;
};
dragended = (simulation, d) => {
if (!getEvent().active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
};
drawGraph = (svg, graph, simulation) => {
const colorMap = {
'1': 'red',
'2': 'blue',
'3': 'green',
};
const node = select(svg)
.append('g')
.attr('class', 'nodes')
.selectAll('circle')
.data(graph.nodes)
.enter()
.append('circle')
.attr('r', 10)
.attr('fill', d => colorMap[d.group])
.attr('transform', 'translate(0,0)')
.call(
drag()
.on('start', d => this.dragstarted(simulation, d))
.on('drag', d => this.dragged(d))
.on('end', d => this.dragended(simulation, d))
);
const link = select(svg)
.append('g')
.attr('class', 'links')
.selectAll('line')
.data(graph.links)
.enter()
.append('line')
.attr('stroke-width', d => Math.sqrt(d.value))
.attr('style', 'stroke: #999; stroke-opacity: 0.6;')
.attr('transform', 'translate(0,0)');
node.append('title').text(d => d.id);
simulation.nodes(graph.nodes).on('tick', () => this.ticked(link, node));
simulation.force('link').links(graph.links);
};
ticked = (link, node) => {
node.attr('cx', d => d.x).attr('cy', d => d.y);
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);
};
render() {
return (
<div className="column large-12 medium-12 small-12" style={style}>
<svg width="415" height="200" ref={this.setRef} />
</div>
);
}
}
【问题讨论】:
标签: reactjs d3.js force-layout