前言

我不负责拓扑组件是对的,就应该给大佬去维护,大佬参考github上issue中的讨论与代码思路,实现了线条上的流动效果,牛的牛的,我赶紧学习一波,下面代码实现都是初步思路,没有进行实现优化,如防抖优化、定时器优化等,根据项目需求自行优化即可。

vis-network中没有提供流动的配置,这个功能需要自己去实现。

实现思路

思路是需要自己绘制一个canvas图层,和地图功能类似,在自定义canvas上实现流动功能。但是简单的实现流动功能还是不行的,拓扑图是支持平移、缩放等操作的,所以自定义canvas也是适配这些操作,在拓扑图平移、缩放等操作时,流动效果要一直在线条的正确位置才行。

主要思路如下:

自定义canvas图层,插入vis-network的canvas图层中,当作子canvas,子canvas的大小与位置要与父canvas完全一致,两者是重叠关系。为拓扑图中的每一个线条增加流动效果,并且是无限循环的。当拓扑图触发 draging,zoom,resize等事件时,流动效果的位置也要与线条位置保持一致。

自定义canvas图层

在拓扑图渲染完成后,开始绘制自定义图层。关键代码如下:

import * as lineAnimation from './lineAnimation';

const network = new vis.Network(dom, { nodes, edges }, options);

lineAnimation.setAnimationCanvas(network); // 绘制自定义canvas

function setAnimationCanvas(container) {

let trafficCanvas = container.body.container.getElementsByClassName(

'networkTrafficCanvas',

)[0];

if (trafficCanvas === undefined) {

const { frame } = container.canvas;

trafficCanvas = document.createElement('canvas');

trafficCanvas.className = 'networkTrafficCanvas';

trafficCanvas.style.position = 'absolute';

trafficCanvas.style.top = 0;

trafficCanvas.style.left = 0;

trafficCanvas.style.zIndex = 1;

trafficCanvas.style.pointerEvents = 'none';

trafficCanvas.style.width = frame.style.width;

trafficCanvas.style.height = frame.style.height;

// trafficCanvas.width = frame.canvas.clientWidth;

// trafficCanvas.height = frame.canvas.clientHeight;

trafficCanvas.width = frame.canvas.width;

trafficCanvas.height = frame.canvas.height;

frame.appendChild(trafficCanvas);

}

}

线条流动效果

lineAnimation.getAllEdges(network);

function getAllEdges(container) {

// 清除定时器、清空画布

removeCanvas(container);

// 绘制流动节点

lineAnimate(container);

}

其中绘制节点前的关键处理是找到所有的线条与要配置好自定义canvas,要与vis-network中自带canvas配置保持一致。

const canvas = container.body.container.getElementsByClassName(

'networkTrafficCanvas',

)[0];

const context = canvas.getContext('2d');

clearTimeout(timeout);

const scale = container.getScale();

const { edges } = container.body;

const points = Object.values(edges);

const { translation } = container.body.view;

// 设置缩放和位置

context.setTransform(1, 0, 0, 1, 0, 0); // 6个参数分别为水平缩放、水平倾斜、垂直倾斜、垂直缩放、水平移动、垂直移动

context.translate(translation.x, translation.y);

context.scale(scale, scale);

然后是要通过遍历绘制流动节点,单个节点的绘制如下。这里不展示调用drawLine函数的传参与线条数组遍历处理是因为我们的处理有局限,只能用于直线线条,好像还不支持曲线线条,这里的算法可以自己进行扩展创造。

export function drawLine(container, x, y, arg) {

const ctx = container.body.container

.getElementsByClassName('networkTrafficCanvas')[0]

.getContext('2d');

ctx.beginPath();

const endX = arg.from.x + arg.distanceX * (arg.speed + 0.2);

const endY = arg.from.y + arg.distanceY * (arg.speed + 0.2);

const grd = ctx.createLinearGradient(x, y, endX, endY);

grd.addColorStop(0, 'rgba(255,0,255,0)');

grd.addColorStop(1, 'rgba(88, 206, 255,1)');

ctx.moveTo(x, y);

ctx.lineTo(endX, endY);

ctx.lineCap = 'round';

ctx.lineWidth = 3;

ctx.strokeStyle = grd;

ctx.stroke();

}

最后是添加定时器,不断的重绘,通过改变流动节点的绘制位置来实现流动效果。这里不用重复调用lineAnimate函数,可以自己抽取一下代码进行优化,只需重复调用流动节点的重绘与canvas画布清除操作就行了,这里示例就懒得改了。

timeout = setTimeout(() => {

lineAnimate(container);

}, 100);

window.sessionStorage.setItem('timeout', timeout);

对于清除画布,这里被骗了一次,使用如下方法发现只能清除部分画布内容。

context.clearRect(

0,

0,

canvas.width,

canvas.height,

);

后来发现由于之前的context.translate()操作让canvas的原点改变了,于是又写了一种清除画布思路,不知道还会不会产生问题。因为canvas画布是可以进行缩放的,要处理scale不是1的情况。

// 清除画布

if (scale >= 1) {

context.clearRect(

-Math.abs(translation.x),

-Math.abs(translation.y),

canvas.width,

canvas.height,

);

} else {

context.clearRect(

-Math.abs(translation.x) / scale,

-Math.abs(translation.y) / scale,

canvas.width / scale,

canvas.height / scale,

);

}

拓扑图平移、缩放

不写了不写了,思路很清晰了,这里只参考思路即可,具体实现可优化!

function canvasScale(container) {

// 清除定时器、清空画布

removeCanvas(container);

// 重新绘制节点动画

lineAnimate(container);

}

拓扑图触发resize

这里思路差不多,只是加了一层canvas大小的重新计算,都是可加防抖操作进行处理的,这里懒得加了。

export function canvasTranslate(container) {

// 清除定时器、清空画布

removeCanvas(container);

// 重新计算canvas大小

const canvas = container.body.container.getElementsByClassName(

'networkTrafficCanvas',

)[0];

const { frame } = container.canvas;

canvas.width = frame.canvas.width;

canvas.height = frame.canvas.height;

// 重新绘制节点动画

lineAnimate(container);

}

推荐链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。