Loading...
在 X6 中,为节点或边添加动画能够显著提升画布的视觉效果和交互体验。支持多种动画实现方式,包括 animate API、CSS 动画和 SMIL 动画等。其中,animate API 功能最为强大,本文将重点介绍其使用方法。以下是一个基础动画示例:
import { Graph } from '@antv/x6'const graph = new Graph({container: document.getElementById('container'),height: 300,background: {color: '#F2F7FA',},})const node = graph.addNode({shape: 'rect',label: 'hello X6',x: 100,y: 140,width: 100,height: 50,attrs: {body: {strokeWidth: 1,rx: 6,ry: 6,},},})node.animate(// 将节点的位置从原本的位置通过动画移动到 (300, 140){ 'position/x': 300 },// 动画持续时间为 1000ms,当动画运动到终点后下次将从终点开始运动,重复次数为无穷{ duration: 1000, direction: 'alternate', iterations: Infinity },)
对于熟悉 Web Animations API 的开发者而言,可以明显看出 X6 的 animate API 是基于 Web Animations API 标准实现的。因此,其使用方式与 Web Animations API 基本兼容,并实现了该标准中的大部分核心功能。
animate API 接收两个关键参数:
duration 为 1000 表示动画运行一次的时间为 1000msdirection 为 alternate 表示动画将在起点和终点之间来回运动iterations 为 Infinity 表示动画将无限次重复部分开发者可能对使用 position/x 而非直接使用 x 属性存在疑问,这是因为 X6 的动画系统基于 cell.setPropByPath() 方法实现,该方法通过属性路径来修改属性值。节点的属性结构通常如下所示(也可通过 cell.prop() 方法获取):
{// ...position: {x: 370, y: 180},size: {width: 100, height: 60},attrs: {// ...}}
所以给位置添加动画是通过 position/x 来设置动画效果,子属性使用 / 来拼接,其他动画属性同理。例如想要给节点大小添加动画,就可以这样写:
// 将节点的宽度设置为 200 并且添加动画node.animate({'size/width': 200 },1000)
理解了动画支持的属性机制,那么就可以给 X6 节点内置属性外的额外属性也添加动画效果,例如给自定义节点 data 内的 ratio 自定义属性添加动画效果:
import { Dom, Graph, Shape } from '@antv/x6'Shape.HTML.register({shape: 'custom-html',width: 160,height: 80,effect: ['data'],html(cell) {// 自定义的 HTML 节点接收一个面积数据来控制 div 大小,可以对该自定义属性添加动画效果const { ratio } = cell.getData() ?? {}const div = document.createElement('div')const area = 12000const width = Math.sqrt(area / ratio)const height = width * ratioDom.css(div, {width,height,background: '#fff',borderRadius: 10,border: "1px solid #000"})return div},})const graph = new Graph({container: document.getElementById('container'),height: 300,background: {color: '#F2F7FA',},})graph.addNode({shape: 'custom-html',x: 80,y: 80,data: { ratio: 1 },animation: [// 对自定义节点的宽高比添加动画效果[{ 'data/ratio': 3 / 5 }, { duration: 1000, iterations: Infinity }],],})
除了通过 API 的形式来调用添加动画,animate API 还支持通过节点的 animation 配置的方式来添加动画,可以省去一个额外的步骤,在很多场景下会很实用,下面的例子将实现一个和上面的例子一样的动画:
import { Graph } from '@antv/x6'const graph = new Graph({container: document.getElementById('container'),height: 300,background: {color: '#F2F7FA',},})graph.addNode({shape: 'rect',label: 'hello X6',x: 100,y: 140,width: 100,height: 50,attrs: {body: {strokeWidth: 1,rx: 6,ry: 6,},},// 将动画相关配置直接写到节点的配置中,当节点被添加到画布中时,动画会自动开始animation: [[{ 'position/x': 300 },{ duration: 1000, direction: 'alternate', iterations: Infinity },],],})
animation 配置是一个数组:
animate 的参数)一个常见的场景是对很多节点添加相同的动画,不管是重复调用 animate API 还是通过配置的方式来添加动画,都需要对每一个节点进行重复的操作,而在 X6 中本身就支持通过注册 Shape 的方式来复用节点,同样动画也支持被注册进 Shape 中,来使一批节点都支持动画:
import { Graph } from '@antv/x6'Graph.registerNode('animated-rect',{inherit: 'rect',width: 150,height: 60,attrs: {body: {strokeWidth: 1,rx: 6,ry: 6,},},// 注册 Shape 时先添加好动画效果animation: [[{ 'position/x': 300 },{ duration: 1000, direction: 'alternate', iterations: Infinity },],],},true,)const graph = new Graph({container: document.getElementById('container'),height: 300,background: {color: '#F2F7FA',},})graph.addNode({shape: 'animated-rect',x: 100,y: 50,label: "hello X6"})graph.addNode({shape: 'animated-rect',x: 100,y: 150,label: "hello Animation"})
X6 中的 animate API 基于 Web Animations API 设计,所以同样支持灵活的动画控制能力,例如暂停、恢复、取消等。下面是一个简单的动画控制例子:
import { Graph } from '@antv/x6'const graph = new Graph({container: document.getElementById('container'),height: 300,background: {color: '#F2F7FA',},})const node = graph.addNode({shape: 'rect',label: '点击我将暂停动画',x: 100,y: 140,width: 150,height: 60,attrs: {body: {strokeWidth: 1,rx: 6,ry: 6,},},animation: [[{ 'position/x': 300 },{ duration: 1000, direction: 'alternate', iterations: Infinity },],],})graph.on('node:click', ({ cell }) => {if (cell === node) {const [animation] = cell.getAnimations()animation.pause()}})
除了暂停动画的能力外,animate API 总共支持以下能力:
pause() 暂停动画play() 播放/恢复动画cancel() 取消动画finish() 结束动画reverse() 反向播放动画updatePlaybackRate() 更新动画播放速度X6 中的动画事件支持两种风格,一种是基于 Web Animations API 的设计,例如通过设置 onfinish 等事件来监听动画的事件:
import { Graph } from '@antv/x6'const graph = new Graph({container: document.getElementById('container'),height: 300,background: {color: '#F2F7FA',},})const node = graph.addNode({shape: 'rect',label: '点击我将开始动画',x: 100,y: 140,width: 150,height: 60,attrs: {body: {strokeWidth: 1,rx: 6,ry: 6,},},})graph.on('node:click', ({ cell }) => {if (cell !== node) returnconst animation = node.animate({ 'position/x': [100, 300] },{ duration: 1000, direction: 'alternate', iterations: 1 },)// 通过 onfinish 监听动画事件animation.onfinish = () => {console.log('动画结束')}})
基于 Web Animations API 的设计总共支持如下事件:
onfinish 动画结束oncancel 动画取消另外一种是基于 X6 自身的设计,通过 on API 来监听动画事件:
import { Graph } from '@antv/x6'const graph = new Graph({container: document.getElementById('container'),height: 300,background: {color: '#F2F7FA',},})const node = graph.addNode({shape: 'rect',label: '点击我将开始动画',x: 100,y: 140,width: 150,height: 60,attrs: {body: {strokeWidth: 1,rx: 6,ry: 6,},},})graph.on('node:click', ({ cell }) => {if (cell !== node) returnnode.animate({ 'position/x': [100, 300] },{ duration: 1000, direction: 'alternate', iterations: 1 },)})// 通过 on 监听动画事件graph.on('node:animation:finish', () => {console.log('动画结束')})
基于 X6 自身的设计总共支持如下事件:
cell:animation:finish 动画结束cell:animation:cancel 动画取消X6 事件系统详细可见事件。