optimize-web-animations
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseOptimize Web Animations
优化Web动画
Core Rule
核心准则
Measure the real page before editing. The goal is not to remove motion; it is to make offscreen work stop, visible motion resume correctly, and route/unmount cleanup release long-lived resources.
Use Codex Browser when available, especially for localhost pages. Do not use Chrome unless the user explicitly asks for it.
在编辑前先测量真实页面性能。目标并非移除动画,而是让屏幕外的动画停止运行、可视区域的动画正确恢复,以及在路由切换/组件卸载时清理释放长期占用的资源。
若可用,优先使用Codex Browser,尤其是针对本地localhost页面。除非用户明确要求,否则不要使用Chrome。
Workflow
工作流程
-
Inspect repo context.
- Read or local instructions.
AGENTS.md - Run early.
git status --short - Find page components, animation hooks, CSS keyframes, ,
requestAnimationFrame,setInterval, canvas/WebGL/physics components, media elements, GSAP timelines/tweens, and existing visibility utilities.setTimeout - Search effect cleanup for event listeners, observers, RAF loops, intervals, timers, external scripts, media streams, WebGL textures/materials/geometries/renderers, and async work that can complete after unmount.
- If the worktree is dirty, plan narrow staging from the start.
- Read
-
Capture a baseline in the browser.
- Open the exact route the user named.
- Profile at top, mid-page, footer/lower content, and one mobile viewport when layout could differ.
- Count CSS animations by computed ,
animationName, and visibility. IncludeanimationPlayStateand::before.::after - Inspect canvases/WebGL elements separately; CSS profiling does not prove RAF loops have stopped.
- Record which animation names are running offscreen and the DOM owners responsible.
- For memory/leak asks, also record element/canvas/image/iframe counts, exposed JS heap metrics when available, an idle sample after 10-30 seconds, and a short route-cycle sample. If heap APIs return or the Browser sandbox blocks monkey-patching, say so and rely on stable observable counts plus source audit.
null - Keep stress tests bounded. A Browser tab crash during profiling is evidence of overload, but do not over-attribute the cause unless reproduced by a minimal test.
- See for a reusable Codex Browser evaluator.
references/browser-profiling.md
-
Patch the smallest owner that controls the motion.
- Prefer an existing page reveal/visibility hook if the app has one.
- Otherwise add an that toggles a stable class such as
IntersectionObserveron sections and animated child elements.is-offscreen - Pause CSS animations with targeted rules:
css
main > section.is-offscreen .expensive-animation,
.expensive-animation.is-offscreen {
animation-play-state: paused !important;
}- For repeated cards or placeholders, observe the card shell and the animated descendants, not the whole document.
- For marquee/ticker tracks, pause the track when its section is offscreen.
- For skeleton loaders and pseudo-element glimmers, include and
::beforepause selectors where needed.::after - For canvas/WebGL/physics loops, gate the RAF loop directly:
- Start when the canvas/container intersects.
- Cancel when offscreen.
requestAnimationFrame - Resume on re-entry.
- Disconnect observers and cancel frames on cleanup.
- Add a non-visual debug marker such as when it helps browser verification.
data-animation-active
- Respect if the component already does, and avoid introducing React render loops for scroll/animation state.
prefers-reduced-motion - For leak hardening:
- Clear every timeout/interval created by the effect.
- Cancel RAF before unmount and before restarting a loop.
- Disconnect ,
IntersectionObserver,ResizeObserver, and custom subscriptions.MutationObserver - Remove global/window/document listeners with the same handler reference.
- Dispose Three/WebGL textures, materials, geometries, renderers, and remove renderer DOM nodes.
- Kill GSAP tweens/timelines for DOM nodes and mutable objects such as shader uniforms.
- Stop media streams and pause detached video/audio sources.
- Guard async loaders with an flag and dispose loaded resources if they resolve after unmount.
isDisposed - In React cleanup, capture values inside the effect before returning cleanup if lint warns the ref may change.
ref.current - Cap physics or simulation frame deltas after visibility pauses so delayed frames do not run oversized updates.
-
Verify behavior, not just builds.
- Reload the route and rerun the same top/mid/footer/mobile profiles.
- Target result: for the page sections under test.
offscreenRunningCount: 0 - Confirm visible animations still run or resume when scrolled into view.
- Confirm RAF/canvas loops report inactive offscreen and active in view, or otherwise prove cancellation from source/runtime state.
- For leak audits, compare before/after route cycles and idle samples. DOM/canvas/image counts should return to the same baseline after repeated navigation, allowing for small expected async content changes.
- Exercise a normal page interaction such as search/filter/navigation so the observer does not break dynamic content.
- Check fresh-tab console warnings/errors.
-
Run local checks.
- Use the repo's normal gates. For React/Vite apps this is often:
bash
git diff --check
npm run lint
npm run build- Mention known non-fatal warnings separately from failures.
- Commit narrowly when requested by repo/user instructions.
- If unrelated dirty changes exist, use an isolated index:
bash
rm -f /tmp/<task>-index
GIT_INDEX_FILE=/tmp/<task>-index git read-tree HEAD-
检查仓库上下文。
- 阅读或本地说明文档。
AGENTS.md - 尽早执行命令。
git status --short - 查找页面组件、动画钩子、CSS关键帧、、
requestAnimationFrame、setInterval、Canvas/WebGL/物理组件、媒体元素、GSAP时间线/补间动画,以及已有的可见性工具。setTimeout - 检查事件监听器、观察者、RAF循环、定时器、外部脚本、媒体流、WebGL纹理/材质/几何体/渲染器,以及可能在组件卸载后仍会完成的异步任务的清理逻辑。
- 如果工作区存在未提交的修改,从一开始就规划好范围狭窄的暂存方案。
- 阅读
-
在浏览器中捕获基准数据。
- 打开用户指定的具体路由。
- 在页面顶部、中部、底部/下方内容区域,以及布局可能不同的移动端视口分别进行性能分析。
- 通过计算、
animationName和可见性统计CSS动画数量,包括animationPlayState和::before伪元素。::after - 单独检查Canvas/WebGL元素;CSS性能分析无法证明RAF循环已停止。
- 记录哪些动画名称在屏幕外仍在运行,以及对应的DOM元素所有者。
- 针对内存泄漏排查需求,还需记录元素/Canvas/图片/iframe数量、可用的JS堆指标、10-30秒后的空闲采样数据,以及短路由循环采样数据。如果堆API返回或浏览器沙箱阻止猴子补丁,需说明情况并依赖稳定的可观测计数及源代码审计。
null - 压力测试需控制范围。性能分析期间浏览器标签崩溃是过载的证据,但除非通过最小化测试复现,否则不要过度归因。
- 可参考获取可复用的Codex Browser评估方法。
references/browser-profiling.md
-
修改控制动画的最小范围代码。
- 如果应用已有页面显示/可见性钩子,优先使用。
- 否则添加,为页面区块和动画子元素切换稳定的类名,例如
IntersectionObserver。is-offscreen - 通过针对性规则暂停CSS动画:
css
main > section.is-offscreen .expensive-animation,
.expensive-animation.is-offscreen {
animation-play-state: paused !important;
}- 对于重复的卡片或占位符,观测卡片外壳及其动画子元素,而非整个文档。
- 对于滚动字幕/滚动轨道,当其所在区块处于屏幕外时暂停轨道动画。
- 对于骨架加载器和伪元素闪烁效果,必要时添加和
::before的暂停选择器。::after - 对于Canvas/WebGL/物理循环,直接控制RAF循环:
- 当Canvas/容器进入可视区域时启动循环。
- 当处于屏幕外时取消。
requestAnimationFrame - 重新进入可视区域时恢复循环。
- 在清理阶段断开观察者并取消帧请求。
- 若有助于浏览器验证,添加非可视化调试标记,例如。
data-animation-active
- 如果组件已支持,需保持兼容,避免因滚动/动画状态引发React渲染循环。
prefers-reduced-motion - 针对内存泄漏加固:
- 清除所有由特效创建的计时器/定时器。
- 在组件卸载和重启循环前取消RAF。
- 断开、
IntersectionObserver、ResizeObserver和自定义订阅。MutationObserver - 使用相同的处理函数引用移除全局/窗口/文档监听器。
- 销毁Three/WebGL的纹理、材质、几何体、渲染器,并移除渲染器DOM节点。
- 终止针对DOM节点和可变对象(如着色器 uniforms)的GSAP补间/时间线。
- 停止媒体流并暂停分离的视频/音频源。
- 为异步加载器添加标志,若在组件卸载后才完成加载,则释放已加载资源。
isDisposed - 在React清理逻辑中,如果代码检查工具警告ref可能发生变化,需在返回清理函数前捕获effect内的值。
ref.current - 在可见性暂停后限制物理或模拟帧的增量,避免延迟帧执行过大的更新操作。
-
验证行为,而非仅验证构建结果。
- 重新加载路由并重复执行顶部/中部/底部/移动端的性能分析。
- 目标结果:测试页面区块的。
offscreenRunningCount: 0 - 确认可视区域的动画仍能正常运行,或在滚动进入可视区域时恢复运行。
- 确认RAF/Canvas循环在屏幕外时处于非活跃状态,在可视区域时处于活跃状态,或通过源代码/运行时状态证明已取消。
- 针对内存泄漏审计,比较路由循环前后和空闲采样的数据。DOM/Canvas/图片数量在重复导航后应回到相同基准值,允许存在少量预期的异步内容变化。
- 执行常规页面交互,如搜索/筛选/导航,确保观察者不会破坏动态内容。
- 检查新标签页的控制台警告/错误。
-
运行本地检查。
- 使用仓库的常规检查流程。对于React/Vite应用,通常是:
bash
git diff --check
npm run lint
npm run build- 将已知的非致命警告与错误分开说明。
- 若仓库/用户要求,进行窄范围提交。
- 如果存在无关的未提交修改,使用独立索引:
bash
rm -f /tmp/<task>-index
GIT_INDEX_FILE=/tmp/<task>-index git read-tree HEADApply only the intended hunks to the temporary index.
仅将预期的代码块应用到临时索引。
GIT_INDEX_FILE=/tmp/<task>-index git diff --cached --check
GIT_INDEX_FILE=/tmp/<task>-index git commit -m "Pause offscreen <page> animations"
git restore --staged <files> 2>/dev/null || true
- Never stage broad files from a dirty worktree unless every hunk belongs to the task.
7. Report with evidence.
- Lead with findings: what was still running, what looked leak-prone, and what could not be measured.
- Separate source-audit risks from live Browser measurements.
- Include the exact sampled route(s), offscreen animation counts, DOM/canvas count stability, route-cycle result, and local checks.
- State limitations plainly, especially unavailable heap counters or blocked Browser instrumentation.GIT_INDEX_FILE=/tmp/<task>-index git diff --cached --check
GIT_INDEX_FILE=/tmp/<task>-index git commit -m "Pause offscreen <page> animations"
git restore --staged <files> 2>/dev/null || true
- 除非所有代码块都属于当前任务,否则不要从脏工作区暂存大范围文件。
7. 附带证据提交报告。
- 先说明发现的问题:哪些内容仍在运行、哪些存在泄漏风险、哪些无法测量。
- 将源代码审计风险与浏览器实时测量结果分开。
- 包含具体的采样路由、屏幕外动画数量、DOM/Canvas计数稳定性、路由循环结果和本地检查情况。
- 明确说明局限性,尤其是不可用的堆计数器或被阻止的浏览器工具。Good Fix Patterns
优秀修复模式
- Section-level plus element-level
is-offscreenfor long sections where below-the-fold child animations can still run.is-offscreen - Shared visibility selector constants per route, such as .
COURSES_PAGE_ANIMATION_VISIBILITY_SELECTOR - thresholds around
IntersectionObserverfor animation gating.0.01 - Direct RAF loop control for WebGL/canvas effects; CSS cannot pause JavaScript render loops.
animation-play-state - Frame delta caps for physics loops that resume after a paused or delayed frame.
- Captured cleanup nodes for React refs used by GSAP/WebGL effects.
- guards for image/video/texture/data loaders that may resolve after unmount.
isDisposed - Short idle and route-cycle probes to catch accumulating DOM nodes, canvases, iframes, or unreleased media.
- 区块级结合元素级
is-offscreen,适用于长页面中折叠区域下的子动画仍可能运行的场景。is-offscreen - 每个路由使用共享的可见性选择器常量,例如。
COURSES_PAGE_ANIMATION_VISIBILITY_SELECTOR - 用于动画控制的阈值设置为
IntersectionObserver左右。0.01 - 直接控制WebGL/Canvas特效的RAF循环;CSS的无法暂停JavaScript渲染循环。
animation-play-state - 物理循环在暂停或延迟后恢复时限制帧增量。
- 为GSAP/WebGL特效使用的React refs捕获清理节点。
- 为可能在组件卸载后才完成的图片/视频/纹理/数据加载器添加守卫。
isDisposed - 短时间的空闲和路由循环探测,以捕获累积的DOM节点、Canvas、iframe或未释放的媒体资源。
Avoid
避免事项
- Removing all animations to make the profile pass.
- Pausing visible hero motion because an ancestor selector is too broad.
- Assuming covers pseudo-elements or JavaScript RAF loops.
animation-play-state - Trusting a single top-of-page measurement on long pages.
- Treating unavailable heap counters as proof there is no memory leak.
- Running unbounded stress loops in the Browser; use bounded cycles and record crashes without overstating causality.
- Using screenshots alone as performance proof.
- Letting unrelated local hunks ride along in the commit.
- 为了通过性能分析而移除所有动画。
- 由于祖先选择器范围过宽而暂停可视区域的核心动画。
- 假设能覆盖伪元素或JavaScript RAF循环。
animation-play-state - 在长页面上仅依赖顶部的单次测量数据。
- 将不可用的堆计数器视为无内存泄漏的证据。
- 在浏览器中运行无限制的压力循环;使用有限循环并记录崩溃情况,但不要过度夸大因果关系。
- 仅使用截图作为性能证明。
- 在提交时附带无关的本地代码块。