前端做了一轮动效设计,反复遇到同一个问题:这个动效是在帮助用户理解界面,还是只在展示"我会做动效"。区分的标准不是感觉,是三条具体的判据。
动效的三条判据
判据一:是否传达了状态变化
用户操作后,系统进入新的状态。动效的作用是让这个状态变化可感知。
加载中。一个网络请求发出去了,但数据还没回来,用户看不到任何反馈会以为卡了。打字指示(三个点循环脉冲、错开时间)就是这类必做的动效。用户不需要理解"这是一个脉冲",只需要看到"系统在做什么"。
成功和失败。表单提交后,服务器返回 200 还是 400,用户需要知道。Toast 从屏幕边滑入、带上颜色标记(绿色还是红色)和文案,用户秒懂结果。这里动效的信息密度很高:位置(从边界滑入说明是一个通知)、方向、颜色都在传达信息。
这一类动效 该做。不做的代价是用户体验割裂——界面突然变了,用户需要花脑力去理解"发生了什么"。
判据二:是否建立了空间关系
用户在多个界面间切换,每个界面有不同的内容。动效的作用是让用户在心理上形成"我从 A 地来到了 B 地"的感觉。
页面切换。新页面从右侧滑入或从中央缩放进来,老页面退出。用户的注意力自然跟随移动,不会觉得"屏幕闪了一下";反而能通过位移方向推断页面的层级关系。
弹窗。一个模态框从中央向外弹出、带回弹感,用户知道"这个内容是浮在上面的、暂时的"。而如果弹窗突然出现,用户需要花力气解析"这是什么、它和后面的页面什么关系"。
展开与收起。列表条目逐个滑入,后出现的条目在前面条目之后,这叫 stagger 编排。用户可以跟踪每一项的出现,而不是"列表突然全填满了,我从哪里开始看"。导航栏的 active 指示器平移而不是闪烁切换,用户能看到"我从这里跳到那里"。
这一类动效也 该做。代价同样高:没有这些线索,界面就像一个无生命的状态表格,用户要主动扫描才能判断当前在哪、怎么走。
判据三:装饰性动效
一个元素 hover 时发光、卡片有光扫效果、logo 漂浮、按钮涟漪……这些都是装饰。它们不传达任何状态变化,不建立空间关系,就是"看起来更高级"。
品牌要求这些吗?可能。会提升用户体验吗?证据不足。该砍。
这里的逻辑是:每一帧动画都要用户的 GPU 和浏览器去渲染。装饰性动效的每一帧都是在"花钱不赚钱"。当装饰堆积到一定程度,就会出现性能问题。
实践中的折衷是:装饰性动效只保留在关键时刻。用户花钱了、获得了成功的反馈,这时撒纸屑。余额减少了,扣点粒子迸发。这些装饰是在庆祝或强调,而不是无处不在。
性能与可访问性的硬要求
60fps 底线
动效要稳定在 60fps,否则就是卡顿伪装成流畅。这意味着只动 transform、opacity 和 filter,不动 layout 属性。
为什么?改变 width、height、margin 或 left/top 会触发重排(reflow)。浏览器要重算文档流、重排页面,这会花很长时间,远超单帧预算。动画自动掉帧。
实例:一个列表容器用 height 从隐藏到展开的动画,看起来像"列表展开",实际上每帧都在触发重排。改成用 scaleY transform 动画,性能立刻改善。
另一个细节:长列表的逐项入场动画。当列表项数足够多时,用 stagger 从上往下依次滑入,屏幕可见范围内的项动画效果还不错,但超出视口的项同样在运行动画逻辑——整个列表的动画状态都要参与计算,对内存和渲染管线造成压力。硬要求是:大列表只对首屏可见项做动画,不可见项直接渲染到最终状态。
prefers-reduced-motion:必须尊重用户的选择
系统级的「减少动态效果」开关(macOS 辅助功能、Windows 显示设置)对应 CSS 媒体查询 prefers-reduced-motion: reduce。一部分用户打开这个设置(通常因为前庭功能障碍、晕动症或认知障碍),动效会让他们头晕或注意力分散。
硬要求:当系统选择 reduced-motion 时,所有动效必须降级。降级方案是什么?最简单且有效的方案是淡入淡出。用户看不到位移、没有高频闪烁,界面从无到有、从有到无。装饰性的粒子、纸屑、光扫一律不渲染。
实现方式是在应用根部用一个全局的 MotionConfig 接管 reducedMotion="user"。React 的 useReducedMotion() hook 会读系统偏好。检查返回值,该短路的短路,该降速的降速。
没有这一步,上线的产品会有一小部分用户会因为你的动效而无法使用你的产品。
参数选择的逻辑
时长有三个档位。
Micro(0.17 秒 / 170ms):按钮 hover 抬升、开关滑动、输入框 focus 发光。这些是每秒可能发生多次的交互,用户期望立刻看到反馈。太慢了会让交互感觉迟钝。
Standard(0.34 秒 / 340ms):消息气泡进场、列表项滑入、Toast 弹出。这是用户注意到的变化,需要一些时间让眼睛跟随,但不能太长。动效过长就开始让人觉得"卡"而不是"流畅"。
Emphasis(0.48 秒 / 480ms):页面切换的主体。新页面的进入需要最长的时间,因为整个屏幕都在变。但退出只需要 ~0.36 秒(75% 的进入时长),用户离开某个页面应该更快、不需要过度演示。
缓动曲线有两种常见类型。
OutExpo([0.16, 1, 0.3, 1]):开始快、结束慢。用在列表项进场、消息气泡滑入。因为是内容从无到有,用户的注意力已经被吸引,动画可以前置地快速到达目标位置,最后用缓冲平稳落地。感觉是"内容飘进来了"。
SoftSpring([0.34, 1.4, 0.5, 1]):轻微的超调回弹。用在弹窗进出、输入框 focus。因为是用户主动触发的强交互,回弹感可以传递"系统在听我"的反馈。不是生硬的到位,而是"弹"到位。
Spring 预设有三档:snappy(stiffness 520, damping 30)用于最快反应、bouncy(380, 22)用于入场、smooth(260, 32)用于页面转场。数字越大振荡越少,但响应越快;数字小了振荡次数多、柔软感强。
这些参数都是经过测试的。如果你随意加长动效时间想让用户"看清每一帧",代价是整个产品的交互节奏都会崩。快动效之间的间隙会积累,让用户觉得系统在思考,而不是在响应。
时长过长的代价
某些设计师认为慢动效显得"精致"。实践中这是错的。
首先,从用户体验的角度:动效的目的是传达信息,不是让用户等待。当一个必要的动效(如页面转场)变得太长,用户在反馈到来之前就开始扫视下一个操作目标,或者认为系统在卡。更长的等待就变成了对用户时间的浪费。
其次,从性能的角度:更长的动效意味着更多的帧要渲染。一个快速的动效和一个冗长的动效相比,后者不仅占用的渲染时间更多,对 GPU 和内存的压力也更大。在移动设备上、在 GPU 受限的情况下,这直接转化成帧率下降、电池耗尽、设备发热。
第三,从注意力的角度:冗长的动效会让用户的焦点分散。正在播放的动画仍在吸引视觉注意,用户无法提前扫视接下来的内容。这不是在尊重用户,而是在强制停留。
在实施 yun-claude 的动效设计时,判断的标准是:动效的时长应该足够用户感知状态变化,但不足够让用户等待。所以页面进入用 0.48s(完整展示),退出只用 0.36s(快速离开),打字指示用 0.17s(微交互要快)。这个分层是为了加速工作流,而不是为了炫技。
小结
写三条判据是为了在每个 feature 决策点问自己:
- 这个动效在传达用户需要知道的状态变化吗?
- 这个动效在建立空间或层级关系吗?
- 如果都不是,它只是装饰。
如果是前两者,动效 该做。保证 60fps、尊重 reduced-motion、控制好时长参数。
如果是装饰,该砍。或者保留到特定时刻(成功、失败、关键反馈),而不是无处不在。
动效不是越多越好,也不是越慢越高级。克制地用、精准地用,动效才能真正帮助用户理解界面。
■