
关于
适用于 React/Next.js 的生产级 UI 动效系统。适用于实现动画、过渡或动效模式。
name: motion-ui description: "适用于 React/Next.js 的生产级 UI 动效系统。在实现动画、过渡或动效模式时使用。" origin: ECC
Motion System v4.2
适用于 React / Next.js 的生产级 UI 动效系统。
专注于性能、无障碍性和可用性 — 而非装饰。
何时使用
当动效能够:
- 引导注意力(例如引导流程、关键操作)
- 传达状态(加载中、成功、错误、过渡)
- 保持空间连续性(布局变化、导航)
适用场景
- 交互组件(按钮、模态框、菜单)
- 状态过渡(加载中 → 已加载、打开 → 关闭)
- 导航和布局连续性(共享元素、交叉淡入淡出)
注意事项
- 无障碍性:始终支持减少动效
- 设备适配:针对低端设备调整
- 性能权衡:响应性优先于视觉流畅度
避免使用动效的情况
- 纯装饰性
- 降低可用性或清晰度
- 对性能产生负面影响
工作原理
核心原则
动效必须:
- 引导注意力
- 传达状态
- 保持空间连续性
如果以上都不满足 → 移除它。
安装
npm install motion
版本
motion/react- 当前 Motion for React 项目的默认导入路径(包名:motion)framer-motion- 仍依赖 Framer Motion 的项目的旧版导入路径
不要混用。 混用会导致内部调度器冲突和 AnimatePresence 上下文损坏 — 来自一个包的组件无法与另一个包的组件协调退出动画。
检查项目使用的版本:
cat package.json | grep -E '"motion"|"framer-motion"'
始终从同一来源一致导入:
// 正确(现代方式)
import { motion, AnimatePresence } from "motion/react"
// 正确(旧版方式)
import { motion, AnimatePresence } from "framer-motion"
// 永远不要在同一项目中混用两者
Motion Tokens
// motionTokens.ts
export const motionTokens = {
duration: {
fast: 0.18,
normal: 0.35,
slow: 0.6
},
// 将这些作为 `transition` 对象中的 `ease` 值使用:
// transition={{ duration: motionTokens.duration.normal, ease: motionTokens.easing.smooth }}
easing: {
smooth: [0.22, 1, 0.36, 1] as [number, number, number, number],
sharp: [0.4, 0, 0.2, 1] as [number, number, number, number]
},
distance: {
sm: 8,
md: 16,
lg: 24
}
}
使用示例:
import { motionTokens } from "@/lib/motionTokens"
<motion.div
initial={{ opacity: 0, y: motionTokens.distance.md }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: motionTokens.duration.normal,
ease: motionTokens.easing.smooth
}}
/>
性能规则
安全属性
- transform
- opacity
避免使用
- width / height
- top / left
规则:响应性 > 流畅度
设备适配
该启发式方法结合 CPU 核心数和可用内存以获得更可靠的信号。deviceMemory 在 Chrome/Android 上可用;回退方案覆盖 Safari 和 Firefox。
const isLowEnd =
typeof navigator !== "undefined" && (
// 低内存(仅 Chrome/Android;其他浏览器为 undefined → 视为高性能)
(navigator.deviceMemory !== undefined && navigator.deviceMemory <= 2) ||
// 少核心且无内存 API(覆盖弱硬件上的 Safari/Firefox)
(navigator.deviceMemory === undefined && navigator.hardwareConcurrency <= 4)
)
const duration = isLowEnd ? 0.2 : 0.4
无障碍性
JS (useReducedMotion)
import { motion, useReducedMotion } from "motion/react"
export function FadeIn() {
const reduce = useReducedMotion()
return (
<motion.div
initial={{ opacity: 0, y: reduce ? 0 : 24 }}
animate={{ opacity: 1, y: 0 }}
/>
)
}
CSS
@media (prefers-reduced-motion: reduce) {
.motion-safe-transition {
transition: opacity 0.2s;
}
.motion-reduce-transform {
transform: none !important;
}
}
Tailwind
<div class="motion-safe:animate-fade motion-reduce:opacity-100"></div>
架构与模式
核心模式
| 场景 | 模式 |
|---|---|
| 悬停反馈 | whileHover |
| 点击/按压反馈 | whileTap |
| 滚动时显示 | whileInView |
| 滚动关联值 | useScroll + useTransform |
| 条件挂载/卸载 | AnimatePresence |
| 小型布局偏移(单元素,< ~300px 变化) | layout 属性 |
| 大型布局偏移或全页重排 | 避免 layout;改用 CSS 过渡或页面级路由 |
| 复杂的命令式序列 | useAnimate |
为什么避免在大容器上使用
layout? Framer 的布局动画使用transform来协调位置,但在跨越整个视口或触发深层重排的元素上,测量成本会导致可见的卡顿和 CLS。建议使用 CSS Grid/Flexbox 过渡代替。
兼容工具
Claude CodeCursor
标签
移动端
