python力导向图论文_react和d3.js(v4)力导向图force结合使用

本文介绍了如何将d3的力导向图从v3升级到v4,详细讲解了d3.forceSimulation()的使用,以及在react中控制节点和连线的方法。同时,讨论了d3.v4与v3在力导向图实现上的差异,包括节点和连线的初始化、拖拽、缩放等操作的调整。升级过程中需要注意节点数据和连线数据的重新设置,以及事件监听和处理方式的变化。
摘要由CSDN通过智能技术生成

前段时间由于性能要求,需把项目d3的版本从v3升级到v4,据了解d3由于在v4版本之前是没有进行模块化的,所以v3代码的扩展性是比较差的,考虑到长远之计,d3在v4版本算是对代码进行了模块化的重构吧,给开发者提供了一些可定制化的东西,所有api变化较大,这个坑还需各种研究文档才能填完,好了,下面开始我的表演了。

初始化force布局

初始化函数从v3的d3.layout.force()变成v4的d3.forceSimulation(),部分参数设置方式如下:

this.force = d3.forceSimulation().alphaDecay(0.1) // 设置alpha衰减系数

.force("link", d3.forceLink().distance(100)) // distance为连线的距离设置

.force('collide', d3.forceCollide().radius(() => 30)) // collide 为节点指定一个radius区域来防止节点重叠。

.force("charge", d3.forceManyBody().strength(-400)) // 节点间的作用力

为布局添加点和线

this.force.nodes(nodes) // 节点数据

.force('link', d3.forceLink(links).distance(150)); // 连线数据 distance为连线的距离设置

.alpha(1); // 设置alpha值,让里导向图有初始动力

.restart(); // 启动仿真计时器

由于在v4版本中nodes的x、y坐标和加速度vx、vy只在nodes中计算一次,所有在变成有节点或连线增加的时候,必须重新执行一次force.nodes(nodes)和force('link', d3.forceLink(links)),初始化节点的数据结构。如果在v3版本中,只需在布局初始化时执行即可,在d3会在每次force.start()方法执行时重新初始化一次节点和连线的数据结构,这是一个特别需要注意的地方,另外在v4版本中start方法被遗弃,需使用restart方法。

react部分

将节点的dom结构交给react来控制,方便在节点上添加事件。以下为svg渲染部分代码。

render() {

const { width, height, nodes, links, scale, translate, selecting, grabbing } = this.props.store;

return (

className={cn({

grab: !selecting && !grabbing,

grabbing: !selecting && grabbing

})}

>

links.map(link => (

key={`${link.source.uid}_${link.target.uid}`}

ref={child => this.links[`${link.source.uid}_${link.target.uid}`] = child}

x1={link.source.x}

y1={link.source.y}

x2={link.target.x}

y2={link.target.y}/>

))

{

nodes.map(node => (

node={node}

store={this.props.store}

addRef={child => this.nodes[node.uid] = child}/>

))

}

);

}

Node.js 节点

以下为Node Component部分代码

class Node extends Component {

render() {

const { node, addRef, store } = this.props;

const { force } = store;

return (

ref={child => {

this._node = child;

addRef(child);

}}

transform={`translate(${node.x || width / 2},${node.y || height / 2})`}

>

// 节点图片dom

{

node.locked && (

x={10}

y={10}

release={() => { // 解锁节点

node.fixed = false;

node.locked = false;

node.fx = null; // 当节点的fx、fy都为null时,节点处于活动状态

node.fy = null;

force.alpha(0.3).restart(); // 释放锁定节点时需设置alpha值并重启计时器,使得布局可以运动。

}}

/>

)

}

);

}

componentDidMount() {

this._node.__data__ = this.props.node; // 将node节点在d3内部存一份引用,让每次计时器更新的时候自动更改nodes列表中的数据

d3.select(this._node) // 各种事件

.on('click', d => {

// code

})

}

}

Lock.js 节点解除固定按钮。

class Lock extends Component {

render() {

const { x, y, fixed } = this.props;

return (

ref="lock"

xlinkHref="#lock"

x={x}

y={y}

/>

);

}

componentDidMount() {

const { release } = this.props;

d3.select(this.refs.lock)

.on('click', () => {

d3.event.stopPropagation();

release();

});

}

}

仿真计时器 tick

计时器函数,在仿真启动的过程中,计时器的每一帧都会改变一次之前我们在内部存的引用(this._node.__data__ = this.props.node)的node的数据的x值和y值,这时我们需要更新dom结构中的节点和线偏移量。

force.on('tick', () => {

nodes.forEach(node => {

if (!node.lock) {

d3.select(self.nodes[node.uid]).attr('transform', () => `translate(${node.x},${node.y})`);

}

});

links.forEach(link => {

d3.select(self.links[`${link.source.uid}_${link.target.uid}`])

.attr('x1', () => link.source.x)

.attr('y1', () => link.source.y)

.attr('x2', () => link.target.x)

.attr('y2', () => link.target.y);

});

});

