ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

motion 仓库 issue-1831 排查实录:drag 释放触发多 variant onAnimationComplete 的复现方案与源码剖析

motion 仓库 issue-1831 排查实录:drag 释放触发多 variant onAnimationComplete 的复现方案与源码剖析 前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载本文是 motion 项目对历史 issue-18312022 年 framer-motion v7 时代的拖拽行为报告的完整排查计划书。它以拖拽释放后onAnimationComplete是否为所有 variant 依次触发为核心给出了一条先最小复现、后定性归因、再决定是否修复的标准 triage 流程。读完本文你将掌握如何为拖拽 多 variant 场景搭建无外部依赖的最小复现 fixture、motion 内部 variant 层animation types的优先级与重新评估机制、以及no repro → no fix的仓库级处置规范。一、Issue 概览三个症状捆绑的 Chakra UI 沙箱报告issue-1831 的报告来自一个基于 Chakra UI 的幻灯片slideshow沙箱构建在 framer-motion ~7 之上。报告捆绑了三个症状拖拽释放触发全部 variant 的onAnimationComplete释放一次拖拽onAnimationComplete在极短时间内为全部三个 variant 依次触发而不是只为最终生效的那一个触发第一次拖拽不移动盒子某个盒子首次被拖拽时纹丝不动首次拖拽的闪烁会杀死重新居中动画第一次拖拽产生的视觉闪烁导致后续的 re-centering 动画失效。该 issue 在仓库 triage 体系中的定性如下详见 plans/issues/issue-1831.md分类NEEDS-REPRO需要复现未确认即不动代码优先级P32022 年 framer-motion v7 时代问题复现依赖 Chakra UI仅 1 条评论工作量S小风险LOW依赖无类别bugtriage计划创建时间commit42bfbe3ed2026-06-11仓库对该类问题有一条硬性政策no repro → no fix, no speculative coverage tests无复现则无修复、不写猜测性覆盖测试。这条政策贯穿了整个计划的 Step 设计与 STOP 条件设定。二、Why this matters症状成因的初步判断计划对三个症状给出了明确的成因假设这决定了排查的着力点症状 1核心症状计划判断它可能是当前 by-design按设计如此的行为。原因链是结束一次拖拽时拖拽控制层会调用animationState.setActive(whileDrag, false)这会触发所有 variant 层的重新评估每个动画类型层都会被重新跑一遍animateChanges对于那些值已经位于目标处的层产生的动画会立即完成而每个完成的动画都会以自己的 variant 名称触发一次onAnimationComplete。也就是说onAnimationComplete的多次触发可能不是 bug而是多层变体被同时重新求值这一设计机制的副产物。判断它究竟属于 by-design 还是真正的 double-fire正是本计划的 Step 2 要回答的问题。症状 2–3计划认为这两个症状与首次拖拽first-drag缺陷吻合而这些缺陷在此后已被单独修复。修复证据包括VisualElementDragControls.ts中针对带初始坐标的元素首次拖拽会吸附到错误位置的修复注释以及9f228395e提交中的 momentum动量修复。为什么现在才处理四个大版本之后原始沙箱已无法访问Cloudflare 对 agent 屏蔽且与 Chakra UI 深度耦合。在动任何代码之前必须先用一个全新的最小复现来验证症状是否在当前版本上依然存在。三、Current state源码现状与最小复现形状计划先圈定了两处关键源码作为理论依据的锚点VisualElementDragControls.tscancel()方法在每次释放拖拽时执行animationState.setActive(whileDrag, false)随后 variant 重新评估会对所有处于激活态的层执行动画variant 解析与动画调度packages/motion-dom/src/render/utils/animation-state.ts注issue-1831 计划书写于 framer-motion 时代该文件当时位于packages/framer-motion/src/render/utils/在当前仓库中已随 motion-dom 包划分迁移至packages/motion-dom/src/render/utils/职能不变。计划强调在形成哪些层会触发回调的任何理论之前必须先通读animation-state.ts。这与仓库以源码实证代替猜测的原则一致。最小复现形状issue 正文已完整指定无需沙箱即可重建一个motion.div开启drag三个 variantsbase/exiting/center各自有互不相同的x、scale、opacity目标后面的 variant 覆盖前面的animate{[...]}形式的多 variant 数组如[base, center]onAnimationComplete{(def) log(def)}把触发名称记录到日志。这个形状刻意剥离了 Chakra UI 与幻灯片业务逻辑只保留触发争议行为的最小要素。四、底层原理variant 层的优先级与重新评估机制要理解释放拖拽为何可能触发多个 variant 的完成回调需要看清 motion 的 variant 层机制。相关核心实现在 packages/motion-dom/src/render/utils/variant-props.ts 与 packages/motion-dom/src/render/utils/animation-state.ts。1. 动画类型的优先级顺序variantPriorityOrder定义了七个动画类型层animate → whileInView → whileFocus → whileHover → whileTap → whileDrag → exitexit优先级最高animate最低。在animation-state.ts中animateChanges()以逆序reversePriorityOrder遍历所有层即从exit开始逐层向下处理。2. 值保护protectedKeys机制遍历每一层时前面已经处理过被更高优先级层接管的所有 key 会被记录为该层的protectedKeys确保低优先级层不会重新动画这些值。这就是同一值只有一个赢家的设计来源——理论上只有真正胜出的层才应该动画并触发完成回调。3.setActive的级联效应setActive(type, isActive)除了更新当前元素的state[type].isActive还会把激活状态递归传播给所有 variant 子节点visualElement.variantChildren.forEach(...)随后调用animateChanges(type)。关键点在animateChanges内部activeDelta type changedActiveType ? typeState.isActive : null当某层刚刚被置为非激活activeDelta false时其解析值会被清空resolvedValues {}进而触发值被移除的判定路径而其他层的值若与上一次解析相同next prev会被加入该层的protectedKeys而跳过动画——但若某层因为属性本身如animate{[base, center]}数组发生变化而需要重新动画且其目标值与当前值一致动画就会立即完成并触发onAnimationComplete。这正是计划判断症状 1 可能是 by-design的机制依据释放拖拽会改变whileDrag层的激活态从而驱动一次全层重新求值只要某个变体层的目标值已经就位它就会产生一条零时长动画并立刻回调完成。从源码结构看这是层求值机制的天然行为而非明显的双重触发 bug。4. 拖拽生命周期中的两次setActive在 VisualElementDragControls.ts 中onStart阶段调用animationState.setActive(whileDrag, true)激活拖拽变体而在cancel()L302中调用setActive(whileDrag, false)关闭它。两次调用都会触达上述animateChanges全层求值流程——排查时只需盯住释放这一次即可。五、Step 1构建最小 fixture剥离 Chakra UI计划的 Step 1 是在 dev/react 演示工程下新增一个一次性 fixturedev/react/src/tests/drag-variants-complete.tsx除非确认了真实 bug否则该 fixture不提交到仓库。fixture 的关键配置variants定义base、exiting、center三个变体分别给出互不相同的x/scale/opacity目标值animate{[base, center]}多变体数组后面的center覆盖前面的basewhileDrag{{ scale: 1.05 }}激活拖拽态onAnimationComplete把收到的 variant 定义def追加进window.__completed数组供后续人工检查。计划为该步骤给出的时间盒是约 45 分钟并强调一个 STOP 边界如果两次尝试后该场景仍无法用纯 props 表达必须引入 AnimatePresence 风格的 exit 标志就停下来汇报缺口而不是把 fixture 一步步扩建成完整的 Chakra 沙箱——这既能控制工作量也守住最小复现的本意。六、Step 2探测 headline 症状构建完成后进入验证环节。先跑通构建与开发服务器命令如下摘自计划文档的命令表用途命令预期结果构建yarn build仓库根目录exit 0启动服务器React 18PORT$((10000 RANDOM % 50000)); cd dev/react TEST_PORT$PORT yarn vite --port $PORT 之后执行npx wait-on http://localhost:$PORT服务器就绪人工探针打开http://localhost:$PORT/?testfixture观察控制台输出可观测__completed随后手动或快速跑一次前台 Cypress拖拽盒子数像素后释放检查window.__completed的内容。计划要求至少记录三种释放场景并留下书面观察无闪烁释放no-flick有闪烁释放flick动画进行中释放release-during-animation。判定分支若只有预期中的 variant 完成回调→ 症状 1 在 main 上不可复现进入 Step 3若释放瞬间所有 variant 名称全部触发→ 复现成功此时只需通读一遍animation-state.ts即可判断这究竟是 by-design 的层重新求值很可能还是真正的 double-fire。无论哪种结论都必须 STOP 并汇报by-design → 提出以文档方式关闭的建议真 bug → 单独建立修复计划。同时顺带记录但不深入第一次拖拽是否移动了盒子——计划判断症状 2–3 已被此后的 first-drag 与 momentum 修复覆盖9f228395e提交与VisualElementDragControls.ts中针对带初始坐标元素首次拖拽吸附错误位置的修复注释计划文档引用其位于 571–577 行都是佐证。七、Step 3门控关闭为 needs-repro / outdatedStep 3 带有一个门控条件只有当本计划在 plans/issues/README.md 中的行被标记为 APPROVED 时才允许在 GitHub 侧执行评论与关闭操作。评论通过 GitHub CLI 在 issue 下追加评论说明原始沙箱基于 framer-motion 7、现已不可访问在当前版本上做的最小重构无法复现全部 variant 依次完成的行为评论中内联附上 fixture 代码方便报告者自行扩展首次拖拽类问题已由9f228395e及 snap-to-cursor 原点修复单独解决如果问题仍存在请基于最新版本重新提供复现。关闭以stateclosed且state_reasonnot_planned的方式关闭该 issue。若门控未通过则不评论不关闭仅将 README 行更新为实际到达的分支状态NO-REPRO — awaiting close approval未复现等待关闭审批或 REPRO — needs follow-up plan已复现需要后续计划。八、Done criteria 与 STOP conditions计划的完成标准逐项勾选fixture 已构建三种释放场景已观察并记录无任何源码改动除非确认 bug否则 fixture 不提交以git status验证仅当 README 行 APPROVED 时才评论并关闭 issue否则将行更新为实际到达的分支状态。计划预设的两个 STOP 条件Step 2 复现了全部 variant 依次完成—— 立即汇报严禁在本计划下直接修改animation-state.ts复现不等于获得修复授权修复需要独立的计划两次尝试后 fixture 仍无法表达该场景需要 AnimatePresence 式 exit 标志—— 汇报缺失项而不是把 fixture 扩充成完整的 Chakra 沙箱。这两条 STOP 条件共同体现了仓库的纪律triage 计划只负责确认事实不越权动代码复现手段保持最小不向历史依赖妥协。九、Maintenance notes真正的可操作后续是文档计划的维护备注给出了一个颇具价值的结论如果症状被复现且判定为 by-design真正可操作的后续不是改代码而是补文档——onAnimationComplete与多 variantanimate数组如animate{[base, center]}之间的契约目前未在文档中说明而本 issue 恰恰是这种混淆的实例证据。换言之onAnimationComplete的触发粒度与 variant 层的重新求值时机是绑定在一起的当开发者使用多 variant 数组时一次状态切换可能触发多个完成回调。把这个契约写清楚比在animation-state.ts里做防御性改动更能解决此类困惑也符合仓库no repro → no fix的克制路线。十、小结一份可复用的拖拽 variant 排查模板issue-1831 计划的价值不仅在于处理一个具体 issue更在于它演示了一条可复用的排查路径剥离依赖把 Chakra UI 沙箱抽象为一个motion.div 三 variants 多 variantanimate数组 完成回调日志的最小形状锚定源码在VisualElementDragControls.ts拖拽生命周期与animation-state.tsvariant 层求值中定位机制根源设计判定分支区分by-design 层重新评估与真实 double-fire两种结局并分别对应文档化关闭与独立修复计划两种处置守住边界STOP 条件防止计划越权改代码或扩张成沙箱重建工程。对于 motion 的使用者而言文中关于 variant 层优先级whileDrag高于animate、protectedKeys值保护机制、以及setActive级联传播的描述也可以直接迁移到日常排错中当你的多 variant 动画出现多余的回调或意外重跑时先检查是否存在某层激活态变化驱动了全层重新求值再决定是调整 props 还是调整预期。赞分享前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载相关推荐让 LazyMotion 与 Reorder 共存的警告变得可操作motion 仓库 issue-2094 修复方案与源码剖析让 LazyMotion 与 Reorder 共存的警告变得可操作motion 仓库 issue 2094 修复方案与源码剖析 本文围绕 motion 仓库中前端UI组件NixOS 部署 Steam 深度解析FHS 兼容环境、programs.steam 模块与 steam-run 实战NixOS 部署 Steam 深度解析FHS 兼容环境、programs.steam 模块与 steam run 实战 本文围绕 NixOS 手册中的 Ste前端UI组件Miles PPO实战用GAE价值函数提升样本效率的完整配方Miles PPO实战用GAE价值函数提升样本效率的完整配方 Miles 是面向 LLM/VLM 后训练的开源强化学习框架从 slime 分叉并共同演进。本前端UI组件上一篇AGIEval深度解析人类认知基准如何评估AI模型的真实能力下一篇Semi Design Typography 版式组件完全指南标题、文本、段落、数值格式化与智能省略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表