D3js力导向图搭建
d3js是一个可以基于数据来操作文档的JavaScript库。可以使用HTML,CSS,SVG以及Canvas来展示数据。力导向图能够用来表示节点间多对多的关系。
实现效果:连线有箭头,点击节点能改变该节点颜色和所连接的线的粗细,缩放、拖拽。
版本:4.X
安装和导入
npm安装:npm install d3
前端导入:import * as d3 from 'd3';
import { chartReq} from './actionCreator';
import './Chart.less';
const WIDTH = 1900;
const HEIGHT = 580;
const R = 30;
let simulation;
class Chart extends Component {
constructor(props,context) {
super(props,context);
this.print = this.print.bind(this);
this.forceChart = this.forceChart.bind(this);
this.state = {
};
}
componentWillMount() {
this.props.dispatch(push('/Chart'));
}
componentDidMount() {
this.print();
}
print() {
let callback = (res) => { // callback获取后台返回的数据,并存入state
let nodeData = res.data.nodes;
let relationData = res.data.rels;
this.setState({
nodeData: res.data.nodes,relationData: res.data.rels,});
let nodes = [];
for (let i = 0; i < nodeData.length; i++) {
nodes.push({
id: (nodeData[i] && nodeData[i].id) || '',name: (nodeData[i] && nodeData[i].name) || '',type: (nodeData[i] && nodeData[i].type) || '',definition: (nodeData[i] && nodeData[i].definition) || '',});
}
let edges = [];
for (let i = 0; i < relationData.length; i++) {
edges.push({
id: (relationData[i] && (relationData[i].id)) || '',source: (relationData[i] && relationData[i].start.id) || '',target: (relationData[i] && relationData[i].end.id) || '',tag: (relationData[i] && relationData[i].name) || '',});
}
this.forceChart(nodes,edges); // d3力导向图内容
};
this.props.dispatch(chartReq({ param: param },callback));
}
// func
forceChart(nodes,edges) {
this.refs['theChart'].innerHTML = '';
// 函数内其余代码请看拆解代码
}
render() {
return (
<Row style={{ minWidth: 900 }}>
<div className="outerDiv">
<div className="theChart" id="theChart" ref="theChart">
</div>
</div>
</Row>
);
}
}
Chart.propTypes = {
dispatch: PropTypes.func.isrequired,};
function mapStateToProps(state) {
return {
};
}
const WrappedChart = Form.create({})(Chart);
export default connect(mapStateToProps)(WrappedChart);
二、拆解代码
1.组件
整个图都将在div里绘制。
2.构造节点和连线
名称
});
}
let edges = []; // 连线
for (let i = 0; i < relationData.length; i++) {
edges.push({
id: (relationData[i] && (relationData[i].id)) || '',source: (relationData[i] && relationData[i].start.id) || '',// 开始节点
target: (relationData[i] && relationData[i].end.id) || '',// 结束节点
tag: (relationData[i] && relationData[i].name) || '',// 连线
名称
});
}
具体怎么构造依据你们的项目数据。
3.定义力模型
d.id).distance(150))
.force('collision',d3.forceCollide(1).strength(0.1))
.force('center',d3.forceCenter(WIDTH / 2,HEIGHT / 2))
.force('charge',d3.forceManyBody().strength(-1000).distanceMax(800));
通过simulation.force()设置力,可以设置这几种力:
- Centering:中心力,设置图中心点位置。
- Collision:节点碰撞作用力,.strength参数范围为[0,1]。
- Links:连线的作用力;.distance设置连线两端节点的距离。
- Many-Body:.strength的参数为正时,模拟重力,为负时,模拟电荷力;.distanceMax的参数设置最大距离。
Positioning:给定向某个方向的力。
通过simulation.on监听力图元素位置变化。
4.绘制svg
{
console.log('click',d3.event.target.tagName);
})
.call(zoom); // 缩放
const g = svg.append('g'); // 则svg中创建g
创建svg,在svg里创建g,将节点连线等内容放在g内。
- select:选择第一个对应的元素
- selectAll:选择所有对应的元素
- append:创建元素
5.绘制连线
{ return d && 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y; }) // 遍历所有数据,d表示当前遍历到的数据,返回绘制的贝塞尔曲线
.attr('id',(d,i) => { return i && 'edgepath' + i; }) // 设置id,用于连线
文字
.attr('marker-end','url(#arrow)') // 根据箭头
标记的id号
标记箭头
.style('stroke','#000') // 颜色
.style('stroke-width',1); // 粗细
连线用贝塞尔曲线绘制:(M 起点X 起点y L 终点x 终点y)
6.绘制连线上的箭头
Box','0 0 20 20') // view
Box
.attr('refX',9.3 + R) // 偏离圆心距离
.attr('refY',5) // 偏离圆心距离
.attr('orient','auto'); // 绘制方向,可设定为:auto(
自动确认方向)和 角度值
arrowheads.append('path')
.attr('d','M0,0 L0,10 L10,5 z') // d: 路径描述,贝塞尔曲线
.attr('fill','#000'); // 填充颜色
- viewport:可视区域
- viewBox:实际大小,会自动缩放填充viewport
7.绘制节点
{ // 点击事件
console.log('click');
})
.call(drag); // 拖拽单个节点带动整个图
创建圆作为节点。
.call()调用拖拽函数。
8.节点名称
{ //
文字内容
return d && d.name; // 遍历nodes每一项,
获取对应的name
});
因为文字在节点上层,如果没有设置禁止鼠标事件,点击文字将无法响应点击节点的效果,也无法拖拽节点。
9.连线名称
{ return i && '#edgepath' + i; }) //
文字布置在对应id的连线上
.style('pointer-events','none')
.text((d) => { return d && d.tag; });
10.鼠标移到节点上有气泡提示
{ // .text设置气泡
提示内容
return node.definition;
});
11.监听图元素的位置变化
{
// 更新节点坐标
nodesCircle.attr('transform',(d) => {
return d && 'translate(' + d.x + ',' + d.y + ')';
});
// 更新节点
文字坐标
nodesTexts.attr('transform',(d) => {
return 'translate(' + (d.x) + ',' + d.y + ')';
});
// 更新连线位置
edgesLine.attr('d',(d) => {
const path = 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
return path;
});
// 更新连线
文字位置
edgesText.attr('transform',i) => {
return 'rotate(0)';
});
});
12.拖拽
13.缩放
获取g的缩放系数和平移的坐标值。
}
function onZoomEnd() {
// console.log('zoom end');
}
const zoom = d3.zoom()
// .translateExtent([[0,0],[WIDTH,HEIGHT]]) // 设置或
获取平移区间,默认为[[-∞,-∞],[+∞,+∞]]
.scaleExtent([1 / 10,10]) // 设置最大缩放比例
.on('start',onZoomStart)
.on('zoom',zooming)
.on('end',onZoomEnd);
三、其它效果
1.单击节点时让连接线加粗
{
edges_line.style("stroke-width",function(line){
if(line.source.name==node.name || line.target.name==node.name){
return 4;
}else{
return 0.5;
}
});
})
2.被点击的节点变色
{
nodesCircle.style('fill',(nodeOfSelected) => { // nodeOfSelected:所有节点,node: 选中的节点
if (nodeOfSelected.id === node.id) { // 被点击的节点变色
console.log('node')
return '#36F';
} else {
return '#9FF';
}
});
})
四、在react中使用注意事项
{ // callback
获取后台返回的数据,并存入state
let nodeData = res.data.nodes;
let relationData = res.data.rels;
this.setState({
nodeData: res.data.nodes,});
let nodes = [];
for (let i = 0; i < nodeData.length; i++) {
nodes.push({
id: (nodeData[i] && nodeData[i].id) || '',});
}
let edges = [];
for (let i = 0; i < relationData.length; i++) {
edges.push({
id: (relationData[i] && (relationData[i].id)) || '',target: (relationData[i] && relationData[i].end.id) || '',});
}
this.forceChart(nodes,edges); // d3力导向图
内容
};
this.props.dispatch(getDataFromNeo4J({
neo4jrun: 'match p=(()-[r]-()) return p limit 300',},callback));
}
在哪里构造图
因为图是动态的,如果渲染多次(render执行多次,渲染多次),不会覆盖前面渲染的图,反而会造成渲染多次,出现多个图的现象。把构造图的
函数print()放到componentDidMount()内执行,则只会渲染一次。
对节点和连线数据进行增删改操作后,需要再次
调用print()
函数,重新构造图。
从哪里获取数据
数据不从redux
获取,发送请求后callback直接
获取。
五、干货:d3项目查找网址
D3js所有项目检索.http://blockbuilder.org/search/
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。
原文链接:https://www.f2er.com/js/34196.html