motion 仓库 issue-2450 复盘:`animate()` 驱动原生 SVG 元素 transform 的验证与回归测试
前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载导读本文以 plans/issues/issue-2450.md 为骨架完整还原一次针对 motion 仓库的“验证型”issue 修复计划当开发者通过animate()/useAnimate()直接驱动普通非motion.*组件SVG 子元素时transform 是否能被正确应用为 CSS 变换。文章既给出该计划的完整执行流程fixture、Cypress 规格、双 React 版本验证也结合当前仓库源码剖析了曾经导致 bug 的“dimension gate”机制、WAAPI 路由误判以及SVGVisualElement渲染管线的现状帮助读者理解 SVG 动画在 motion 内部的设计与调试方法。背景issue 2450 是什么issue-2450 是 motion 仓库记录的一个历史 bugCategory: bug (believed fixed on main)详见 plans/issues/issue-2450.md 的 Status 段。问题报告于 2024 年现象是通过animate()/useAnimate()动画的 SVG 子元素即不是用motion.*组件创建的元素从未收到 CSS transform。当时报告者已经给出了正确诊断其根因位于build-attrs.ts该文件只在“dimensions尺寸已定义”的情况下才把 transform 拷贝进style而 dimensions 只由 React 的motion管线提供config-motion.tscreateDOMVisualElement永远不会提供。也就是说动画值被构建成了 SVG attribute而不是可被 CSS 计算的style.transform。由于构成该机制的“两半”后来都已被移除这个 bug 被认为已在 main 上修复——但缺少一个专门覆盖animate() 原生 SVG 元素 transform 组合的回归测试。issue-2450 计划的全部价值就在于把这个“被认为已修复”变成“可被 CI 反复验证的事实”。当前状态源码侧的两处关键事实在撰写本计划时planned at commit42bfbe3ed当前仓库的源码状态已经与 bug 报告时截然不同计划文档明确给出了两处实现证据。1. dimension gate 已消失packages/motion-dom/src/render/svg/utils/build-attrs.ts 中的buildSVGAttrs现在无条件处理 transformcssStyleProperties[transform, opacity, offsetDistance, ...]里的键若出现在attrs中会被移动到styleL52-L57只要存在style.transform或attrs.transformOrigin就会设置transformOrigin默认值L59-L62只要存在style.transform就会强制设置style.transformBox fill-boxL64-L71并注明“SVG 的 element transform-origin 使用自身中心作为参照因此 transformBox 变为 fill-box”。相关的历史提交包括b5586d076“Removing types related to dimensions in SVG”与c550c9b48/44d2f467etransform-box 修复。从源码结构看这正是 issue 报告者所描述的“dimension gate”被整体移除后的形态不再有任何“dimensions 是否定义”的分支transform 一律走 style 通道。2.animate()的 element 路径仍创建SVGVisualElementpackages/framer-motion/src/animation/animate/subject.ts 的animateSubject中当 subject 是Element时调用animateElement经由 create-visual-element.ts 的createDOMVisualElementisSVGElement(element) !isSVGSVGElement(element)时创建SVGVisualElementL26-L28否则创建HTMLVisualElement创建的节点立即mount(element)并注册进visualElementStore。关键点该路径创建 VisualElement 时完全不涉及 dimensions渲染状态中只有transform、transformOrigin、style、vars、attrs等字段。因此“dimensions 只由 React motion 管线提供”的旧前提已经不成立。3. WAAPI 路由误判已修复#3081计划文档还提到一个“症状相同”的 WAAPI 路由 bug已在 v12.36.0 的86907d130提交“Fix SVG transform animations not applied without other SVG attributes (#3081)”中修复。当前仓库的 packages/motion-dom/src/utils/is-html-element.ts 中isHTMLElement的判断是isObject(element) offsetHeight in element !(ownerSVGElement in element)即ownerSVGElement存在说明是 SVG 元素时不再被误判为 HTML 元素SVG transform 由此走 JS 路径经过 SVG 渲染管线而不是被错误地路由到 Web Animations API。已有 E2E 覆盖 svg-transform-animation.ts配套 fixture svg-transform-animation.tsx验证了 #3081 的场景但它只覆盖motion.*组件这正是本计划要补齐的空白。计划的执行前提drift check任何计划开始前首先要执行两条“漂移检查”Drift check确认外部前提仍然成立gh api repos/motiondivision/motion/issues/2450 --jq .state→ 期望返回open若返回closed则立即停止。git log --oneline 42bfbe3ed..HEAD -- packages/motion-dom/src/render/svg/→ 检查 PR #3749worktree-style-effect是否已合并。若已合并SVG 渲染会迁移到packages/motion-dom/src/effects/svg/此时上述验证更加重要但 fixture/spec 的设计不变。从当前仓库看packages/motion-dom/src/effects/svg/下已经存在 index.ts其中svgEffect/svgSubjectEffect以createSelectorEffect/createEffect形式提供 SVG 值的读写通道transform 属性读取时返回各值类型默认值其余键走 attribute 读取与计划文档中“SVG 渲染迁移后”的描述一致。Step 1创建与 issue 完全镜像的 fixture计划只允许修改两个文件Scope 段明确列出 in-scope 清单其余任何文件都不许动dev/react/src/tests/svg-animate-plain.tsx新建packages/framer-motion/cypress/integration/svg-animate-plain.ts新建fixture 必须导出名为App的组件才能被dev/react/src/App.tsx中的import.meta.glob(./tests/*.tsx, { eager: true, import: App })自动收集并以?testsvg-animate-plain访问。计划给出的 fixture 代码如下import { useAnimate } from framer-motion import { useEffect } from react export function App() { const [scope, animate] useAnimate() useEffect(() { animate( #target, { x: 100, rotate: 45 }, { type: tween, ease: linear, duration: 10 } ) }, []) return ( svg ref{scope} width{300} height{300} rect idtarget x{0} y{0} width{50} height{50} fill#09f / /svg ) }设计要点rect是普通SVG 元素没有motion.rect也没有任何 transform 初始值——完全复现 issue 报告中的“非 motion 组件 animate()”组合useAnimate()返回的animate在useEffect中启动用#target选择器定位目标关键手法长时长10s 线性缓动。这样在动画进行到一半时读取计算样式能得到“成比例”的中间值约 50px 位移、22.5° 旋转如果 transform 缺失或目标值错误检查会按比例暴露问题。这是 CLAUDE.md 中约定的测试模式。Step 2创建 Cypress 规格packages/framer-motion/cypress/integration/svg-animate-plain.ts的断言设计计划文档 Step 2访问?testsvg-animate-plainwait(5000)即动画 50% 处用.then()而非.should()读取#target的getComputedStyle(el).transform期望是一个matrix(...)平移分量约为 50px允许 ±10 的时序容差并且不等于none额外断言getComputedStyle(el).transformBox fill-box——这是 SVG 渲染管线的标志正是旧 bug 会跳过的东西明确不要用el.getAnimations()SVG 的 transform 在这里走 JS 路径而非 Web Animations。关于断言风格的参照可以对比已有规格 svg-transform-animation.ts 中对rect.style.transform的matrix(...)正则解析与closeTo容差写法。Step 3在 React 18 与 React 19 双版本运行CI 会同时跑两个 React 版本因此规格必须在两个版本上都通过。运行配方来自仓库 CLAUDE.md仓库根目录前台执行# React 18 PORT$((10000 RANDOM % 50000)) cd dev/react TEST_PORT$PORT yarn vite --port $PORT DEV_PID$! npx wait-on http://localhost:$PORT cd ../../packages/framer-motion npx cypress run --headed --config baseUrlhttp://localhost:$PORT --spec cypress/integration/svg-animate-plain.ts kill $DEV_PID # React 19 — 同上但 cd 到 dev/react-19并追加 --config-filecypress.react-19.json配套的命令清单在计划文档 “Commands you will need” 表格中目的命令期望构建yarn build仓库根目录exit 0Cypress React 18上述配方spec 通过Cypress React 19上述配方react-19 变体spec 通过关闭 issue受控gh api -X PATCH repos/motiondivision/motion/issues/2450 -f stateclosed -f state_reasoncompletedclosed首次运行建议tail -60截取输出。Step 4结果处置disposition若两个版本都通过预期结果说明 bug 已在 main 上修复。若plans/issues/README.md中对应行状态为 APPROVED则在 #2450 上评论由移除 SVG dimensions gate 及86907d130、v12.36.0 修复并新增回归规格后执行受控关闭命令否则将该行标记为 BLOCKED“verified fixed; awaiting close approval”。若规格失败说明 bug 在当前管线仍然存在——立即 STOP报告失败的断言与计算出的 transform 值。此时本计划转变为“失败测试先行”的 FIX 计划前半部分调查起点是build-attrs.ts或 #3749 之后的effects/svg/build.ts与supportsBrowserAnimation路由。完成标准与停止条件计划文档的 Done criteria 与 STOP conditions 是验证型计划的标准治理边界Done criteriafixture spec 存在且在 React 18 与 React 19 本地均绿issue 已评论并关闭仅当行状态 APPROVED或行状态置为 BLOCKEDgit status确认 in-scope 清单之外无任何文件被修改plans/issues/README.md状态行已更新STOP conditions任一 React 版本上规格失败 → bug 存在报告不在本计划内修复一个版本通过、另一个失败 → 按 CLAUDE.md 调查不得跳过?testsvg-animate-plain加载失败fixture 注册方式变化→ 检查dev/react/src/tests/中同类测试的收集方式。维护备注与后续展望计划文档在 Maintenance notes 中给出两点明确的延伸方向PR #3749 合并后需重跑本规格该 PR 重写SVGVisualElement46/-46并把 attr 构建迁移到packages/motion-dom/src/effects/svg/build.tsSVG 值在支持处以 style 渲染。新规格恰好是这次迁移的回归闸门是“最容易受 SVG 重写扰动”的行为之一。明确延后motion/mini的animate()无 VisualElement走svgEffect需要同样的验证但那是独立的实现面不在本计划范围内。对读者而言issue-2450 的完整故事是一次典型的开源维护实践历史 bug → 根因被删掉而“隐性修复” → 用最小 fixture Cypress 规格把行为钉死 → 双 React 版本验证 → 受控关闭。而 plans/issues/README.md 中该行的状态P3标注“SVG animate() dimension gate removed (12.36.0); re-test after pr-3749”如实记录了这一过程是理解仓库验证治理流程的窗口。赞分享前端UI组件【免费下载链接】motionA modern animation library for React and JavaScript项目地址https://gitcode.com/GitHub_Trending/mo/motion点击查看免费下载相关推荐Motion 拖拽约束dragConstraints随元素尺寸变化自动重算issue 1827 的修复验证与回归测试方案Motion 拖拽约束dragConstraints随元素尺寸变化自动重算issue 1827 的修复验证与回归测试方案 导读 本文基于 plans/is前端UI组件Motion 仓库 issue-2234 验证实录AnimatePresence wait 模式新元素卡在 opacity 0的修复验证工作流Motion 仓库 issue 2234 验证实录AnimatePresence wait 模式新元素卡在 opacity 0的修复验证工作流 本文以 p前端UI组件OHIF ViewedDataServiceDICOM 阅片“已查看”状态跟踪服务的设计与实现OHIF ViewedDataServiceDICOM 阅片“已查看”状态跟踪服务的设计与实现 本文基于 OHIF 官方文档 ViewedDataServic前端UI组件上一篇DBeaver 性能监控完整指南用 Dashboard 与自定义扩展盯住慢查询5分钟入门下一篇SeetaFaceEngine商业应用从开源到产品化的10个成功案例指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考