Cytoscape.js 核心视图动画完全指南:从 `cy.animate()` 平移缩放到动画队列与缓动原理
2026/9/23 15:39:50 网站建设 项目流程
  • 数据可视化

【免费下载链接】cytoscape.js

Graph theory (network) library for visualisation and analysis

项目地址:https://gitcode.com/gh_mirrors/cy/cytoscape.js
点击查看免费下载

本指南以 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指定动画时长(毫秒)。

当同时给出panzoom时,二者在同一段动画内同步插值,形成"边平移边缩放"的组合过渡效果。如果动画目标是zoompan,在动画的每一帧中,视图都会触发panzoomviewport事件,方便你监听视口变化(详见第四节)。

示例 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 }目标平移量(模型坐标)。若同时指定了panBypanBy会优先被换算为pan(当前 pan + 偏移量)
panBy{ x, y }相对当前平移位置的偏移量,源码会将其转换为绝对pan(见 src/define/animation.mjs)
zoomnumber \| object目标缩放级别。若为普通数值,动画期间会受minZoom/maxZoom约束(见 src/core/animation/step.mjs);若为对象(含levelposition),会先通过getZoomedViewport()换算为最终的 pan/zoom
center(或centre{ eles }将指定元素集合居中,源码通过getCenterPan( eles, zoom )换算为pan(见 src/define/animation.mjs)
fit{ eles, padding }将视口适配到元素集合,通过getFitViewport( eles, padding )同时换算panzoom(见 src/define/animation.mjs)

从源码可以看到这些属性具有优先级覆盖关系panBy覆盖pancenter覆盖panfit同时覆盖panzoom→ 对象形式的zoom覆盖zoom(与可能的pan)。写多个互斥属性时,后者生效。

2.2 通用动画参数

参数类型/取值默认值说明
durationnumber \| 'slow' \| 'fast'400动画时长(毫秒)。源码中'slow'被解析为600'fast'被解析为200(见 src/define/animation.mjs)
delaynumber0动画开始前的延迟毫秒数。存在delay时,动画在延迟期间不更新任何属性(见 src/core/animation/step.mjs)
queuebooleantrue是否将动画加入队列(在已有动画时排队执行)而非立即打断当前动画
easingstring \| array'linear'缓动函数名称,或形如['cubic-bezier', t1, p1, t2, p2]['spring', tension, friction]的数组(见下文)
completefunction动画完成时(progress = 1)触发的回调
stepfunction动画每一帧更新后触发的回调,形如step( now )
framefunction每一帧动画被应用前触发的回调

完整的缓动函数清单定义在 src/core/animation/easings.mjs:包括lineareaseease-inease-outease-in-out,以及 sine/quad/cubic/quart/quint/expo/circ 系列的ease-in-*ease-out-*ease-in-out-*变体,还有带参的springcubic-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 ):创建一个仅起"等待"作用的占位动画,常用于编排动画序列的节奏,其内部实现为一次durationdelay均为timeanimate调用(见 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('缩放聚焦动画已完成'); });

四、视口动画期间的监听事件

当动画正在更新panzoom时,每一帧都会触发相应事件(见 src/core/animation/step.mjs):

  • 平移更新时触发pan
  • 缩放更新时触发zoom(缩放值同时被钳制在minZoommaxZoom之间);
  • 二者任一发生即触发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()的底层调度,有助于写出更流畅的交互动画:

  1. 注册到动画池:调用animate()时,每个目标都会创建Animation实例(src/animation.mjs),记录起始状态(startPan/startZoom/startPosition)与目标状态,然后通过hook()加入目标的当前动画列表或队列,并注册到核心的动画池cy.addToAnimationPool()(见 src/core/animation/index.mjs)。无样式(headless)环境下动画池会被跳过以节省开销。
  2. 动画循环startAnimationLoop()决定调度方式——若渲染器存在且实现了beforeRender,则把逐帧步骤挂到渲染器的beforeRender钩子上,与绘制同步执行;否则(如 headless 环境)退化为基于requestAnimationFrame的自循环(见 src/core/animation/index.mjs)。
  3. 逐帧推进:每一帧,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.mdpause.mdstop.mdreverse.mdprogress.mdapply.mdpromise.md
  • 官方演示(动画交互的完整示例):documentation/demos/animated-bfs/(BFS 遍历动画)、documentation/demos/performance-tuning/(大规模图渲染与动画调优)
  • 数据可视化

【免费下载链接】cytoscape.js

Graph theory (network) library for visualisation and analysis

项目地址:https://gitcode.com/gh_mirrors/cy/cytoscape.js
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询