在计时器的每一帧中,仿真的alpha系数会不断削减,可通过force.alpha()来获取和设置alpha系数,削减速度由alphaDecay来决定,默认值为0.0228…,衰减系数可通过force.alphaDecay()来获取和设置,当alpha到达一个系数时,仿真将会停止,也就是alpha的目标系数alphaTarget,该值区间为[0,1]. 默认为0,可通过force.alphaTarget()来获取和设置,另外还有一个速度衰减系统velocityDecay,相当于摩擦力。区间为[0,1], 默认为0.4。在每次tick之后,节点的速度都会等于当前速度乘以1-velocityDecay,和alpha衰减类似,速度衰减越慢最终的效果越好,但是如果速度衰减过慢,可能会导致震荡。以上为tick过程的发生。需要注意的是,在v4版本中,tick事件的callback中不带任何参数,在v3版本的'tick'事件中,我们可通过callback(e)中的e.alpha来获取alpha值,而在v4版本中,alpha值只能通过force.alpha()来获取。

拖拽 Drag

创建拖拽操作

let startTime = 0;

this.drag = d3.drag()

.on('start', (d) => {

startTime = (new Date()).getTime();

d3.event.sourceEvent.stopPropagation();

if (!d3.event.active) {

this.force.alphaTarget(0.3).restart(); // 当前alpha值为0,需设置alphaTarget让节点动起来

}

d.fx = d.x;

d.fy = d.y;

})

.on('drag', d => {

this.grabbing = true;

d.fx = d3.event.x;

d.fy = d3.event.y;

})

.on('end', d => {

const nowTime = (new Date()).getTime();

if (!d3.event.active) {

this.force.alphaTarget(0); // 让alpha目标值值恢复为默认值0

}

if (nowTime - startTime >= 150) { // 操作150毫秒的拖拽固定节点

d.fixed = true;

d.locked = true;

}

this.grabbing = false;

});

将拖拽操作应用到指定的选择集。

d3.select('#outg').selectAll('.node').call(this.drag);

在内部,拖拽操作通过selection.on来为元素添加监听事件. 事件监听器使用 .drag 来标识这是一个拖拽事件。拖拽drag的v4版本与v3不同的是,v3通过force.drag()创建拖拽操作,拖拽过程事件使用dragstart、drag、dragend,在拖拽过程中d3内部自动设置alpha相关系数让节点运动起来,而在v4中版本中需要手动设置。

缩放 Zoom

在v4版本中,缩放操作通过transform对象进行,可以通过d3.zoomTransform(selection.node())获取指定节点的缩放状态,也可以通过d3.event.transform来获取当前正在缩放的节点的缩放状态。

与拖拽类似,需要先创建缩放操作。

const self = this;

const outg = d3.select('#outg');

this.zoomObj = d3.zoom()

.scaleExtent([0.2, 4]) // 缩放范围

.on('zoom',() => {

const transform = d3.event.transform;

self.scale = transform.k; // 保存当前缩放大小

self.translate = [transform.x, transform.y]; // 保存当前便宜量

outg.attr('transform', transform); // 设置缩放和偏移量 transform对象自带toString()方法

})

.on('end', () => {

// code

})

将缩放操作应用于选择集,并取消双击操作

const svg = d3.select('#svg');

svg.call(this.zoomObj).on('dblclick.zoom', null);

如果要禁止滚轮滚动缩放,可以在讲zoom事件应用于选择集之后移除zoom事件中的滚轮事件:

svg.call(this.zoomObj).on("wheel.zoom", null);

当缩放事件被调用,d3.event会被设置为当前的zoom事件,zoom event对象由以下几部分组成:

target - 当前的缩放zoom behavior。

type - 事件类型:“start”, “zoom” 或者 “end”,参考 zoom.on。

transform - 当前的zoom transform(缩放变换)。

sourceEvent - 原始事件, 比如 mousemove 或 touchmove。

通过按钮缩放、定位视图。

this.zoomObj.transform(d3.select('#svg'), d3.zoomIdentity.translate(newX,newY).scale(newScale))

在v3版本中,可以通过zoom.scale(s)和zoom.translate(x, y)设置缩放和偏移量后通过使用'zoom.event(selection)'方法应用到指定选择节点,而在v4中版本需要通过d3.zoomIdentity创建新transform对象,并通过translate(x, y)和scale(s)方法设置偏移量和缩放级别,然后将该transform应用到选择集中。另外也可以通过zoom.translateBy(selection, x, y)、zoom.translateTo(selection, x, y)、zoom.scaleBy(selection, k)、zoom.scaleTo(selection, k)方法进行变换。

小结

由于api变动较大,v3升级v4需要耐心看api,查看各个部分的变化,所以,升级需谨慎。最后附上d3.js v4.0中文api。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值