关于 motion-principles
motion-principles 是面向 Claude Code、claude.ai 和 Cowork 的创意编码技能,涵盖 Web、Android 和 Apple 平台的动效与交互设计。它提供时长、缓动、无障碍和性能指导,并链接到平台专属技能以深入实践。
主要功能
- 01跨平台动效指南
- 02时长与缓动建议
- 03减少动态效果的无障碍指导
- 04动画性能检查清单
- 05链接平台专属技能
适用场景
- 01规划 UI 转场行为
- 02检查动画是否支持减少动态效果
- 03改善动画流畅度和性能
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ athevon/ genjutsu --skill motion-principles安装后在 Claude Code 中输入/motion-principles确认已加载。
详细介绍
版本敏感。 以下所有 API 名称、SDK 门槛和浏览器支持声明均已于 2026-09-08 对照一手来源核实。核实了什么以及何时核实,见
_jutsu/VERSIONS.md。如果该日期已过,请在依据版本号采取行动前重新核实。
动效原则
基础内容。每次调用创意技能时都会加载。 此处列出简明规则。深入内容见
references/。
时长规则
| 场景 | 时长 | 原因 |
|---|---|---|
| 微交互(切换、悬停、焦点) | 100-150ms | 即时反馈,不会让人感觉有延迟 |
| UI 过渡(模态框、抽屉、标签切换) | 200-300ms | 流畅但绝不迟滞 |
| 页面/路由过渡 | 300-500ms | 构建空间叙事 |
| 滚动驱动 / 3D | 自由(基于进度) | 与用户输入绑定,不设固定时长 |
频率规则: 动画播放得越频繁,就必须越短、越含蓄。 按钮悬停(每天 1000 次)= 100ms 透明度变化。引导流程揭示(仅一次)= 600ms 以上的完整编排。
缓动速查表
| 操作 | 缓动 | 原因 |
|---|---|---|
| 元素进入 | ease-out / 弹簧 | 减速并停在静止位置(自然到达) |
| 元素退出 | ease-in | 加速离开(不挡路) |
| 元素在状态间移动 | ease-in-out | 平滑启动和停止 |
| 与滚动同步 | linear / none | 与输入保持 1:1 匹配,不会感觉滞后 |
| 弹跳/活泼 | 弹簧(欠阻尼) | 超调带来生命力 |
| 干脆利落的 UI | cubic-bezier(0.2, 0, 0, 1) | 快速启动,平滑落定 |
退出总是比进入更含蓄。 进入:300ms ease-out,完整编排。退出:200ms ease-in,仅改变透明度。
原生缓动对应关系(跨平台)
| Web(CSS / JS) | SwiftUI | Compose |
|---|---|---|
cubic-bezier(0.2, 0, 0, 1) | .spring(response: 0.4, dampingFraction: 0.85) 或 .snappy | spring(stiffness = Spring.StiffnessMedium, dampingRatio = 0.85f) |
ease-out | .easeOut(duration: 0.3) | tween(durationMillis = 300, easing = LinearOutSlowInEasing) |
ease-in | .easeIn(duration: 0.2) | tween(durationMillis = 200, easing = FastOutLinearInEasing) |
| 弹簧(弹跳) | .bouncy(iOS 17+) | spring(stiffness = Spring.StiffnessLow, dampingRatio = Spring.DampingRatioMediumBouncy) |
| 弹簧(平滑) | .smooth(iOS 17+) | spring(stiffness = Spring.StiffnessMedium, dampingRatio = Spring.DampingRatioNoBouncy) |
无障碍(不可妥协)
减少动态效果 - 必须遵守
每个动画组件都必须尊重用户对减少动态效果的偏好。无论平台如何,都不得例外。
| 平台 | API |
|---|---|
| Web CSS | @media (prefers-reduced-motion: reduce) |
| Web JS | window.matchMedia('(prefers-reduced-motion: reduce)') |
| SwiftUI | @Environment(\.accessibilityReduceMotion) var reduceMotion |
| UIKit | UIAccessibility.isReduceMotionEnabled(+ reduceMotionStatusDidChangeNotification) |
| Compose | 使用 Settings.Global.ANIMATOR_DURATION_SCALE 的自定义辅助函数(深入内容见 mobile-principles/references/accessibility-mobile.md) |
CSS:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
SwiftUI:
struct AnimatedView: View {
@Environment(\.accessibilityReduceMotion) var reduceMotion
@State var visible = false
var body: some View {
Text("Hello")
.opacity(visible ? 1 : 0)
.animation(reduceMotion ? .none : .spring(response: 0.4, dampingFraction: 0.85), value: visible)
}
}
Compose:
@Composable
fun rememberReduceMotion(): Boolean {
val context = LocalContext.current
return remember {
Settings.Global.getFloat(context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f) == 0f
}
}
@Composable
fun AnimatedComponent(visible: Boolean) {
val reduce = rememberReduceMotion()
val alpha by animateFloatAsState(
targetValue = if (visible) 1f else 0f,
animationSpec = if (reduce) snap() else spring(stiffness = Spring.StiffnessMedium)
)
}
其他无障碍要求
- 焦点指示器绝不能被动画隐藏
- 动画内容在每一帧都必须满足 WCAG 对比度比例(不能在过渡中途淡化到文字不可见)
- 循环动画:提供暂停机制
通用“禁止”规则
1. 绝不对 width/height/top/left 添加动画
每一帧都会触发布局重新计算 = 卡顿。
/* BAD */
.drawer { transition: height 0.3s ease; }
.drawer.open { height: 400px; }
/* GOOD */
.drawer { transition: transform 0.3s ease-out; transform: translateY(100%); }
.drawer.open { transform: translateY(0); }
SwiftUI 对应写法(错误 vs 正确):
// BAD - animates frame (causes layout pass)
.frame(height: open ? 400 : 0)
.animation(.easeInOut, value: open)
// GOOD - animates transform via offset
.offset(y: open ? 0 : 400)
.animation(.spring(), value: open)
Compose 对应写法(错误 vs 正确):
// BAD - animates size (full layout pass)
val height by animateDpAsState(if (open) 400.dp else 0.dp)
// GOOD - animates Y offset via graphicsLayer
val translation by animateFloatAsState(if (open) 0f else 400f)
Box(modifier = Modifier.graphicsLayer { translationY = translation })
2. 绝不缩放到 0
scale(0) 会让元素像掉进黑洞一样消失。始终保留最小值。
/* BAD */
.modal-exit { transform: scale(0); }
/* GOOD */
.modal-exit { transform: scale(0.95); opacity: 0; }
SwiftUI 对应写法:
// BAD
.scaleEffect(visible ? 1.0 : 0.0)
// GOOD
.scaleEffect(visible ? 1.0 : 0.95)
.opacity(visible ? 1.0 : 0.0)
Compose 对应写法:
// BAD
Modifier.graphicsLayer { scaleX = if (visible) 1f else 0f; scaleY = if (visible) 1f else 0f }
// GOOD
Modifier.graphicsLayer {
scaleX = if (visible) 1f else 0.95f
scaleY = if (visible) 1f else 0.95f
alpha = if (visible) 1f else 0f
}
3. 进入时绝不使用 ease-in
Ease-in = 缓慢启动。进入的元素若迟疑不前,会让人感觉出了问题。
/* BAD */
.card-enter { animation: fadeIn 0.3s ease-in; }
/* GOOD */
.card-enter { animation: fadeIn 0.3s ease-out; }
/* OR spring via JS for natural feel */
4. UI 交互耗时绝不超过 500ms
模态框、下拉菜单、工具提示、标签页——用户都在等待。请珍惜他们的时间。
// BAD
gsap.to(modal, { opacity: 1, y: 0, duration: 0.8 });
// GOOD
gsap.to(modal, { opacity: 1, y: 0, duration: 0.25, ease: "power2.out" });
SwiftUI:
// BAD
.animation(.easeInOut(duration: 0.8), value: state)
// GOOD
.animation(.spring(response: 0.25, dampingFraction: 0.85), value: state)
Compose:
// BAD
animateContentSize(animationSpec = tween(800))
// GOOD
animateContentSize(animationSpec = spring(stiffness = Spring.StiffnessMediumLow))
5. 绝不忽略 prefers-reduced-motion
这是无障碍要求,不是可有可无的选项。
// BAD
gsap.from('.hero-title', { opacity: 0, y: 40, duration: 0.6 });
// GOOD
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReduced) {
gsap.from('.hero-title', { opacity: 0, y: 40, duration: 0.6 });
}
SwiftUI / Compose 的对应实现,请参阅上方跨平台「减少动态效果」部分。
性能检查清单
- 仅对
transform和opacity(仅合成属性)进行动画处理 - 谨慎使用
will-change,并在动画完成后将其移除 - 对于 JS 动画,优先使用
requestAnimationFrame,而不是setTimeout/setInterval - 对于滚动驱动的动画:CSS
animation-timeline> JSIntersectionObserver> 滚动监听器 - 在低端设备上测试(在 DevTools 中将 CPU 限制为 4 倍)
快速参考:加载子技能
| 需求 | 加载 |
|---|---|
| 深入了解缓动、弹簧配置 | references/easing-guide.md |
| 可直接复制粘贴的进入/退出模式 | references/enter-exit-recipes.md |
| 设计师加权的风格选择 | references/designers.md |
| 移动端 UX 背景 | ../mobile-principles/SKILL.md |
| 桌面端 UX 背景 | ../desktop-principles/SKILL.md |
| GSAP 详细信息 | ../gsap/SKILL.md |
| Framer Motion 详细信息 | ../framer-motion/SKILL.md |
| 纯 CSS 动画 | ../css-native/SKILL.md |
| Three.js / R3F | ../threejs-r3f/SKILL.md |
| Canvas / 生成式内容 | ../canvas-generative/SKILL.md |
| Compose Android 动画 | ../compose-motion/SKILL.md |
| Compose Multiplatform | ../compose-multiplatform/SKILL.md |
| SwiftUI iOS/macOS 动画 | ../swiftui-motion/SKILL.md |
| Compose 高级图形 | ../compose-graphics/SKILL.md |
| SwiftUI 高级图形 | ../swiftui-graphics/SKILL.md |
| 视觉 / 动效 / 无障碍审计 | ../design-audit/SKILL.md |
| UI/UX 智能参考(84 种风格、192 种调色板) | ../ui-ux-pro-max/SKILL.md |