- 数据可视化
【免费下载链接】cytoscape.js
Graph theory (network) library for visualisation and analysis
导读
delay()是 cytoscape.js 为动画系统提供的时间编排工具,允许你在动画链式调用的任意位置插入一段“静默等待期”,从而将视图变换(如fit、pan、zoom)与元素样式动画按时间顺序精确衔接。本文基于 documentation/md/core/delay.md 展开,结合仓库中src/define/animation.mjs与src/animation.mjs的底层实现,完整讲解 core 与 collection 两套delay()API 的用法、底层原理与实战场景。读完你将能够用几行链式代码实现“先聚焦 A 区域 → 停顿 → 再聚焦 B 区域”这类典型的多阶段视图编排。
一、delay() 是什么:不是定时器,而是队列中的“空动画”
cytoscape.js 的动画系统建立在动画队列(animation queue)之上:对同一个目标(core 实例或元素集合)连续调用animate()时,动画会按调用顺序排队执行。delay()的作用就是在这条队列里插入一个不产生任何视觉变化的占位动画,其运行时间恰好等于你指定的毫秒数。
核心delay()的源码实现在 src/define/animation.mjs:
delay: function(){ return function delayImpl( time, complete ){ let cy = this._private.cy || this; if( !cy.styleEnabled() ){ return this; } return this.animate( { delay: time, duration: time, complete: complete } ); }; }, // delay从实现可以确认三个关键事实:
delay()本质是对animate()的封装:它调用this.animate({ delay: time, duration: time, complete: complete }),所以它天然支持链式调用,并且返回的仍然是 core/collection 实例。delay与duration都被设置为同一个值:duration决定了该占位动画占据队列的时间长度,而delay用于让动画循环在到达该动画后的前time毫秒内不推进任何属性(详见下文原理小节)。complete回调被透传:延迟结束时若提供了complete回调,它会被装入该动画的 completes 数组并在动画完成时触发(见 src/animation.mjs 中_p.completes.push( _p.complete )的处理)。
在 src/core/animation/index.mjs 中,core 实例挂载了整套动画 API,其中就包括delay与delayAnimation:
animate: define.animate(), animation: define.animation(), animated: define.animated(), clearQueue: define.clearQueue(), delay: define.delay(), delayAnimation: define.delayAnimation(), stop: define.stop(),元素集合同样拥有delay()(见 src/collection/animation.mjs),因此“对节点做交错动画”这类场景也能直接使用。
注意:
delay()的源码首先检查cy.styleEnabled(),在样式未启用(如纯 headless 数据场景)时会直接return this,即延迟不生效、链式调用继续。
二、核心示例:用 delay() 串联两次 fit 聚焦
原文档 documentation/md/core/delay.md 给出的示例是最经典的“多阶段视图聚焦”场景——先让视图适配到元素#j,等待 1 秒,再适配到元素#e:
cy .animate({ fit: { eles: '#j' } }) .delay(1000) .animate({ fit: { eles: '#e' } }) ;执行过程拆解如下:
| 阶段 | 代码 | 行为 |
|---|---|---|
| 1 | .animate({ fit: { eles: '#j' } }) | 视图平移到并缩放到能完整显示#j的范围(默认持续 400ms,见下文时长说明) |
| 2 | .delay(1000) | 队列在此暂停 1000ms,画面保持不动 |
| 3 | .animate({ fit: { eles: '#e' } }) | 视图再次适配到#e |
这段代码体现了delay()的两大实战价值:
- 免去手写
setTimeout与状态管理:无需手动记录动画是否结束、是否已到 1 秒,动画队列天然保证顺序。 - 与
fit等 core 视图操作无缝集成:fit在 src/define/animation.mjs 中被解析为pan与zoom的动画目标,与delay同属一个动画队列,时序完全可控。
关于默认时长的补充
在上面的示例中,fit动画未指定duration。从 src/define/animation.mjs 可以看到默认值与速记值:
- 未指定时默认
duration = 400(毫秒); - 传入字符串
'slow'解析为600; - 传入字符串
'fast'解析为200。
所以示例实际的时间线约为:0–400ms 聚焦#j→ 400–1400ms 停顿 → 1400ms 起聚焦#e。
三、delay() 的底层原理:动画循环中的 delay 判断
为什么delay()能让画面“停顿”而不产生跳动?答案在动画推进函数 src/core/animation/step.mjs 中:
if( ani_p.delay == null ){ // then update let startPos = ani_p.startPosition; let endPos = ani_p.position; // ... 位置、pan、zoom、样式属性的插值更新 }也就是说:动画在推进时先检查自身的delay字段,只有delay == null才会执行属性插值更新;而delay()生成的动画带有非空的delay: time,因此在动画循环触达它的前time毫秒内,该动画虽然占用队列、推进进度,却不修改任何视觉属性——视觉上便形成了精确的停顿。
从 src/core/animation/index.mjs 可以看到,动画循环由startAnimationLoop()驱动,通过stepAll( now, cy )在每个帧推进所有活跃动画;headless(无渲染器)环境下同样依赖requestAnimationFrame循环。动画对象本身在 src/animation.mjs 中初始化,duration默认 1000ms,delay、complete等参数均通过opts注入_private,这正是step读取ani_p.delay的数据来源。
四、进阶用法一:元素集合上的 delay() 实现交错动画
delay()不仅适用于 core 视图操作,也适用于元素集合。原文档姊妹篇 documentation/md/collection/delay.md 展示了元素颜色动画的两阶段切换:
cy.nodes() .animate({ style: { 'background-color': 'blue' } }, { duration: 1000 }) .delay( 1000 ) .animate({ style: { 'background-color': 'yellow' } }) ;console.log('Animating nodes...');会在链式调用构建队列时立即执行,但视觉上的颜色变化严格遵循“变蓝 1 秒 → 停顿 1 秒 → 变黄”的顺序。
更进阶的用法是按索引递增延迟实现瀑布式交错动画,这在 documentation/md/collection/sort.md 的排序可视化示例中出现过:
var duration = 1000; nodes.removeStyle().forEach(function( node, i ){ node.delay( i * duration ).animate({ style: { 'border-width': 4, 'border-color': 'green' } }, { duration: duration }); });这里对每个节点单独调用node.delay(i * duration),第i个节点会额外等待i * 1000ms,于是节点边框动画依次错峰开始,形成清晰的扫描效果。其原理是对每个元素独立维护动画队列:delay()在 src/collection/animation.mjs 中同样委托给define.delay(),this._private.cy || this保证了在元素上调用时能正确回溯到所属的 cy 实例。
五、进阶用法二:complete 回调与 delayAnimation()
delay()的第二个参数complete会在延迟结束时执行。例如需要在停顿后触发某个外部动作:
cy .animate({ fit: { eles: '#j' } }) .delay(1000, function(){ console.log('停顿结束,准备聚焦 #e'); }) .animate({ fit: { eles: '#e' } });此外,仓库还提供了非自动播放的延迟动画——delayAnimation(time, complete),它与delay()的实现一一对应(见 src/define/animation.mjs):
delayAnimation: function(){ return function delayAnimationImpl( time, complete ){ let cy = this._private.cy || this; if( !cy.styleEnabled() ){ return this; } return this.animation( { delay: time, duration: time, complete: complete } ); }; }, // delay区别在于:
| API | 底层调用 | 行为 |
|---|---|---|
delay(time, complete) | this.animate({...}) | 自动加入队列并立即开始播放 |
delayAnimation(time, complete) | this.animation({...}) | 只创建动画对象,不自动播放,需手动.play() |
delayAnimation返回的动画对象具备 src/animation.mjs 中定义的全套控制方法——play()、pause()、stop()、progress()、promise()等。一个典型场景是配合样式过渡使用:在 src/style/apply.mjs 中,元素样式过渡(transition-delay)正是通过ele.delayAnimation( delay ).play().promise().then( resolve )来等待延迟结束再应用过渡样式:
if( delay > 0 ){ ele.delayAnimation( delay ).play().promise().then( resolve ); } else { resolve(); }这说明delayAnimation在设计上就被引擎自身用于衔接“等待-执行”的异步流程,开发者完全可以在自己的代码中复用它来编写可暂停、可查询进度的延迟逻辑。
六、注意事项与最佳实践
- 只阻塞队列,不阻塞 JS 主线程:
delay()不是sleep(),动画循环期间 JavaScript 事件照常处理,console.log等代码会立即执行。 - 配合
clearQueue()与stop()使用:如果需要在用户交互时打断延迟,可调用cy.stop(true, false)或cy.clearQueue()(实现见 src/define/animation.mjs),避免延迟动画继续占据队列。 - headless 环境行为一致:只要启用样式(
styleEnabled()),动画循环在无渲染器环境同样运行(见 src/core/animation/index.mjs 的headlessStep()),但此时需要显式调用cy.destroy()停止循环。 - 时长单位:
time一律以毫秒为单位;0表示零延迟,等价于直接衔接下一动画。 - 集合与 core 的队列相互独立:
cy.delay()控制的是视图级动画(fit/pan/zoom)时序,eles.delay()控制的是元素级动画时序,两者互不阻塞。
总结
delay()是 cytoscape.js 动画链式编排中最轻量也最常用的时间控制手段:它通过“在队列中插入一段不更新属性的动画”实现了精确停顿,其实现位于 src/define/animation.mjs,延迟期间的静默行为由 src/core/animation/step.mjs 的ani_p.delay == null判断保证。无论是对 core 实例编排多阶段视图聚焦(fit+delay),还是对元素集合实现瀑布式交错动画(node.delay(i * duration)),亦或是借助delayAnimation().promise()编写可中断的异步时序,掌握这一个 API 就能显著提升图谱交互的质感与可控性。
- 数据可视化
【免费下载链接】cytoscape.js
Graph theory (network) library for visualisation and analysis
相关推荐
Cytoscape.js 动画延迟指南:用 `delay()` 编排元素与核心视图的动画时序
Cytoscape.js 动画延迟指南:用 delay 编排元素与核心视图的动画时序 Cytoscape.js 是用于图可视化与分析的 Graph theory
数据可视化5分钟跑通OrcaSlicer命令行批量切片:G-code生成实战手册
5分钟跑通OrcaSlicer命令行批量切片:G code生成实战手册 逐一点开 200 个 STL、调参数、点切片、等 G code,这套鼠标活一个人干要大半
桌面应用3D渲染图形学Font Awesome图标动画延迟策略:创建有序的动画序列
Font Awesome图标动画延迟策略:创建有序的动画序列 你是否遇到过网页中所有图标同时动画造成的视觉混乱?是否想让图标按照特定顺序依次动起来,引导用户注意
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考