- 数据可视化
【免费下载链接】cytoscape.js
Graph theory (network) library for visualisation and analysis
本指南以 documentation/md/core/animate.md 为核心,系统讲解 Cytoscape.js 中针对**核心实例(core)**的动画 API:手动平移(pan)与缩放(zoom)、将视口适配到指定元素(fit)、动画队列(queue)、缓动函数(easing)与生命周期控制。读者学完后,可以独立编写视口平滑过渡、元素聚焦高亮等交互动画,并理解动画在渲染循环中的底层调度机制。
一、快速上手:两个官方示例
cy.animate()是核心(core)与元素(collection)共用的动画入口。针对核心实例,动画作用的对象是视口(viewport)——即整个画布的平移量与缩放级别;针对元素集合,动画作用的则是位置与样式(见第五节)。
示例 1:手动平移与缩放
cy.animate({ pan: { x: 100, y: 100 }, zoom: 2 }, { duration: 1000 });pan: { x, y }:目标平移量,单位是模型坐标(model co-ordinates)。动画期间视口会从当前 pan 平滑移动到{x: 100, y: 100}。zoom: 2:目标缩放级别。动画期间缩放值会从当前值平滑过渡到2。- 第二个参数
duration: 1000指定动画时长(毫秒)。
当同时给出pan与zoom时,二者在同一段动画内同步插值,形成"边平移边缩放"的组合过渡效果。如果动画目标是zoom或pan,在动画的每一帧中,视图都会触发pan、zoom、viewport事件,方便你监听视口变化(详见第四节)。
示例 2:适配(Fit)到指定元素
var j = cy.$('#j'); cy.animate({ fit: { eles: j, padding: 20 } }, { duration: 1000 });fit.eles:要适配到视口内的元素集合(可以是单个元素、集合或选择器)。fit.padding:元素边界与视口边缘之间保留的空白像素。- 执行后,视口会自动计算一个"恰好容纳这些元素 + 边距"的平移量与缩放级别,并平滑动画到该状态,等效于带动画的
cy.fit(eles, padding)。
二、动画属性(properties)与参数(params)详解
cy.animate( properties, params )的两个参数在源码中会被合并处理:src/define/animation.mjs 中properties = util.extend( {}, properties, params ),因此以下属性既可以写在第一个对象里,也可以写在第二个对象里,第二个对象的同名键会覆盖第一个。
2.1 视口类属性(核心实例专用)
| 属性 | 类型 | 说明 |
|---|---|---|
pan | { x, y } | 目标平移量(模型坐标)。若同时指定了panBy,panBy会优先被换算为pan(当前 pan + 偏移量) |
panBy | { x, y } | 相对当前平移位置的偏移量,源码会将其转换为绝对pan(见 src/define/animation.mjs) |
zoom | number \| object | 目标缩放级别。若为普通数值,动画期间会受minZoom/maxZoom约束(见 src/core/animation/step.mjs);若为对象(含level与position),会先通过getZoomedViewport()换算为最终的 pan/zoom |
center(或centre) | { eles } | 将指定元素集合居中,源码通过getCenterPan( eles, zoom )换算为pan(见 src/define/animation.mjs) |
fit | { eles, padding } | 将视口适配到元素集合,通过getFitViewport( eles, padding )同时换算pan与zoom(见 src/define/animation.mjs) |
从源码可以看到这些属性具有优先级覆盖关系:panBy覆盖pan→center覆盖pan→fit同时覆盖pan与zoom→ 对象形式的zoom覆盖zoom(与可能的pan)。写多个互斥属性时,后者生效。
2.2 通用动画参数
| 参数 | 类型/取值 | 默认值 | 说明 |
|---|---|---|---|
duration | number \| 'slow' \| 'fast' | 400 | 动画时长(毫秒)。源码中'slow'被解析为600,'fast'被解析为200(见 src/define/animation.mjs) |
delay | number | 0 | 动画开始前的延迟毫秒数。存在delay时,动画在延迟期间不更新任何属性(见 src/core/animation/step.mjs) |
queue | boolean | true | 是否将动画加入队列(在已有动画时排队执行)而非立即打断当前动画 |
easing | string \| array | 'linear' | 缓动函数名称,或形如['cubic-bezier', t1, p1, t2, p2]、['spring', tension, friction]的数组(见下文) |
complete | function | — | 动画完成时(progress = 1)触发的回调 |
step | function | — | 动画每一帧更新后触发的回调,形如step( now ) |
frame | function | — | 每一帧动画被应用前触发的回调 |
完整的缓动函数清单定义在 src/core/animation/easings.mjs:包括linear、ease、ease-in、ease-out、ease-in-out,以及 sine/quad/cubic/quart/quint/expo/circ 系列的ease-in-*、ease-out-*、ease-in-out-*变体,还有带参的spring与cubic-bezier。缓动函数在解析时复用了样式系统对transition-timing-function的解析逻辑(见 src/core/animation/step.mjs)。
带参缓动示例:
// 弹簧效果:tension=250, friction=30(默认时长参与弹簧生成) cy.animate({ pan: { x: 200, y: 0 } }, { duration: 1000, easing: ['spring', 250, 30] }); // 自定义三次贝塞尔曲线 cy.animate({ zoom: 1.5 }, { duration: 800, easing: ['cubic-bezier', 0.1, 0.7, 0.1, 1] });三、动画队列与生命周期控制
3.1 队列行为
cy.animate()默认将动画排队执行:如果当前已有动画在播放,新动画会进入队列,待当前动画完成后再播放。这一行为由 src/define/animation.mjs 中的判断ele.animated() && (properties.queue === undefined || properties.queue)决定——即只有当前正处于动画中时才排队;若当前空闲,则立即播放。若希望新动画立刻打断当前动画,可显式传入queue: false。
3.2 手动控制
核心实例同样拥有与元素集合一致的动画控制方法(定义于 src/core/animation/index.mjs):
cy.stop( clearQueue, jumpToEnd ):停止当前动画。clearQueue为真时同时清空队列中尚未播放的动画;jumpToEnd为真时动画直接跳到终点(源码将其duration置 0,下一帧即完成),否则立即从当前位置终止。停止后源码会主动调用cy.notify('draw')触发重绘(见 src/define/animation.mjs)。cy.clearQueue():仅清空队列,不影响正在播放的动画。cy.animated():返回当前是否存在正在播放的动画。cy.delay( time, complete ):创建一个仅起"等待"作用的占位动画,常用于编排动画序列的节奏,其内部实现为一次duration与delay均为time的animate调用(见 src/define/animation.mjs)。
此外,通过cy.animation( properties, params )可以创建但不立即播放的Animation对象,之后手动调用其play()、pause()、progress()、reverse()、promise()等方法精细控制。这些方法各自的详细说明见 documentation/md/animation/play.md、documentation/md/animation/pause.md、documentation/md/animation/reverse.md、documentation/md/animation/progress.md 与 documentation/md/animation/promise.md。
组合示例——缩放聚焦并等待完成:
var ani = cy.animation({ zoom: 2, center: { eles: cy.$('#j') } }, { duration: 1200, easing: 'ease-in-out' }); ani.play().promise('complete').then(function(){ console.log('缩放聚焦动画已完成'); });四、视口动画期间的监听事件
当动画正在更新pan或zoom时,每一帧都会触发相应事件(见 src/core/animation/step.mjs):
- 平移更新时触发
pan; - 缩放更新时触发
zoom(缩放值同时被钳制在minZoom与maxZoom之间); - 二者任一发生即触发
viewport。
因此你可以在动画进行中实时响应视口变化,例如同步更新画布外的比例尺或坐标指示器:
cy.on('viewport', function(){ document.querySelector('#status').textContent = 'pan: ' + cy.pan().x.toFixed(1) + ', ' + cy.pan().y.toFixed(1) + ' | zoom: ' + cy.zoom().toFixed(3); }); cy.animate({ pan: { x: 300, y: 150 }, zoom: 1.8 }, { duration: 2000 });五、延伸:元素集合的 animate(与 core 的差异)
虽然本指南聚焦核心实例,但同一 API 也作用于元素集合(src/collection/animation.mjs 同样导出了animate/animation/animated/clearQueue/delay/stop)。两者在 src/define/animation.mjs 中共享同一实现,差异在于属性解释:
- 元素动画支持
position(目标位置)、renderedPosition(以渲染坐标指定位置,源码会借助当前 zoom/pan 反算为模型坐标,见 src/define/animation.mjs)、style/css(目标样式,见 documentation/md/collection/animate.md); - 元素处于
locked()状态时位置动画不会生效(见 src/core/animation/step.mjs); - 样式动画在每帧通过
style.overrideBypass()以 bypass 方式写入,动画结束后样式值会保留在目标值上(见 src/core/animation/step.mjs)。
典型用法——节点移动并改变样式:
cy.$('#a').animate({ position: { x: 200, y: 200 }, style: { 'background-color': '#ff0000' } }, { duration: 800, easing: 'ease-out' });六、底层原理:动画循环与逐帧调度
理解cy.animate()的底层调度,有助于写出更流畅的交互动画:
- 注册到动画池:调用
animate()时,每个目标都会创建Animation实例(src/animation.mjs),记录起始状态(startPan/startZoom/startPosition)与目标状态,然后通过hook()加入目标的当前动画列表或队列,并注册到核心的动画池cy.addToAnimationPool()(见 src/core/animation/index.mjs)。无样式(headless)环境下动画池会被跳过以节省开销。 - 动画循环:
startAnimationLoop()决定调度方式——若渲染器存在且实现了beforeRender,则把逐帧步骤挂到渲染器的beforeRender钩子上,与绘制同步执行;否则(如 headless 环境)退化为基于requestAnimationFrame的自循环(见 src/core/animation/index.mjs)。 - 逐帧推进:每一帧,
stepAll()遍历动画池:当前列表为空时从队列取出下一个动画(src/core/animation/step-all.mjs);首次运行通过startAnimation( ele, ani, now, isCore )计算startTime(src/core/animation/start.mjs);随后step()依据(now - startTime) / duration计算进度百分比,用缓动函数对 pan/zoom 插值并写入视口(src/core/animation/step.mjs);动画结束后从当前列表移除并触发complete回调,同时cy.notify('draw')通知渲染器重绘(src/core/animation/step-all.mjs)。
由此可以推导出几个实用结论:
- 动画进度由"时钟时间差"驱动,因此浏览器标签页最小化时动画不会累积帧数,恢复后依然与真实时间同步;
duration: 0时进度直接置 1,动画瞬间完成,可用于无动画的即时跳转;- 多个元素/核心同时动画时共享同一个循环,性能开销集中在一轮
stepAll内。
七、相关资源
- 本文依据的核心文档:documentation/md/core/animate.md
- 核心动画实现:src/core/animation/index.mjs、src/core/animation/step-all.mjs、src/core/animation/step.mjs、src/core/animation/easings.mjs
- 动画与元素动画的公共定义:src/define/animation.mjs、src/animation.mjs、src/collection/animation.mjs
- 元素动画文档:documentation/md/collection/animate.md,动画对象各方法文档见 documentation/md/animation/ 目录下的
play.md、pause.md、stop.md、reverse.md、progress.md、apply.md、promise.md - 官方演示(动画交互的完整示例):documentation/demos/animated-bfs/(BFS 遍历动画)、documentation/demos/performance-tuning/(大规模图渲染与动画调优)
- 数据可视化
【免费下载链接】cytoscape.js
Graph theory (network) library for visualisation and analysis
相关推荐
Cytoscape.js 动画延迟指南:用 `delay()` 编排元素与核心视图的动画时序
Cytoscape.js 动画延迟指南:用 delay 编排元素与核心视图的动画时序 Cytoscape.js 是用于图可视化与分析的 Graph theory
数据可视化Cytoscape.js 动画系统完全指南:元素动画与视口动画的 API、控制方法与底层渲染原理
Cytoscape.js 动画系统完全指南:元素动画与视口动画的 API、控制方法与底层渲染原理 动画(Animation)是 Cytoscape.js 中让图
数据可视化ATLauncher终极指南:如何快速安装和管理Minecraft模组包
ATLauncher终极指南:如何快速安装和管理Minecraft模组包 想要体验Minecraft的无限可能,但被复杂的模组安装过程困扰?ATLauncher
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考