我使用d3创建了一个带有图形可视化的React / Redux应用程序。 我尝试在没有强制模拟的情况下绑定图形中的节点和链接,但是我无法在onDrag函数中获取路径属性(实现链接),我在其中处理节点拖动。
我是d3中的新手,可能是一个糟糕的图表实现。使用force实现的大多数节点/链接示例,其中节点和链接之间的绑定是隐式的。我找到了that和that,但它使用了旧的d3 v3和links.each(...)在我的应用中无效。
所以,这是我的React组件的代码:
import React,{Component} from 'react';
import * as d3 from "d3";
class MapPanel extends Component {
constructor(props) {
super(props);
this.state = {
data: {
nodes: [
{id: 'A', x: 100, y: 100},
{id: 'B', x: 200, y: 100},
{id: 'C', x: 100, y: 200},
{id: 'D', x: 200, y: 200},
],
links: [
{ source: 'A', target: "B" },
{ source: 'A', target: "C" },
{ source: 'B', target: "C" },
{ source: 'C', target: "D" },
]
}
};
}
componentDidMount() {
const chart = d3.select(this.chartRef)
.attr('width', window.innerWidth-100)
.attr('height', 500)
.attr("id", "svg");
chart.append('defs').selectAll('marker')
.data(['end'])
.enter()
.append('marker')
.attr('id', "arrowhead")
.attr('refX', 22)
.attr('refY', 0)
.attr('orient', "auto")
.attr('markerWidth', 25)
.attr('markerHeight', 25)
.attr('markerUnits', "strokeWidth")
.attr('xoverflow', "visible")
.attr('viewBox', "0 -5 15 15")
.append('path')
.attr('d', 'M0,-5L10,0L0,5')
.attr('fill', '#f00');
const links = chart.selectAll('link')
.data(this.state.data.links);
links
.enter()
.append('path')
.attr('marker-end','url(#arrowhead)')
.attr("stroke", "black")
.attr('d', (d, i) => {
let sourceNode = this.state.data.nodes.find(node => node.id === d.source);
let targetNode = this.state.data.nodes.find(node => node.id === d.target);
return 'M ' + (sourceNode.x + 25) + ' ' + (sourceNode.y + 25) + ' L ' + (targetNode.x + 25) + ' ' + (targetNode.y + 25);
});
const rects = chart.selectAll('rect')
.data(this.state.data.nodes);
rects
.enter()
.append('rect')
.attr('x', (d, i) => d.x)
.attr('y', (d, i) => d.y)
.attr('width', 50)
.attr('height', 50)
.attr('rx', 10)
.attr('ry', 10)
.merge(rects) //merges enter() and update()
.call(
d3.drag()
.on('start', this.onDragStart)
.on('drag', this.onDrag(links))
.on('end', this.onDragEnd)
);
rects
.exit()
.transition()
.attr('r', 0)
.remove();
}
onDragStart() {
d3.select(this)
.raise() //move dragged element to last position in nodes list to layer element ontop other similar elements
.classed('active', true);
}
onDrag(links) {
return function (d, i) {
d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
/*links.attr('d', function(link, linkIndex) {
if(link.source === d.id) return 'M 10 10 L 100 100';
});*/
}
}
onDragEnd() {
d3.select(this).classed('active', false);
}
render = () =>(
<svg className="cognitive-map" ref={(r) => this.chartRef = r}></svg>
);
}
export default MapPanel;