)
前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载本文以仓库内的 plans/issues/issue-2563.md 计划文档为主体深入复盘 Motion 动画库中animate(el, { opacity: 0, display: none })在useAnimate/独立animate()路径下动画结束后display停留在旧值的问题包括其被定位为与 issue-2656 同根因的回归、为何只有none关键字受影响、mixVisibility的修复实现以及如何通过回归测试与 GitHub CLI 完成验证和关闭的完整流程。读者读完可以掌握 Motion 对离散值display/visibility动画的底层处理机制、修复后的预期行为以及仓库中这套问题计划plan的落地执行方法。一、问题现象useAnimate中display: none无法被设置该问题于 2024-03-16 报告针对当时的 11.0.14 版本。复现代码非常简洁animate(el, { opacity: 0, display: none }, { duration: 0.7 })实际表现是opacity正常按 0.7s 淡出但display始终停留在动画开始前的旧值元素在视觉淡出后依然占据布局。而同一动画中如果使用其他display关键字如inline、contents、flex则一切正常。issue 下共有 8 条评论确认了同样的症状并验证了transitionEnd: { display: none }这个 workaround 可以绕开问题。其中一位评论者modulareverything2024-05-03通过二分定位将问题锁定到v10 → v11.1.x 的升级区间——这与 issue-2656 所指向的 11.0.11 回归版本完全吻合为后续合并处理提供了直接证据。二、根因分析为什么偏偏只有none失效2.1 同一回归的两个入口issue-2563useAnimate/ 独立animate()命令式 API 路径issue-2656详见 plans/issues/issue-2656.mdReact 组件的variants/animate属性路径即{ opacity: 1, display: block }与{ opacity: 0, display: none }之间切换时隐藏动画结束后display没有回到none。两个问题在 11.0.10 都正常在 11.0.11 同时被破坏。issue-2656 计划文档明确指出根因是11.0.11 的异步关键帧解析重写async-keyframe-resolutionCHANGELOG 中对应 Keyframes now resolved asynchronously该改动改变了离散字符串关键帧的混合mix方式导致none终点状态丢失。mattgperry 于 2024-05-13 确认了回归原话大意这个确实被无意间破坏了不过此前的行为也不理想——瞬间切到 none并在当天合入修复。2.2isNone()none被当作特殊关键帧为什么其他关键字不受影响关键在关键帧解析器对none的特殊对待。packages/motion-dom/src/animation/keyframes/utils/is-none.ts 中的isNone()把none以及数字0、字符串0、各种零值字符串判定为特殊值export function isNone(value: AnyResolvedKeyframe | null) { if (typeof value number) { return value 0 } else if (value ! null) { return value none || value 0 || isZeroValueString(value) } else { return true } }inline、contents等普通关键字不会命中该判断走的是常规的mixImmediate即时混合路径因此不受回归影响而none由于在解析器和 mixers 中都被当作特殊关键帧处理走进了被破坏的离散混合discrete-mix路径最终丢掉了终点值。这就是只有none坏了的完整解释。三、修复实现mixVisibility与二值可见性插值3.1 核心函数修复提交9dc6e6aa12024-05-13随v11.2.0于 2024-05-14 发布新增了mixVisibility位于 packages/motion-dom/src/utils/mix/visibility.tsexport const invisibleValues new Set([none, hidden]) export function mixVisibility(origin: string, target: string) { if (invisibleValues.has(origin)) { return (p: number) (p 0 ? origin : target) } else { return (p: number) (p 1 ? target : origin) } }其语义可以拆成两条规则从不可见 → 可见如none→blockp 0时保持origin其余进度立即输出target即显示方向瞬间应用可见值从可见 → 不可见如block→nonep 1时才输出target其余进度保持origin即隐藏方向在整个动画期间保持可见动画结束瞬间才切到none/hidden。这正是 CHANGELOG 中该版本记录的措辞Binary visibility interpolation i.edisplay: [block, none]now maintains the visible state throughout the animation见 CHANGELOG.md 对应 v11.2.0 条目。3.2 路由与调度mixVisibility并非孤立生效它被嵌入到混合管线中路由packages/motion-dom/src/utils/mix/complex.ts 的mixComplex检测到invisibleValuesnone、hidden且对端没有可插值数值时直接返回mixVisibility(origin, target)不再走数值混合。保留 JS 路径packages/motion-dom/src/animation/utils/can-animate.ts 中if (name display || name visibility) return true确保这两个属性始终进入 JS 动画路径代码注释明确说明这些传统上不可动画但我们支持它们。不启用 WAAPI 加速packages/motion-dom/src/animation/waapi/supports/waapi.ts 依据acceleratedValues判断display不在加速列表中因此display动画永远不会被 Web Animations API 加速执行全部由 JS 帧循环驱动。终点落盘packages/motion-dom/src/animation/JSAnimation.ts 的tick在动画完成时通过getFinalKeyframe(...)写入真实的最终关键帧即none保证动画结束后样式值精确落在目标值上。3.3 回归测试与演示用例仓库已包含完整的回归测试位于 packages/framer-motion/src/motion/tests/animate-prop.test.tsxanimate display none block immediately switches to block验证显示方向在动画开始后立即输出blockanimate display block none switches to none on animation end验证隐藏方向在动画结束时才输出none还有animate visibility hidden visible immediately switches to visible等visibility用例以及none/block切换触发onAnimationComplete的用例。修复提交同时新增了开发演示 fixturedev/react/src/examples/Animation-display-visibility.tsx可直接在 dev 环境观察display/visibility动画的完整时序。四、修复后的预期行为none在动画完成时生效而非开始时需要特别强调的是修复后display: none在动画完成时才被应用而不是动画一开始就生效——这是设计行为不是 bug。对报告者的 0.7s 场景即元素先淡出 0.7s期间保持可见0.7s 结束后display才切换为none。这样元素在淡出过程中始终可见可交互视觉上自然若要在动画开始时立即隐藏需要改用其他手段例如opacity配合pointer-events或延迟应用display。这也是原文档在 Verdict 末尾特意标注not at the start — that is the designed behaviour, not a bug的原因。因此transitionEnd: { display: none }这个 workaround 在 v11.2.0 之后不再需要官方在关闭评论中也明确建议若在 motion12 上仍可复现请附上最小复现reproduction新建 issue。五、useAnimate路径为何同样被覆盖issue-2563 走的是useAnimate命令式路径issue-2656 走的是 React 声明式路径两条路径最终汇入同一条渲染管线。从 packages/framer-motion/src/animation/animate/subject.ts 可以看到元素动画会为 DOM 元素创建视觉元素createDOMVisualElement见 subject.ts随后与组件动画一样经过AsyncMotionValueAnimation → JSAnimation → mix这条管线。由于display永远不会被 WAAPI 加速见 3.2 节两条入口在 JS 混合层完全汇合因此同一个mixVisibility修复天然覆盖useAnimate与独立animate()。六、计划文档的执行流程从验证到关闭issue-2563 计划文档本身还包含一套可复现的落地步骤这也展示了仓库中 plan 类文档的标准作业方式Step 1用 issue-2656 的回归测试做验证gatenpx jest --config packages/framer-motion/jest.config.json --testPathPatternanimate-prop -t display验证标准所有display相关测试全部通过≥3 个0 失败若有任何失败则说明回归仍然存在必须停止并改用 FIX 计划。Step 2审批门禁Approval gate打开 plans/issues/README.md找到 issue-2563 对应状态行若该计划未被标记为 APPROVED则将该行置为 BLOCKED 并停止。Step 3评论并关闭 issuegh api repos/motiondivision/motion/issues/2563/comments -f bodyThis was the same regression as #2656 and was fixed in v11.2.0 (2024-05-14). Since then, animating to display: none holds the visible value for the duration of the animation and applies none when the animation completes — so with your 0.7s duration, display switches to none after 0.7s (by design, so the element stays visible while it fades out). The transitionEnd workaround is no longer required. If you can still reproduce on motion12, please open a new issue with a reproduction. gh api -X PATCH repos/motiondivision/motion/issues/2563 -f stateclosed -f state_reasoncompleted验证gh api repos/motiondivision/motion/issues/2563 --jq .state返回closed。完成标准与停止条件Done criteriaStep 1 测试通过issue 在 APPROVED 后以completed原因注释并关闭不修改任何源文件更新 plans/issues/README.md 状态行。STOP conditionsStep 1 失败 → 报告并不得关闭README 行未 APPROVED → 置为 BLOCKED。Drift check每次执行前先跑gh api repos/motiondivision/motion/issues/2563 --jq .state若 issue 已关闭则直接标记 DONE 停止避免重复劳动。七、小结从奇怪 bug到可复用的修复范式issue-2563 的价值在于它揭示了一个典型回归的三层结构症状display: none不生效、根因异步关键帧解析重写破坏离散值混合且只有被isNone()特殊化的none走受损路径、修复mixVisibility把display/visibility定义为二值可见性插值显示立即生效、隐藏动画结束生效。它同时演示了与 issue-2656 合并关闭的跨 issue 协同方式同根因问题共享修复与回归测试通过-t display定向测试 GitHub CLI 完成闭环。对使用 Motion 的开发者本文可以提炼出两条实践结论对display/visibility这类离散属性动画期间的值变化是阶梯式的显示方向立即切换隐藏方向在动画完成时切换——这正是淡出后自动隐藏元素的标准模式无需再借助transitionEnd。遇到某个特殊值失效、其他值正常的动画 bug 时可以顺着特殊值在解析器/mixer 中的特殊分支这条线索排查如isNone()对none的特殊处理往往能快速定位到回归点。进一步阅读根因与修复细节见 plans/issues/issue-2656.md核心实现见 visibility.ts、complex.ts、is-none.ts、can-animate.ts回归测试见 animate-prop.test.tsx。赞分享前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载相关推荐Readest 自动导入按子文件夹分组失效问题修复解析Issue 5423 的根因、修复与工程实践Readest 自动导入按子文件夹分组失效问题修复解析Issue 5423 的根因、修复与工程实践 导读 本文基于 Readest 仓库中的修复记忆文档桌面应用跨平台前端Flutter camera 插件版本演进全解析从 CHANGELOG 看 API 迭代、架构迁移与工程实践Flutter camera 插件版本演进全解析从 CHANGELOG 看 API 迭代、架构迁移与工程实践 camera 是 Flutter 官方维护的相机前端UI组件Motion 拖拽惯性修复实录hold-then-flick 速度稀释问题issue-1747的根因、修复与回归验证Motion 拖拽惯性修复实录hold then flick 速度稀释问题issue 1747的根因、修复与回归验证 本篇技术指南以开源动画库 Motio前端UI组件上一篇Rebound高级技巧创建复杂动画链和交互效果下一篇ncc源码中的代码复用策略工具库与辅助函数创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考