关于 charles-ux-animation
为界面设计与开发提供 UX 和动画规则的 skill,帮助团队规划交互、选择动效并检查实现。它按五个问题组织决策,并附有组件配方与动画技术参考。
主要功能
- 01按五个问题评估交互动效
- 02按触发频率选择动画时长
- 03提供组件动效配方与技术参考
- 04为多阶段动画编写分镜
- 05按严重程度整理评审发现
适用场景
- 01为菜单、弹窗和提示设计动效
- 02检查动画时长、曲线与中断行为
- 03审查交互并按严重程度记录问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lemonface0309/ charles-skills --skill charles-ux-animation安装后在 Claude Code 中输入/charles-ux-animation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
README.mdreferences/recipes.mdtechniques.md详细介绍
用户体验与动画
基于 Charles Liu 的《20 Principles of UX & Animation》短片,并结合 Emil Kowalski 的界面动画规则、Josh Puckett 的《Interface Craft》中的动态技巧,以及更多 UX 定律。以 Disney 的 12 条动画原则(Frank Thomas 和 Ollie Johnston,《The Illusion of Life》)、Raphael Salaja 的 userinterface.wiki 和 Jon Yablonski 的《Laws of UX》为基础。
每次交互都要依次回答五个问题:它应该动起来吗?视线应该落在哪里?它应该如何移动?人们能找到它、点中它并做出选择吗?他们能完成操作吗?每次都按这个顺序逐一检查。后面的答案永远无法弥补前面的错误:不该存在的动画,即使曲线再完美,也是错的。
开始使用
- 匹配技术栈。 找出项目现有的动画实现方式(CSS transitions、Tailwind、Motion(作为
motion或framer-motion)、React Spring、WAAPI),并沿用该方式。状态变化使用 CSS transitions。如果已安装 Motion,只将它用于 CSS 无法实现的情况:元素卸载时的退出动画、布局变化,以及跟随手指的弹簧效果。不要为了 CSS transition 就能实现的效果添加库;如果已安装 Motion,也不要手动编写弹簧效果。原因:两个系统为同类变化添加动画时,在时序和中断处理方式上会逐渐产生差异。 - 列出范围内的交互。针对每项交互,记录触发方式(指针、触摸、键盘、网络、滚动)、一个人触发的频率,以及它需要传达的信息。然后,在润色之前,删掉或在评审中标记所有不服务于任务的元素和步骤(奥卡姆剃刀,问题 5)。原因:为即将删除的内容重新设计样式,是在浪费精力。
- 逐项检查下面的问题 1–5,针对每条规则逐一核对每项交互。每个小节都会说明适用场景;如果范围内没有任何操作能触发该规则,就跳过(如果没有内容横跨屏幕移动,就跳过“弧线”)。
- 为数值命名。 时长、曲线和弹簧参数应放在文件顶部或主题中的具名 token 里,并由每个动画使用。三个或更多阶段的序列应配有分镜:注释列出每个阶段的开始时间、
TIMING对象,以及每个元素各自的配置对象(分镜)。可以运行应用时,应实时调整数值(使用仅供开发的控制面板),然后将可行的值写死。原因:找不到的数值,就没人会去调整。 - 验证末尾的检查项。
references/recipes.md 中提供了模态框、抽屉、弹出框、工具提示、轻提示、进入、退出、展开器、图标切换、异步按钮、数字、滑块和轮播的精确参数方案;凡是仅靠数值不足以说明的地方,也附有代码。references/techniques.md 介绍曲线、弹簧、可中断性、弧线、数值映射、橡皮筋效果、循环、遮罩、交叉淡化、分镜、时间线和性能。两个文件开头都有各自章节的列表;请阅读规则指向的章节。
进行评审而非构建? 打开步骤 2 中的交互列表报告。然后针对每项发现写一行表格:严重程度、原则、位置(文件和行号,或帧)、之前、之后。严重程度分为 错误(结果错误、输入丢失、步骤受阻或跳过)、阻碍(比必要情况更慢或更令人困惑)或 润色。先按严重程度排序,再按五个问题排序;“验证”中发现的问题归入其所服务的问题(减少动画和动画属性归入问题 3)。原则未涵盖的 Bug 放在表格之后的列表中。
1. 它应该动起来吗?
使用频率
How often will one person trigger this?
├── Many times a session, or from the keyboard (shortcuts, arrow keys, ⌘K, tabs, focus moving)
│ → no transition. The new state shows on the next frame.
├── A few times a session (menu, popover, modal, drawer, toast, saving a form)
│ → animate, within the timing table in question 3.
└── Rarely: a milestone the person won't repeat this session (onboarding, a first success, finishing a big task)
→ may be choreographed: each move up to 500ms, the whole sequence up to about 2.5s.
Then: does the motion answer a question? (Where did this come from? Did my action register? What changed? What caused it?)
├── No → cut it.
└── Yes → keep it, and make the motion say the answer (question 3, "Make it mean something").
按下和悬停反馈不受此限:无论频率如何,它都保持 100–150ms 的过渡,因为反馈本身就是响应。当悬停和键盘高亮共用一种样式时,只在悬停规则上设置过渡,这样按键反馈才能即时生效。营销页面可以采用更长、更炫的动效;产品 UI 则应遵守时长表。原因:当人们看到某个动画达到第 100 次时,它就不再提供信息,而是开始造成延迟。键盘用户的操作速度快于任何过渡动画,因此缓慢滑入的高亮会跟不上按键。
页面首次绘制时已经处于静止状态的内容,不应播放入场动画:动效只用于响应人们之后触发的变化。在 Motion 中,围绕切换开关、标签页和图标切换使用 AnimatePresence 时,设置 initial={false};但如果首次入场动画本身就是目的,则不要这样设置。
Doherty 阈值
当界面能在 400ms 内做出响应时,人们就能保持专注状态。
- 按下状态应在输入发生的同一帧显示,绝不能等到网络往返后才显示。
- 对几乎不会失败的操作采用乐观更新:点赞、收藏、切换、重新排序、标记为已读、发送消息、移除项目。变化应在同一帧开始(立即开始的动效也算),如果服务端拒绝,则回滚并显示消息。
- 在多步骤工作流中,如果某一步通过了浏览器端验证,就应在后台保存,让人们继续操作;如果保存失败,则显示带有“重试”按钮的 toast,并保留他们当前的位置。例外情况是下一步需要依赖当前步骤的结果(例如,运费和税费取决于已保存的地址):这类步骤应像最终提交一样等待。
- 其他所有操作,包括每次最终提交(付款、下订单):立即禁用触发控件,防止重复提交;只有在 300ms 后才显示“正在保存…”或加载动画,并且一旦显示,至少保持 500ms(异步按钮)。原因:快速响应不会闪现加载动画,缓慢响应也不会让动画闪烁。
- 超过 1s 时,如果结果会出现在某处(列表、页面),就在对应位置显示与内容尺寸相符的骨架屏。如果结果不会显示在其他位置,触发控件的待处理状态就足够了。
- 读取数据时,先显示用户上次看到的内容(来自本地缓存),再与最新数据同步。不要先渲染默认状态,等请求完成后再切换,例如书签图标从空心跳变为实心。
- 对于需要数秒且无法跳过的工作(首次导入、处理文件),应在后台运行,或放在值得观看的内容后面,例如展示产品功能的引导步骤。绝不要只显示一个空转的加载动画。原因:等待时阅读内容,会比盯着加载动画感觉更短。
2. 视线应该落在哪里?
- 分阶段呈现。 一次只呈现一个主要动作:用户触碰的元素,或回应用户的元素。其他所有元素都保持静止或淡出(展开卡片后方加上 40–50% 的黑色遮罩,同级元素的不透明度设为 0.5)。一个操作触发多个动作时,要依次衔接,每个动作都在前一个动作结束后开始(图片到达购物车后,徽章再弹动);绝不让它们同时进行。原因:视线会追随一个目标;两个动作同时进行会分散注意力,两个都无法充分呈现。
- 交错进入。 只用于自行出现的内容(加载中的列表、页面各个分区)。如果内容随已经在移动的容器一起进入(抽屉中的行、模态框的主体),就让它随容器一起完整进入。相同元素的副本(列表行、卡片、菜单项)间隔 30–50ms 进入,但只让前 6 个交错进入;其余元素与第 6 个同时进入。一个分区中的不同部分(标题、正文、操作项)间隔 80–100ms 进入。绝不要让整个分区作为一个整体移动。原因:细小的时间差看起来像一个有先后顺序的动作;整体移动看起来像滑动;在移动的容器中交错进入则会与容器本身争夺注意力。
- 冯·雷斯托夫效应。 每个视图中只让一个元素打破周围的模式:用强调色填充的主要操作。绝不要同时出现两个。要搭配颜色、大小、字重或位置,因为仅靠颜色无法照顾到看不见颜色的人。动效是最强烈的差异,因此任何移动的元素都会被注意到:把它留给最重要的元素。原因:人们会注意并记住与众不同的那个元素,而两个“不同”的元素会相互抵消。
- 序列位置效应。 人们对序列中第一个和最后一个项目的记忆最好。把最重要的项目放在两端(第一个和最后一个标签页、菜单顶部),把最少使用的项目放在中间,并让每个屏幕一开始就说明它的用途。原因:注意力在中间会下降。
- 次要动作。 只能叠加在主要动作之上,绝不能单独出现。它与主要动作同时开始,或最多晚 100ms 开始,在主要动作结束后 150ms 内结束,移动幅度最多为主要动作的三分之一。例如:勾选标记绘制出来后,圆环从 1 缩放到 1.4,同时淡出。只适用于问题 1 中的“很少”分支。原因:它为主要动作提供辅助;一旦它的幅度或持续时间与主要动作相当,就会与主要动作争夺注意力。
- 夸张。 只用于必须引起注意的时刻:提交时的错误、失败的尝试(密码错误、银行卡被拒),以及首次成功。错误抖动会让出错的字段在 360ms 内按 translateX 0、−10、10、−6、6、−3、0px 的顺序移动,并将焦点移到该字段上,在旁边显示提示。失焦时绝不使用,也绝不用于打开菜单或切换标签页等中性过渡。原因:夸张就像音量旋钮;如果一切都很响亮,就没有什么能引起注意。
3. 应该如何动效?
时长
| 项目 | 时长 |
|---|---|
| 按下、悬停、颜色 | 100–150ms |
| 工具提示 | 125ms |
| 下拉菜单、弹出层 | 150–250ms |
| 模态框、抽屉、面板、轻提示 | 200–300ms |
| 原地移动或调整大小(进度填充、行收拢填补间隙、重新排序、展开器) | 200–300ms |
| 页面转场、共享元素移动、翻转、飞到新位置的元素 | 300–400ms |
| 少见的精心编排时刻 | 每次移动最多 500ms |
菜单、弹出层、模态框、抽屉和轻提示的时长不超过 300ms;只有空间移动需要更长时间。在给定范围内,元素越大、移动距离越远,时长就越接近上限。如果感觉太慢,先缩短时长,不要先改曲线。原因:时长决定动作的感觉;同一个动作在 500ms 时显得迟缓,在 150ms 时则显得即时。
退出动画的时长比进入动画短 20–30%,而且更轻柔:使用淡出,最多辅以轻微移动,可以向元素即将前往的位置轻推 8–16px,或缩回进入时的大小,但绝不能缩回到完整高度。例外情况是目标位置本身就是重点(抽屉或面板关闭、卡片被滑走):元素应沿来路退出。绝不要立即使用 display: none,否则会失去元素离开的方向。为使其能够播放退出动画而保持挂载的元素,在关闭时必须不可交互(pointer-events: none 和 inert 属性),避免捕获点击或焦点。原因:元素离开时,注意力早已转移。
缓入与缓出
Follows a finger, or can be thrown or redirected mid-gesture (drag, swipe, sheet, slider, carousel)?
→ spring: Motion { type: "spring", duration: 0.5, bounce: 0 }.
Bounce 0.1–0.3 only for physical, playful objects; never more.
Appearing (popover, menu, modal, toast, page)? → ease-out: cubic-bezier(0.32, 0.72, 0, 1)
Leaving? → ease-in: cubic-bezier(0.55, 0, 1, 0.45), on the shorter exit duration
Moving or resizing while on screen? → ease-in-out: cubic-bezier(0.77, 0, 0.175, 1)
Hover or color? → ease, 150ms
Constant rate, where the motion is time itself (spinner, marquee, countdown, hold-to-confirm ring)? → linear
- 进入动画绝不使用缓入:缓慢的起步会让人觉得有延迟。
- 对任何移动或缩放的元素,绝不直接使用
ease-out或ease-in-out关键字:它们太弱,无法体现动作的意图。使用上面的曲线;曲线中提供了更柔和或更强烈的选项。 - 绝不使用
transition: all。逐一指定每个属性。 - 任何可切换或可中断的动画都使用 CSS 过渡或弹簧效果,这样动画会从元素当前所在位置反向变化(可中断性)。关键帧仅用于不可中断的一次性动画:首次绘制时的进入动画、加载器、庆祝动画。
原因:缓出在开始时移动最多,因此响应感觉即时;缓入在结束时淡出影响;真实物体会加速和减速,因此线性曲线只适用于与时间相关的事物。
起点
弹出层、下拉菜单和工具提示应从其触发器缩放,而不是从中心缩放:在 Radix 中使用 transform-origin: var(--radix-popper-transform-origin),在 Base UI 中使用 var(--transform-origin),或手动设置为触发器所在的一侧。任何缩放进入的元素都应从 scale(0.95)(小元素为 0.97)和 opacity: 0 开始,绝不能从 scale(0) 开始。配对元素(模态框与遮罩层、抽屉与背景、工具提示与箭头)应使用相同的时长和曲线。原因:事物应从孕育它们的源头生长,真实物体总有形状,而同一物体的两个部分使用不同的计时会显得错乱。
弧线
当元素在两个点之间移动,且两个轴上的位移都超过 40px 时(产品图片飞向购物车、卡片被抛出、浮动按钮移至角落),应让它沿曲线移动,使曲线偏离直线 10–20% 的距离(代码)。为体现纵深,在弧线顶点将其放大至不超过 1.3,并同步淡入更大的阴影,使其看起来像是从页面上升起,而非在页面上滑过。单轴移动(抽屉、列表重新排序)应保持直线。原因:被抛出和下落的物体会沿弧线移动,而直线对角线会显得机械。
预备动作
- 上发条属于问题 1 中的“很少”分支:在启动或翻转之前,当某人触发某个动作时(首次成功时撒彩纸、新手引导结束时发送卡片、卡片翻面),先沿相反方向移动行程的 5–10%,或缩放至 0.95,持续 80–120ms,然后再执行动作。不要在任何人们经常执行的操作前加入预备动作,因为这会增加延迟。
- 日常使用的形式是按压。按钮、图标按钮和标签在
:active时缩放至 0.96(绝不低于 0.95),并使用 150ms 的 ease-out 过渡,这样中途松开时会平滑恢复。宽度超过 200px 的表面(卡片、行)在按压和悬停时改变背景,而不是缩放,因为宽阔表面缩放 4% 会产生明显跳动。密集、高频的控件(工具栏、编辑器界面)跳过缩放,因此应为按钮组件提供禁用选项。如果元素已有 transform,则将缩放放在包装元素上。原因:它能在结果出现前确认按压操作。 - 仅在真实指针可悬停时运行悬停动画:将其包在
@media (hover: hover) and (pointer: fine)中(Tailwind v4 的hover:变体已检查hover: hover)。悬停动画如果将元素移出光标下方,会造成闪烁,因此应为子元素设置动画,并保持父元素的悬停区域不动。原因:触摸屏会在轻触时触发悬停。
后续动作
容器停止后,内部的非文本部分稍后片刻再稳定下来:提示框的图标、面板的拉手、被拖动卡片的图像。为它们设置比父元素更柔和的弹簧(阻尼比 0.7–0.85),或延迟 20–40ms 启动,并将过冲控制在行程的 4% 以内。文本绝不延迟或晃动,因此纯文本面板会整体到位;问题 1 中“很多次”分支的任何内容都不应有后续动作。在罕见时刻,主视觉对象最终稳定时可以回弹最长 1s,因为此时用户已经知道发生了什么。原因:所有部分在同一帧停止,会像一张僵硬的贴纸。
挤压与拉伸
仅用于具有物理行为的对象:会弹跳、着陆、被放下或接住的东西(停靠栏图标、落下的徽章、拖放操作中的放置动作、跳动的信封)。在撞击时使用,或作为跳板:跳跃前的挤压能储存能量,推动下一步动作。只挤压形状,绝不挤压其中的文本。高速移动时沿运动方向拉伸,撞击时挤压,幅度最多为 15%,并在 150–200ms 内恢复,同时保持体积:scaleX × scaleY ≈ 1(scaleY 0.87、scaleX 1.15)。绝不要用于输入框、布局容器或移动时需要读取的任何内容。原因:它能表现重量和弹性;用于刚性 UI 时则会显得损坏。
关键帧动画
定义各个状态(关闭和打开、空闲、按下和完成)或明确偏移量上的关键帧,然后让引擎在它们之间插值。仅对由实时输入驱动的运动(拖动、滚动、光标)逐帧编写代码(直接控制)。原因:关键姿态能让运动可预测,并可在一处进行调整。
立体绘制
任何翻转、倾斜或转动的元素都要有真实的深度:
.scene { perspective: 800px; } /* 600–1000px at card size; lower values exaggerate the depth */
.card { transform-style: preserve-3d; transition: transform 400ms cubic-bezier(0.77, 0, 0.175, 1); }
.face { backface-visibility: hidden; }
.back, .card.flipped { transform: rotateY(180deg); }
按压反馈应应用于 .scene,以免覆盖旋转效果。添加光照:使用覆盖层使背向的一面变暗,90° 时最多为 30% 黑色;并在物体抬起时缩小阴影。绝不要用 scaleX 伪造翻转。在 3D 轮播中,中心项周围的间距应大于其后方各项之间的间距,这样旋转的项就不会相互穿透,即使缓慢拖动时也一样。原因:没有 perspective 时,旋转会呈现为扁平的挤压效果。
让动效传达含义
动效承载着变化的含义,因此无需文字说明:
- 数字变化时,只滚动发生变化的数字位:数值变大时向上滚动,变小时向下滚动;使用
tabular-nums,确保其他内容不会移位。 - 项目朝其去向离开:删除的行收起,归档的项目滑向归档处,已发送的消息向上移。
- 以实体物件绘制的内容(卡片、信件、信封、票据)应像实体一样移动:处于视野中时绝不淡入或淡出,绝不穿过其他物体,并且在朝观察者移动之前先完全离开容器边缘。
- 已完成的内容逐渐退场:已完成的任务收起、淡出或堆叠到一旁,让剩余内容更突出。
- 提示应通过展示手势来表达,而不是用文字描述:循环播放一只手的滑动、拖动或刮擦动作,并在用户悬停或触碰的瞬间消失。
原因:符合隐喻的动效一眼就能看懂,而纯装饰性的动效则必须证明自己存在的理由。
替换
当一个元素在原位替换另一个元素时(标签变成加载指示器、复制图标变成勾号、一张卡片替换下一张),要让两者同时动效呈现;绝不要等旧元素完全离开后再显示新元素。将它们叠放在同一位置(同一个网格单元),这样周围内容就不会移位。在 Motion 中,AnimatePresence mode="popLayout" 会立即将离场元素从布局中移除,让同级元素在它退场时填补空隙。图标切换时,通过深度缩放(0.3 → 1)、不透明度和 3px 模糊,并使用无弹跳的弹簧效果(图标切换)。正在移动或改变形状的内容,在整个过程中都应保持为同一个元素;绝不要在移动中途将它替换成另一个元素。原因:按顺序切换会无端让等待时间翻倍,而在移动中途切换会造成抖动。
可中断
动效播放时绝不要阻塞输入。当用户中途改变主意,动效应从当前位置以当前速度反向运行。点击应起辅助作用:吸附到可能的值(例如,点击滑块时落在最近的 10% 位置),然后用弹簧效果到达该位置。拖动应直接响应:元素在手指下保持 1:1 跟随,不做缓动;松手后由弹簧效果接管,并带上松手时的速度。到达边界时,经过一小段死区后用递减回弹效果处理,绝不要突然停止(橡皮筋效果)。原因:让用户等待动效结束的界面,比没有动效的界面更慢;而且用户会故意拖过头来快速到达边界。
复合动效与循环
仅用于环境元素和少见的时刻(主视觉对象、闲置提示、浮动徽标):在主要动效上叠加小幅循环,让 y 轴在 ±10px 范围内漂移,缩放幅度为 1–3%,且二者相位不同。每个循环都应由正弦波驱动,而非关键帧;错开每个项目的相位,而不是延迟播放(循环动效)。循环对象移出屏幕时应暂停。绝不要让用户正在阅读或瞄准的内容循环播放。原因:简单循环叠加在简单动效上会让画面显得鲜活,而关键帧循环在转向处会露出接缝。
4. 人们能找到、点击并选择它吗?
接近原则
间距是人们首先读取的分组信号。属于同一组的元素彼此之间应比它们与其他元素之间更近:组间距至少是组内间距的两倍,标签与对应字段之间的距离应小于它与上方字段之间的距离。原因:距离会先于任何文字传达关系。
相似原则
外观相似的元素会被认为功能相似。为每种角色设定一种外观,并在各处保持一致:所有链接样式相同,所有破坏性操作采用同一种样式,同一类型的卡片结构相同。功能不同的内容应有不同的外观。动效也遵循同样的规则:行为相似的元素应使用相同的时长和曲线令牌。原因:学会一种模式,就能在各处应用;与众不同的元素则会让人觉得像是错误。
均匀连接性
共享容器或连接线比接近或相似更能体现关联。用同一个背景或边框将标签、值和操作绑定在一起,用线连接步骤条或时间线中的各个步骤,绝不要把无关项目放进同一个容器。原因:在其他方式传达信息之前,围绕内容的边框就已经在说“它们是一组”。
格式塔完形律
人们会以尽可能简单的方式理解布局。添加边框、分隔线、卡片或图标之前,先确认间距和对齐是否已经展现了结构;如果已经展现,就不要再添加。只使用完成任务所需的最少对齐线、尺寸和颜色,并采用简单、规整的形状。原因:每增加一个容器,就多了一件需要在阅读内容之前理解的东西。
菲茨定律
目标越大、越近,点击速度越快。主要操作应是最大的目标,并放在指针或拇指当前所在位置附近:例如放在提交字段旁边,或放在移动端屏幕底部。破坏性操作应远离主要操作,避免误触。破坏性操作的确认界面默认聚焦“取消”,破坏性按钮绝不能比“取消”更大,并且要明确写出动作动词(“删除地址”)。触控目标至少为 44×44px,指针目标至少为 40×40px。
点击区域
视觉元素必须保持较小时(16–24px 图标、关闭按钮、复选框、滑块滑块),应扩大可点击区域,而不是扩大视觉元素:
.icon-button { position: relative; }
.icon-button::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: max(100%, 44px); /* 40px where the only input is a mouse */
height: max(100%, 44px);
translate: -50% -50%;
}
如果两个区域会重叠,就将二者等量缩小,但不得小于 24×24px(WCAG 2.2 的最低要求);如果仍会重叠,就拉开项目间距,或将其中一个操作移到别处。可点击卡片中的图标按钮应位于卡片链接之上,并阻止点击传递到卡片链接。当整行都作为一个链接时,整行就是点击目标。原因:人们通常瞄准中心附近点击,而大于视觉元素的区域可以容纳偏差。
希克定律
选择数量越多,决策时间越长。对于需要相互比较的选项(套餐、排序方式、设置、新手引导选项),去掉很少有人选择的选项,并将每组控制在 7 个或更少,配上标题,让人们先选组,再选项目。对于人们已经知道自己想要什么的情况(命令面板、国家和字体选择器),应优先提供搜索:这是查找操作,搜索可以消除选项数量的影响。新手引导和结账流程每一步只做一个决定。只有在确有推荐时才标记一个推荐选项,绝不要预选需要付费或会共享数据的选项。
米勒定律
工作记忆只能容纳少数项目,因此,对于超过 5 个单位的阅读、比较或输入内容,应进行分组:银行卡号按卡组织分组(4-4-4-4,Amex 为 4-6-5),电话号码按所在国家或地区的格式分组,一次性验证码按 3 + 3 分组,长表单按每组最多 5 个字段分节,顶层导航控制在 7 个项目以内。固定格式的数字应在输入时自动格式化。绝不要让人们必须记住某个内容并带到下一个屏幕;要在需要时显示它。原因:一组会被视为一个项目,因此 16 位数字会变成 4 个项目。
雅各布定律
人们大部分时间都在使用其他产品,并期望你的产品以相同的方式运作。遵循惯例:左上角的 Logo 链接到首页;账户和购物车位于右上角;按 ⌘K 或 Ctrl+K 打开命令面板;按 Esc 关闭最上层界面;单字段表单按 Enter 提交;在 iOS 上从左侧边缘滑动可返回;放大镜表示搜索,齿轮表示设置;链接看起来就应该像链接。创新应体现在内容和工艺上,而不是元素的位置或手势的功能上。如果必须打破惯例,就在首次使用时说明,并确保常规操作方式仍然可用。
5. 人们能完成任务吗?
泰斯勒定律
每项任务都包含无法消除、只能转移的复杂性,因此产品应承担起来。预填你能推断的信息(根据账户或区域设置确定国家,根据邮政编码确定城市,根据账户确定姓名和电子邮箱),将每个选项的默认值设为大多数人会选的选项,永远不要询问你已经知道的信息。如果某个选项需要额外付费或会共享数据,默认选择免费的、保护隐私的选项(标准配送,关闭共享)。原因:产品没有承担的部分,每个人每次都得自己承担。
帕金森定律
任务会膨胀到填满分配给它的时间,因此要缩短时间。启用自动填充(autocomplete 属性),记住上次使用的值,将常见答案设为一键选项,并让可选字段不出现在主要流程中。提前说明较长任务需要多长时间(“大约 2 分钟”)。原因:每多一个字段,就多一个让人犹豫的机会。
奥卡姆剃刀与渐进式披露
如果两种设计都能奏效,就发布组成更少的那一种,并删除所有无助于完成任务的元素和步骤。这就是“运行它”第 2 步中的精简操作,要在任何润色之前完成。然后逐步呈现复杂内容:显示大多数人需要的选项,将其余选项放在“更多选项”或展开面板中,并且只在用户给出需要后续字段的答案后,才显示该字段。绝不要隐藏大多数访问者都需要的内容。原因:过早呈现的复杂性,会让每个人都为此付出代价。
帕累托法则
大部分使用都集中在少数几个功能上。找出这些功能(根据使用数据或产品的核心任务),为它们安排醒目的位置、默认值,并优先润色;将其余功能下移一级。原因:平均分配润色精力,大多会浪费在少有人看的地方。
波斯特尔法则
接受格式不规整的输入,并返回一种规范格式。对于用户从已知范围中选择的日期(配送日、预约日期、会议日期),使用仅包含有效日期的选择器。对于用户根据已有信息输入的内容(生日、文档中的日期、电话号码或卡号、电子邮箱),使用能够解析所有常见格式的文本字段:日期(“15 jan 2026”、“2026-01-15”、“1/15/26”,按用户的区域格式),带空格、连字符或括号的电话号码和卡号,带多余空格或大小写混杂的电子邮箱,以及末尾带空白字符的粘贴内容。像日期这样的自由格式值,应在失去焦点时解析,并以规范格式显示回来(“Thursday, 15 January 2026”)。只在失去焦点或提交时验证,绝不要在用户首次输入字段时验证;错误一旦出现,就在每次击键时重新检查,让错误在修正后立即消失。如果确实无法解析,就保留用户输入的内容,并说明可用的格式,同时给出示例。绝不要拒绝本来能够解析的输入。原因:严格要求格式,就是把工作转嫁给用户。
错误:先预防,再恢复
- 预防。 将输入限制为有效内容(仅包含有效日期的选择器、用于计数的步进器),禁用不可能的选项,并在旁边说明原因;在用户输入之前展示预期格式,而不是等他们出错后才展示。只有无法撤销的破坏性操作才需要确认,并在按钮上写明操作(“删除 3 个文件”)。
- 恢复。 可撤销的操作都应提供“撤销”,而不是要求确认:删除、归档或发送后,至少 5 秒内显示带“撤销”选项的提示。项目立即从屏幕上消失,并在撤销窗口结束时(或页面隐藏时)发出请求,这样“撤销”只需将项目恢复即可。操作失败时,保留用户输入的所有内容,并用浅白易懂的语言说明发生了什么以及如何修复,且在出错的字段或操作旁说明。
原因:人们会习惯性地点过确认对话框,所以确认无法预防错误;而撤销仍能在出错后发挥作用。
蔡格尼克效应与目标梯度
未完成的任务会萦绕在人们心头,而目标越近,人们行动越快。明确显示当前进度和剩余内容(“第 2 步,共 4 步”“还剩 3 个字段”、勾选已完成项目的清单),保存部分进度,并提供从上次停下处继续的选项。计入已经完成的工作:用户完成的步骤(创建账户)应显示为已完成;一旦用户做过某些事,进度就绝不能显示为 0%。步骤进度条从上一个值开始增长,从左边缘使用 scaleX(绝不使用 width),采用 ease-in-out 曲线,且绝不向后跳。原因:可见的剩余工作能吸引人们回来,而看起来近在咫尺的目标更容易完成。
峰终定律
人们会根据体验中最强烈的时刻和结尾来评价体验。每个流程都应以确认信息收尾,说明发生了什么以及接下来该做什么,绝不能只做简单跳转。只有在真正的重要节点才庆祝(问题 1 中的“很少”分支);常规完成只需安静地确认。与其润色普通时刻,不如先解决最糟糕的时刻(错误、漫长等待)。原因:结尾是人们会记住并告诉别人的部分。
验证
尽可能运行所有检查。在仅审查代码时,不要搭建测试框架:列出发现后,再列出无法运行的检查,以及每项检查能发现什么。
- 逐帧检查每个动画,并将速度调至 10%(DevTools → Animations,或逐帧录制)。第一帧能立即响应,最后一帧平稳结束、没有突变,并且每次只有一个元素起主导作用。
- 在中途打断每个动画。它会从当前位置反向运动。
- 快速敲击键盘,遍历每个列表并使用每个快捷键。没有任何反馈会落后于按键。
- 将网络速度降至 1s 或更慢。按键会立即注册,300ms 内不会闪现加载动画,慢速结果会在即将出现的位置显示进度。
- 点击手机上每个小目标的边缘。轻点不会触发悬停动画。
- 将杂乱输入粘贴到每个字段,并强制触发每种故障(离线、服务端错误、无效数据)。用户已输入的内容会保留,并且能够恢复。
- 以新用户身份完整走完每个流程一次。默认值已填好,进度清晰可见,最后一个屏幕会说明发生了什么。
- 减少动态效果(
prefers-reduced-motion: reduce;Motion 中的useReducedMotion()):所有动画都关闭,包括淡入淡出;所有结束状态和信息都与开启动态效果时一致。唯一例外是表示工作仍在进行的指示器(如加载动画、进度条),它们会继续移动,因为静止的加载动画看起来像卡住了。 - **切换主题。**页面上不会触发颜色过渡。
- **属性:**位移和尺寸变化只能使用
transform和opacity;颜色和stroke-dashoffset可以使用,模糊半径保持在 20px 以下。绝不要为 width、height、top、left 或 margin 添加动画:使用 FLIP(先测量,再为 transform 添加动画)折叠已移除的行;使用网格行方案打开展开器(展开器)。如果元素在移动开始或结束时偏移 1px,则为其设置will-change: transform。 - **第十次使用:**其他所有事项完成后,以今天已经看过十次的人为视角,按正常速度播放每个动画。删掉或缩短开始让人觉得缓慢的动画。
当以上要求全部满足时,吸引力便会自然呈现。你无法在最后把它添加上去,只能避免失去它。最终检查:全程使用一套具名曲线和时长;为每种状态都做好设计(空白、加载、错误、成功、离线);没有任何内容凭空出现。