运动感觉
大多数人叫不出缓动曲线的名字,但他们确切地知道想要什么感觉:平静、有冲击力、高级、俏皮。这些词语对应着几十条曲线,AI会猜测,结果工作就变成了几轮“太快了”、“弹跳少一点”。这项技能为人和你提供了一套共享的名称,每个名称都绑定到精确的值。他们用感觉描述;你来处理数字。
它专为网站设计:营销、作品集、内容和商店网站。对于应用或产品UI,同样的感觉也适用;使用每种速度的快速端。
三个部分:
- 感觉:如何运动。 速度加上缓动曲线。14种感觉如下。
- 效果:什么在运动。 遮罩揭示、视差、跑马灯等。参见
references/feeds.md。 - 旋钮 调整一种感觉:速度、停止、超调、交错、距离、延迟、触发、强度。
用感觉说话,而不是曲线
这个人可能不懂任何运动术语,也不应该需要懂。
- 永远不要让他们选择 一种缓动、一条曲线、一个贝塞尔或一毫秒数。询问感觉,用日常生活中的画面来描述:“它应该像放下玻璃杯一样轻轻落地,还是像弹簧一样稍微过度然后稳定下来?”
- 用动画的外观来描述感觉,而不是它是如何制作的:“开始时很快,然后滑行到停止”,而不是“power4.out”。
- 感觉名称是可选的快捷方式。 顺带提一句(“我将这种感觉称为豪华”),以便他们可以重复使用。永远不要要求他们使用,也不要纠正他们的措辞。
- 数字放在代码中,再加上一句话给构建或审查它的人:“对于开发者:1.2 秒,
cubic-bezier(0.22, 1, 0.36, 1)。”当没有人会用它来构建时,省略那一行,例如当这个人只是在选择方向时。 - 此文件中的词语是给你的,不是给他们的。 旋钮名称和CSS术语不要出现在你的回复中。说“结尾的小弹跳”(超调)、“一个接一个”(交错)、“它移动了多远”(距离)、“它停止得有多轻柔”(停止)、“一次移动了多少”(强度)、“它从哪里开始生长”(原点)、“对于关掉动画的人”(减少动画)。
- 一行翻译,然后构建。 不要讲课,也不要耽误工作。唯一的例外是下面表格中标记为“询问”的词,这时需要先问一个简单的问题。
- 眼见为实。 当他们无法用语言表达时,提供演示页面(
assets/feeds-demo.html,在任何浏览器中打开)或构建一个快速的A/B切换开关让他们选择。
14种感觉
| 感觉 | 看起来像 | 速度 | CSS | GSAP |
|---|---|---|---|---|
| 清爽 | 一个电灯开关:瞬间,干净的停止 | 150 毫秒 | cubic-bezier(0.33, 1, 0.68, 1) | power2.out |
| 利落 | 快速射出并稳稳落地 | 300 毫秒 | cubic-bezier(0.16, 1, 0.3, 1) | expo.out |
| 弹力 | 快速,小小的过冲,然后稳定下来 | 600 毫秒 | 弹簧,弹跳 0.3(CSS中的 linear()) | 无弹簧;最接近 back.out(1.2) |
| 顺滑 | 一扇推拉门:柔和开始,柔和停止 | 500 毫秒 | cubic-bezier(0.65, 0, 0.35, 1) | power2.inOut |
| 轻柔 | 飘入并渐隐至停止 | 700 毫秒 | cubic-bezier(0.61, 1, 0.88, 1) | sine.out |
| 豪华 | 快速启动,长时间缓慢滑行至静止 | 1.2 秒 | cubic-bezier(0.22, 1, 0.36, 1) | power4.out |
| 飘浮 | 一片羽毛:缓慢而均匀,没有明确的开始或停止 | 2 秒 | cubic-bezier(0.37, 0, 0.63, 1) | sine.inOut |
| 戏剧性 | 剧院幕布:蓄势,冲刺,稳定 | 1.2 秒 | cubic-bezier(0.87, 0, 0.13, 1) | expo.inOut |
| 蓄力 | 弹弓:先向后拉,在终点处过冲 | 800 毫秒 | cubic-bezier(0.68, -0.6, 0.32, 1.6) | back.inOut(1.7) |
| 弹出 | 跳过头一次然后回来 | 450 毫秒 | cubic-bezier(0.34, 1.56, 0.64, 1) | back.out(1.7) |
| 弹性 | 果冻:摇晃几下,然后稳定下来 | 1 秒 | linear() 来自 elastic.out | elastic.out(1, 0.3) |
| 弹跳 | 球落地 | 900 毫秒 | linear() 来自 bounce.out | bounce.out |
| 机械式 | 传送带:全程相同速度 | 1 秒 | linear | none |
| 步进式 | 翻页书:一帧一帧地跳 | 900 毫秒 | steps(6) | steps(6) |
一种感觉在任何速度下都保持其曲线:“400毫秒的弹力”是弹力的形状,只是更快。纯CSS中的弹簧、弹性和弹跳是从曲线采样的 linear() 列表;现成的值在 references/feels.md(“现成可用的代码”)中。弹簧“弹跳”是SwiftUI和Motion使用的0–1值(阻尼比 = 1 − 弹跳)。
如何工作
- 首先检查元素: 它被看到的频率,以及它是界面部分还是展示运动(下面的“选择感觉之前”)。这可能意味着更少的运动,或者没有运动。
- 翻译 他们的词语为感觉 + 旋钮 + 效果,使用下面“日常用语”表格。如果两三种感觉都合适,问一个简单的问题(或显示A/B切换)。
- 在构建之前用一行简单的话说出计划,例如“标题行在你滚动到它们时向上滑入并缓慢停止,只一次(感觉:豪华)。”他们可以在任何东西构建之前改变方向。
- 构建 使用持续时间、延迟和缓动作为CSS变量或一个配置对象(
--motion-dur,--motion-ease,--motion-stagger),这样每次调整都是一个数字的改变。遵循“构建规则”。 - 报告 用平实的语言,并附上开发者的值。对于更改,展示前 → 后:“再慢一点:1.2 秒 → 1.8 秒”。保留之前的值,这样“回去”就能精确恢复它们。
- 保持一致性: 一旦网站的感觉明确了,建议将其保存为项目的设计标记中的运动调色板(下面),这样每个部分都以相同的方式运动。
整个网站的运动调色板
一个好的网站使用2或3种感觉,而不是14种:一种用于界面部分,一种用于展示时刻,可能一种用于强调。当有人根本无法描述运动时,用三个词询问品牌,并用一行提出一个调色板。
| 品牌听起来像 | 展示 | 界面 | 强调 |
|---|---|---|---|
| 平静、值得信赖、关怀(健康、金融、法律) | 轻柔 | 清爽 | — |
| 高级、编辑(时尚、建筑、酒店) | 豪华 | 清爽 | 首次加载用戏剧性 |
| 大胆、快速、技术(初创公司、SaaS、体育) | 利落 | 清爽 | 弹出 |
| 友好、俏皮(儿童、食品、消费应用) | 弹力 | 利落 | 弹出或弹跳 |
| 创意、表现力(工作室、作品集、代理机构) | 豪华或戏剧性 | 利落 | 蓄力 |
| 梦幻、自然(健康、美容、冥想) | 循环用飘浮,内容用轻柔 | 清爽 | — |
| 复古、原始(游戏、音乐、粗野主义) | 步进式 | 清爽 | 机械式跑马灯 |
选择感觉之前
元素多久被看到一次?
令人愉悦一次的运动,在第十次时就会碍事。将运动量与访问者遇到元素的频率相匹配。
| 频率 | 示例 | 运动 |
|---|---|---|
| 每页每次访问 | 导航链接、页眉、按钮、页脚链接、表单字段 | 小而快:清爽,或者只改变颜色。键盘焦点立即显示焦点环,没有运动。这些元素的入场(例如导航在加载时滑入)仅在第一次页面浏览时播放。 |
| 每次点击 | 页面过渡 | 短:600毫秒或更少,即使品牌是慢而高级的 |
| 每页一次 | 主角区、章节揭示、案例研究卡片、数字计数 | 完整的感觉。滚动揭示只播放一次,而不是每次你往回滚动时再次播放。 |
| 每次访问一次 | 介绍加载器、首次加载主角区时刻 | 给戏剧性和愉悦留出空间。仅在第一次页面浏览时运行加载器。 |
调色板的强调放在结果上,而不是控件上:添加到购物车的按下保持快速(清爽),而购物车计数才是弹出动画。
当有人要求在一个每页元素上使用缓慢的感觉时,构建快速版本并用一句话说明如何切换:“导航链接在每个页面上,所以我保持了快速;慢速版本是一个数字的替换。”不要等待回答;这是他们的决定。
界面部分还是展示?
- 界面部分: 访问者正试图做某事。导航下拉菜单、移动菜单、标签页、手风琴、模态框、工具提示、表单反馈、筛选器。将它们保持在 300 毫秒或更少,无论感觉如何;移动菜单和模态框可以上升到 400 毫秒。退出以大约75%的进入速度运行:将较短的持续时间放在关闭状态。
- 展示运动: 设定氛围。主角区、章节揭示、图片揭示、跑马灯、背景循环、首次加载时刻。使用感觉本身的速度,长时间也没问题。
按元素的默认值(当没有给出感觉时)
这里的速度替换了感觉本身的速度。任何反馈(“感觉廉价”、“太弹了”)都会覆盖这些默认值。
| 元素 | 感觉 | 速度 |
|---|---|---|
| 按钮、链接、切换、悬停、工具提示 | 清爽 | 150 毫秒 |
| 导航下拉菜单、标签页、卡片悬停 | 利落 | 250 毫秒 |
| 手风琴、常见问题 | 顺滑 | 300 毫秒 |
| 移动菜单、模态框、抽屉、面板 | 弹力 | 400 毫秒 |
| 拖放 | 弹力 | 600 毫秒 |
| 幻灯片、轮播、在屏幕上移动的东西 | 顺滑 | 500 毫秒 |
| 文字和图片揭示(向上淡入24像素,交错60-80毫秒) | 调色板的展示感觉;如果没有提示,则利落 | 感觉本身 |
| 页面过渡 | 顺滑 | 600 毫秒 |
| 主角区时刻、首次加载 | 戏剧性 | 1.2 秒 |
| 背景循环、环境形状 | 飘浮 | 2 秒 |
| 成功状态、徽章 | 弹出 | 450 毫秒 |
| 加载器、跑马灯、滚动擦洗运动 | 机械式 | 根据需要 |
日常用语
使用这些进行翻译。左列是人们实际说的话;都不需要运动知识。在较长的对话中,每个翻译展示一次,然后静默应用。
词语 → 感觉
| 他们说 | 理解为 | 何时询问 |
|---|---|---|
| 高级、昂贵、优雅、有档次、高端、豪华、“像一本杂志”、“缓慢而自信” | 豪华 | — |
| 顺滑、流畅、黄油般、无缝、丝滑、“滑行” | 顺滑(横向移动)或轻柔(出现) | 元素不能明显提示时:“从头到尾均匀滑行,还是轻轻到达并渐隐至停止?” |
| 柔和、平静、放松、安静、舒适、温暖、“不花哨” | 轻柔 | — |
| 微妙、几乎不易察觉、“不想让人注意到” | 清爽(界面)或轻柔(内容) | — |
| 梦幻、轻盈、漂浮、失重、“像羽毛”、“像在水下” | 飘浮 | — |
| 快、迅速、响应快、即时、点击强、紧凑、“像电灯开关” | 清爽(小东西)或利落(大东西) | 大小不明确 |
| 有冲击力、有活力、大胆、自信、有力、“重重落地”、“有态度” | 利落 | — |
| 自然、有物理感、有生命力、“像手机应用”、“像真实物体” | 弹力 | — |
| 有趣、俏皮、可爱、友好、顽皮、快乐 | 弹出、弹力或弹性 | 几乎总是:“跳过头一点、柔和的弹簧式稳定,还是果冻般的摇晃?” |
| 弹跳的 | 弹跳(落地并弹跳)或弹出/弹力(过冲后稳定) | 总是:“像球落地一样弹跳,还是稍微过头然后稳定?” |
| 果冻状、摇晃的、橡胶感、有弹性、抖动 | 弹性 | — |
| 电影感、史诗、“像电影”、有力、沉重、“一个惊喜时刻”、大揭示 | 戏剧性 | — |
| 卡通、“先蓄力”、“像弹弓” | 蓄力 | — |
| 机器人、恒定、稳定、“像传送带”、滚动条 | 机械式 | — |
| 复古、断续、定格动画、像素、故障感、“像老式电子游戏”、翻页书 | 步进式 | — |
| 现代、酷、不错、简洁、“让它出彩”、“让它好看”、“加点动画”、“让它有活力” | 本身没有意义 | 总是:提供3种适合上下文的用平实语言描述的感觉,或者用三个词询问品牌(“让它出彩”可能意味着弹出感觉,或者只是“更引人注目”) |
反馈 → 旋钮
| 他们说 | 更改 |
|---|---|
| 太快、匆忙、“眨眼的功夫就错过了”、“我看不到它” | 速度 × 1.5 |
| 太慢、迟缓、滞后、“永远完不成”、“我在等它” | 速度 ÷ 1.5。在界面部分上,同时检查300毫秒的限制。 |
| 突然、刺耳、颠簸、“弹回原位”、“停止得太突然” | 停止:更柔和,沿着 power2.out → power3.out → power4.out → expo.out 前进一步(CSS值在 references/feels.md) |
| 软绵绵、“结尾拖沓”、“爬行”、“需要太长时间才能稳定” | 停止:更坚硬(更短的尾巴),或更快 |
| 微弱、无生气、“需要更多冲击力”、“没有落地” | 停止更坚硬 + 速度 ÷ 1.5,或切换到利落 |
| 太弹了、“太多了”、俗气、孩子气、“过分了” | 超调降低一步:弹出 cubic-bezier(0.34, 1.56, 0.64, 1) → 一点点 cubic-bezier(0.34, 1.4, 0.64, 1) → 无(利落) |
| 感觉廉价 | 通常是运动过多:距离降低和超调降低;如果慢,则速度 ÷ 1.5 |
| 跳跃、抖动、“不知从哪里突然出现” | 它从无到有生长或超调:从 scale(0.95) 开始(构建规则),超调降低一步,距离降低 |
| 僵硬、机器人、死气沉沉、“需要更多生命力” | 超调增加一步,或切换到弹力 |
| 平淡、无聊、“什么都没发生”、“我没注意到” | 交错增加,或距离增加 |
| 忙碌、分散注意力、“事情太多了”、烦人 | 强度降低,交错降低。烦人通常意味着看得太频繁:检查“元素多久被看到一次?” |
| “让我头晕”、“让我想吐” | 移除视差、大缩放和长距离移动;检查减少动画的备用方案 |
| 开始太晚/太早 | 延迟减少/增加,以100毫秒为步长 |
| 感觉不对、“不太对”、“我不知道哪里错了” | 不要猜:问“是速度、开始方式、停止方式,还是结尾的小弹跳?”,或并排显示两个版本 |
构建规则
- 用
transform移动,用opacity淡出。 不要对width、height、top、left或margin做动画;它们在滚动时会卡顿。对于手风琴,grid-template-rows: 0fr → 1fr是公认的例外(内容放在带有min-height: 0; overflow: hidden的内部包装器中)。永远不要transition: all;命名属性。 - 永远不要从无到有生长。 从
scale(0.95)开始缩放进入,并带有opacity: 0(对于更大的弹出使用0.9),永远不要用scale(0)。 - 根据视口裁剪而不是根据父级。
overflow: clip是你想要的修剪图案的方式,而不是overflow: hidden,后者设置了新的堆叠上下文并且不能与overflow: visible在同一轴上共存。 - 尊重
prefers-reduced-motion。 默认是关于减速运动的,所以“减少”意味着减少不必要的运动,而不一定是消除所有运动。如果用户偏好是reduce:- 移除视差效果、大缩放、长距离移动和所有背景循环。
- 将所有持续时间和延迟减少到150毫秒以下(将任何值乘以
0.3并向下取整)。 - 保留出必要的界面运动:导航下拉菜单、工具提示、切换、反馈指示器仍然可以动画,但速度要快得多。
- 对于使用GSAP的项目:在GSAP之上应用此规则,使用一个预设的“快速”覆盖。
- 触摸设备需要更大的交互目标。 可悬停的元素在触摸上变为点击,确保所有交互式元素在触摸端有至少44×44像素的点击目标大小,并且触摸延迟(来自
fastclick或 CSStouch-action: manipulation)已经被处理。 - 检查时间对应的所见。 动画的视觉节奏属性应该与屏幕上发生的其他事情协调。当用户的语言设置为从右到左时,将进入方向反转,以便运动指向阅读方向。
运动审查清单
在部署之前,检查以下几点(或者与某人一起审查):
- 所有进入动画是否只播放一次? 标记每个部分以免再次触发滚动揭示。
- 每次加载是否有多于3个同时动画? 如果是,交错它们以减少视觉过载。
- 退出是否比进入更快? 对于界面元素,退出运行约75%的进入时间。
- 是否所有焦点指示器都使用焦点环,没有任何动画? 键盘用户需要即时反馈。
- 对于减少动画,是否有清晰、功能性的备用方案? 元素可见,布局不变。
- 动画是否依赖
window.innerHeight或window.innerWidth? 确保它们被防抖处理(~100毫秒)。 - 是否所有嵌套动画都使用声明的CSS变量? 没有硬编码的计时。
- 是否所有动画都在改变
transform和opacity属性? 没有布局触发的属性。 - 在触控设备上,悬停触发动画是否被移除或替换? 触摸没有悬停状态。
- 是否所有动画都最终稳定下来? 无限循环应该在背景/装饰元素上,而不是在内容元素上。