- 前端
- UI组件
【免费下载链接】motion
A modern animation library for React and JavaScript
本篇技术指南围绕 motion 仓库(README.md)中针对 issue-2608 的处理方案展开,核心主题是:animate(sequence)返回的播放控制对象(playback controls)自带可读写的time属性,通过赋值即可让序列跳转到任意时间点,并从该位置继续执行剩余动画。读完本文,你将掌握在按钮点击等交互场景中"跳到序列特定位置并续播"的标准写法、底层的时间分发与关键帧扁平化原理,以及当前版本在"按标签(label)跳转"上的已知限制。
一、问题背景:2024 年的一条悬而未决的提问
issue-2608 记录了一个 2024 年的用户问题(0 条评论):
"when click a button, I want to jump to a specific position in a sequence and execute the rest from there."
即:点击按钮时,希望跳到一段动画序列中的某个特定位置,并从该位置继续执行剩余部分。处理该问题的计划文档 plans/issues/issue-2608.md 将其分类为SUPPORT / COVERED BY EXISTING API——也就是说,这个问题并不需要新增 API,现有能力已经覆盖:自时间线(timeline/sequence)功能诞生起,animate(sequence)返回的控制对象就支持可设置的time。计划要求以"带实例代码的完整回答"来关闭该 issue。
二、核心答案:给controls.time赋值即可跳转续播
计划文档给出的推荐回答(recommended answer)非常简洁,可直接复制使用:
const controls = animate(mySequence) // 稍后,例如在按钮的 click handler 中: controls.time = 2.5 // 单位:秒 —— 跳转到该位置并继续播放 // controls.pause() / controls.play() / controls.speed 同样可用关键行为说明:
controls.time = 2.5表示把整个序列"seek"到 2.5 秒处,元素会立即采样到该时间点对应的属性值;- 赋值后序列会从该位置继续播放(而不是停在原地);
- 如果当前控制对象处于暂停或已结束(finished)状态,需要先调用
controls.play()再设置time,以恢复播放; - 配合
controls.pause()、controls.play()、controls.speed等既有方法,可以组合出"暂停 → 拖到某位置 → 续播"的完整交互体验。
三、源码级原理:time 是如何在整条序列上分发的
3.1 序列返回的是 GroupAnimation,time 被广播到所有子动画
animate(sequence)在 packages/framer-motion/src/animation/animate/index.ts 中识别到序列输入后,会调用animateSequence为序列中每个 subject/value 生成独立动画,再用GroupAnimationWithThen将所有动画包装成一个统一控制对象返回(new GroupAnimationWithThen(animations))。
这个分组控制对象的time读写实现在 packages/motion-dom/src/animation/GroupAnimation.ts:
get time() { return this.getAll("time") } set time(time: number) { this.setAll("time", time) }其中setAll会把新时间逐一写入每一个子动画(GroupAnimation.ts):
private setAll(propName: PropNames, newValue: any) { for (let i = 0; i < this.animations.length; i++) { ;(this.animations[i][propName] as any) = newValue } }GroupAnimation同时支持speed、startTime、state、duration、iterationDuration等属性的统一分发,以及play()、pause()、stop()、cancel()、complete()等方法的批量调用(见 GroupAnimation.ts),这保证了时间跳转与播放控制是一套自洽的完整语义。
3.2 为什么"按时间 seek"是精确的:关键帧绝对偏移扁平化
计划文档指出,序列在构建阶段会把每个 segment 拍平成每个 subject/value 一条带绝对偏移(absolute offsets)的关键帧动画,实现位于 packages/framer-motion/src/animation/sequence/create.ts。
createAnimationsFromSequence按顺序遍历序列数组,通过calcNextTime(currentTime, transition.at, prevTime, timeLabels)解析每个 segment 的at时间(支持绝对时间、"<"/">"相对偏移、标签引用),把 timeline 中每个关键帧落到全局绝对时间轴上。因此:
- 设置
controls.time = 2时,每个子动画都精确采样到第 2 秒应处的状态; - 采样之后,序列从该全局时间点自然"接续"播放剩余片段——这正是"jump to a specific position and execute the rest from there"的实现基础;
- 由于时间轴是全局扁平化的,跳转不存在"半截片段"的错位问题,跳转结果与从头播放到该时刻的状态完全一致。
四、测试验证:仓库中已有的可设置 time 用例
计划文档 Step 1 要求核对既有测试是否覆盖"对animate()结果设置.time"。经核查,packages/framer-motion/src/animation/animate/tests/animate.test.tsx 中已有多个直接证据:
- 读写语义(第 286-293 行):创建
animate(div, { x: 100 }, { duration: 10 })后,断言animation.time初始为 0,赋值为 5 后读回仍为 5; - 跳到终点(第 295-308 行):暂停后
animation.time = 1,采样值立即落到 duration 终点(opacity 变为 0.5); - 序列中的往返 scrub(第 516-541 行):对包含回调型 segment 的序列,分别
animation.time = 0.5/1.5/0.5/1.5前后拖拽,验证回调在越过阈值时触发、回退时触发 undo——这组用例直接证明了对整条序列(而非单个动画)设置 time 是精确且可反复 seek 的。
这些测试与计划文档"如果已有设置.time的测试则引用之"的验证要求吻合,也印证了该能力是被仓库持续保障的公共行为。
五、已知限制:按标签(label)跳转尚未暴露
计划文档明确要求"诚实地陈述"一条已知限制:
- 序列支持给时间点命名标签(label),例如字符串形式的 segment 或带
name的{ at, name }对象; - 这些标签到时间的映射存放在
createAnimationsFromSequence内部的timeLabels中(sequence/create.ts),是模块内部实现,并未向用户暴露; - 因此,如果用户希望"跳到名为
intro的标签处",今天必须自己把标签换算成时间(例如通过已知各段 duration 累加计算),再赋给controls.time。
计划文档对这类需求的建议是:若有真实诉求,可单独提交一个聚焦的功能请求(feature request),而不要承诺该能力一定会被实现。
六、完整实战示例:按钮点击跳转序列位置
综合以上,一个可在 React/JavaScript 中直接套用的完整方案如下:
const sequence = [ [box, { x: 0 }, { duration: 1 }], [box, { scale: 1.5 }, { duration: 1 }], [box, { rotate: 90 }, { duration: 1 }], [box, { y: 100 }, { duration: 1 }], ] const controls = animate(sequence) // 跳转到序列 2.5 秒处并继续播放剩余动画 function jumpToMiddle() { if (controls.state === "finished" || controls.state === "paused") { controls.play() } controls.time = 2.5 } // 配合其他控制方法实现"拖拽 scrub" function scrubTo(t) { controls.pause() controls.time = t }要点回顾:
controls.time单位始终为秒;- 跳转后默认继续播放,若对象处于暂停/结束态请先
play(); - 跳转精度由全局绝对时间轴保证,可在序列内任意往返 seek;
- 按标签跳转当前不公开,需要自行换算时间。
七、计划本身的执行流程(供参考)
issue-2608 的处理计划还展示了这类"答案 + 关闭"型 issue 的标准流程(plans/issues/issue-2608.md):先通过grep核对测试覆盖(Step 1),再由门禁(plans/issues/README.md中对应行 APPROVED)决定是否在 issue 上发布回答并调用 GitHub API 关闭(Step 2)。计划明确列出 STOP 条件:若验证发现set time无法正确对序列 seek(那将是真正的 bug,需另立计划),或门禁未通过,则不得"回答即关闭"。该计划风险评级为 LOW、工作量 S,且全程不修改任何源码文件。
- 前端
- UI组件
【免费下载链接】motion
A modern animation library for React and JavaScript
相关推荐
Motion 序列动画标签偏移:在 `at` 选项中实现 `"label+0.2"` 相对时间定位
Motion 序列动画标签偏移:在 at 选项中实现 "label+0.2" 相对时间定位 导读 Motion(framer motion)的序列(sequen
前端UI组件novatorem高级技巧:创建专属主题模板,让你的GitHub主页脱颖而出
novatorem高级技巧:创建专属主题模板,让你的GitHub主页脱颖而出 novatorem是一款能够将Spotify实时播放状态动态展示在GitHub个人
在 Factory Bot 动态属性中使用序列(Sequence)的完整指南
在 Factory Bot 动态属性中使用序列(Sequence)的完整指南 本文聚焦 Factory Bot 中「在动态属性(dynamic attribut
测试开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考