Animation and Motion: When Less Is More
动画与动效:少即是多
Motion is the most overused and under-thought-about element in modern interface design. Done well, it makes a UI feel responsive, physical, and human. Done badly, it makes the same UI feel slow, distracting, or even nauseating. The line between the two is not a matter of taste; it is a set of well-understood rules about easing, duration, what to animate, and — crucially — when not to animate at all. This guide is about the small set of decisions that turn motion from a gimmick into a tool.
动效是现代界面设计里被过度使用、又被思考得最少的元素。做好了,让 UI 感觉响应迅速、有物理感、有人情味;做差了,让同一个 UI 感觉迟缓、令人分心,甚至让人恶心。两者之间的分界线不是"口味问题",而是一组被充分理解的规则:关于缓动、时长、动什么 —— 最关键的,是什么时候根本不该动。本指南要讲的,就是把动效从"花活"变成"工具"的那一小套决定。
What motion is for: four jobs, not decoration
动效的用途:四种工作,不是装饰
A useful question before adding any animation is: what job does this motion do? The honest answer, in almost every case, is one of four things.
在加入任何动画之前,有一个有用的问题:这个动效到底在做什么?几乎所有情况下,老实的答案都会落在四种之一。
Feedback is the most important. A button that depresses slightly when you press it tells you, "your click was received". A toggle that slides from off to on confirms the change. Without this feedback, the user is left guessing. The motion is the receipt.
反馈是最重要的一种。按钮在被按下时轻微下沉,就是在告诉你"点击收到了"。开关从关滑到开,是在确认状态变化。没有这种反馈,用户就只能猜。这个动效就是那张"小票"。
Transition explains spatial change. When a card expands into a full-screen view, when a sidebar slides in, when a page morphs into a detail view, the motion tells the user "this thing came from that thing". Continuity of motion is continuity of meaning. Without it, two related screens feel like two unrelated screens.
过渡解释空间上的变化。一张卡片展开成全屏视图,侧栏滑入,列表页变形成详情页 —— 动效在告诉用户"这个东西来自那个东西"。动效的连续性就是语义的连续性。没了它,两块相关界面会显得毫不相关。
Attention is the most dangerous. A pulsing notification badge, a flashing banner, a tooltip that bounces — these are all attempts to pull the eye. They work, and that is exactly the problem: they pull the eye away from whatever the user was already doing. Attention-grabbing motion is a tax on every other piece of content on the page. Use it sparingly, and only for things that genuinely deserve it.
吸引注意是最危险的一种。跳动的通知徽标、闪烁的横幅、来回弹的提示框 —— 都是想把视线拉过去。它们有效,这正是问题:它们把视线从用户原本在做的事情上拉开。吸引注意的动效是对页面上所有其他内容征收的"税"。要谨慎使用,只用于确实值得的事情。
Delight is the optional one. A subtle confetti burst when a goal is met, a spring-loaded card on first load, a custom cursor that leaves a trail — these add personality but not information. They are fine when the rest of the experience is solid; they are a problem when they substitute for one. If the only thing memorable about your interface is the animation, the rest of it is broken.
愉悦是可选的一种。达成目标时撒一小撮彩纸,首次加载时卡片回弹一下,自定义光标留下一串轨迹 —— 这些增加的是"个性"而不是"信息"。当其他体验都扎实时,它们是加分项;当它们成为替代品时,就是问题。如果你的界面唯一让人记住的是动效,那其他部分一定是有问题的。
The 12 principles, applied to UI
12 原则,在 UI 中的应用
Disney animators Ollie Johnston and Frank Thomas published The Illusion of Life: Disney Animation in 1981, codifying twelve principles that make drawn movement feel alive. They translate remarkably well to interface motion.
迪士尼动画师 Ollie Johnston 和 Frank Thomas 在 1981 年出版了《生命的幻象:迪士尼动画》,把让"画出来的运动"显得有生命力的十二条原则系统化。它们被翻译到界面动效上时,效果出奇地好。
Squash and stretch give an element weight. A button that compresses on press and springs back communicates mass. Anticipation is a small wind-up before a larger motion. A notification dot that briefly scales up before sliding out reads as deliberate. Staging means clear visual hierarchy of who is moving. Straight ahead vs pose to pose: UI almost always uses the latter — define start and end states, interpolate between them. Follow through and overlapping action mean different parts of an element arrive at rest at different times, like a swinging door with a slightly delayed handle. Slow in and slow out is the easing, which gets its own section below.
挤压与拉伸赋予元素"重量感"。一个按钮按下时压缩、回弹时拉伸,传达了"质量"。预备是大动作之前的小预摆。通知点先短暂放大再滑出,读起来才是"有意为之"。演出布局意味着清晰划分"谁在动"。逐帧 vs 关键帧:UI 几乎总是用后者 —— 定义起止状态,中间插值。跟随与重叠动作指一个元素各部分到达静止的时间不同,像一扇带把手的门,门扇和把手先后停下。缓入与缓出就是缓动,下面单独讲。
The remaining six principles are arcs (motion rarely follows a perfectly straight line — a slight curve feels more natural), secondary action (a small accompanying motion that supports the main one — a button that depresses and slightly changes color), timing (duration as meaning — fast reads as urgent, slow reads as deliberate), exaggeration (a slightly larger version of the real motion reads better than a literal one), solid drawing (in UI: respect the spatial system, do not violate the grid), and appeal (the design quality of the motion itself — fluid, confident, characteristic). None of these are decorative. Each is a tool for a specific perceptual job.
剩下六条是 弧线(运动很少是直线 —— 略带弧线更自然)、次要动作(小动作衬托主动作 —— 按钮按下时既下沉又微微变色)、节奏(时长即意义 —— 快显得紧迫,慢显得从容)、夸张(比"真实动作"略大的版本反而更易读)、扎实的描绘(在 UI 里即:尊重空间系统,不破坏网格)、吸引力(动效本身的设计质感 —— 流畅、自信、有特征)。这些都不是装饰,每一条都在为某种具体的感知任务服务。
Easing and duration: why linear feels robotic
缓动与时长:为什么线性动画像机器人
A linear animation moves at a constant speed from start to end. In the real world, almost nothing moves this way — a thrown ball decelerates under gravity, a swinging pendulum slows as it rises, a person leaning forward accelerates first, then decelerates as they reach their destination. Linear motion reads as mechanical, synthetic, "off".
线性动画以恒定速度从起点到终点。真实世界里,几乎没有东西是这样运动的 —— 抛出的球在重力下减速,单摆上升时减慢,人前倾时先加速,到达位置时再减速。线性运动读起来是机械的、合成感强的、"不对劲"。
CSS offers four named easing functions, all variations of the cubic-bezier curve:
CSS 提供了四个命名的缓动函数,都是三次贝塞尔曲线的变体:
- ease — the default. Slight slow start, fast middle, slow end. A good general-purpose choice.
- ease —— 默认值。轻微缓启动、中段快、缓收尾。通用场景下的好选择。
- ease-in — slow start, fast end. Use for elements leaving the screen (they "depart" naturally).
- ease-in —— 缓启动、快收尾。用于离场元素("离开"得很自然)。
- ease-out — fast start, slow end. Use for elements entering the screen (they "arrive" naturally).
- ease-out —— 快启动、缓收尾。用于入场元素("到达"得很自然)。
- ease-in-out — slow at both ends, fast in the middle. Use for elements that stay on screen and move within it.
- ease-in-out —— 两端都缓、中间快。用于屏幕内"原地移动"的元素。
For more character, custom cubic-beziers give finer control. The Material Design system publishes a set of "standard" and "emphasized" easings; the SwiftUI defaults do the same. A typical "snappy" UI curve is cubic-bezier(0.2, 0.0, 0, 1); a typical "elegant" curve is cubic-bezier(0.4, 0.0, 0.2, 1). The difference is small but felt.
想要更有个性,可以用自定义的 cubic-bezier 取得更细的控制。Material Design 公开了一组"标准"与"强调"缓动;SwiftUI 的默认值也是同样的思路。一个典型的"干脆利落"曲线是 cubic-bezier(0.2, 0.0, 0, 1);一个典型的"优雅"曲线是 cubic-bezier(0.4, 0.0, 0.2, 1)。差别很小,但能被感受到。
Duration matters as much as easing. A useful rule of thumb: 200 ms for micro-interactions (button press, toggle switch, hover state), 300 ms for small transitions (tooltip appearing, tab switching, dropdown opening), 500 ms for large transitions (page changes, modal opening, drawer sliding in). Anything longer than 500 ms feels slow for UI; anything shorter than 100 ms feels abrupt, almost invisible.
时长与缓动同等重要。一条实用的经验法则:200 毫秒用于微交互(按钮按下、开关切换、悬停态),300 毫秒用于小过渡(提示框出现、Tab 切换、下拉打开),500 毫秒用于大过渡(页面切换、模态打开、抽屉滑入)。超过 500 毫秒在 UI 中会感觉迟缓;短于 100 毫秒则显得突兀、几乎不可见。
The free animations: transform and opacity
自由的动画:transform 与 opacity
Of all the CSS properties you could animate, only two are essentially free: transform and opacity. They are the only properties that the browser can hand off to the GPU and animate on the compositor thread, without triggering layout or paint. Everything else — animating width, height, top, left, margin, background-color — runs on the main thread and is subject to jank when the page is busy with other work.
在所有可以动效化的 CSS 属性里,只有两个本质上是"免费"的:transform 和 opacity。它们是浏览器唯一能交给 GPU、在合成线程上动效化、不触发布局或重绘的属性。其他一切 —— 动 width、height、top、left、margin、background-color —— 都在主线程上运行,页面有其他任务时就会卡顿。
The practical implication: animate position via transform: translateX(...) rather than left: 0 → 100px. Animate size via transform: scale(...) rather than width: 100px → 200px. Animate visibility via opacity: 0 → 1 rather than visibility: hidden → visible. These three substitutions alone can make the difference between a 60-fps animation and a 30-fps one on a mid-range phone.
实际意义:用 transform: translateX(...) 来动位置,而不是 left: 0 → 100px;用 transform: scale(...) 来动尺寸,而不是 width: 100px → 200px;用 opacity: 0 → 1 来动可见性,而不是 visibility: hidden → visible。仅这三个替换,就足以让动画在中端手机上从 30 帧跳到 60 帧。
There is one more piece of magic: the will-change CSS property. Setting will-change: transform on an element that is about to be animated tells the browser "promote this to its own layer now, so when the animation starts, the layer is already there". Used as a permanent declaration it backfires (every element with will-change is treated as a separate layer, which costs memory and paint time), so apply it just before the animation and remove it when finished. Or use it sparingly on known-animated elements.
还有一块"魔法":will-change 这个 CSS 属性。在即将被动画的元素上写 will-change: transform,等于告诉浏览器:"现在就把这一层提升为独立合成层,等动画开始时这一层已经在那里了"。如果长期挂着反而适得其反 —— 每个带 will-change 的元素都会被当作独立层,消耗显存和重绘时间。所以应该在动画即将开始时加,结束后移除;或者只在"已知会持续动效"的元素上长期使用。
CSS vs JavaScript: when to reach for a library
CSS 还是 JavaScript:什么时候需要库
For the vast majority of UI motion, CSS transitions and keyframe animations are enough. They are declarative, fast, and integrated with the browser's compositor. They can be triggered by hover, focus, class changes, or media queries. They pause, resume, and cancel with the element's lifecycle. For micro-interactions, state changes, and simple entrances, stop at CSS.
对绝大多数 UI 动效而言,CSS 的 transition 和 keyframe 动画已经足够。它们是声明式的、快速的,并且和浏览器的合成器深度集成。可由 hover、focus、class 变化或媒体查询触发,会随着元素的生命周期暂停、恢复、取消。微交互、状态变化、简单的入场,做完 CSS 就够了。
JavaScript animation libraries become useful when you need physics-based motion, complex sequencing, scroll-driven animation, gesture-driven animation, or animation along a path. GreenSock (GSAP) is the de facto standard for complex UI work — it has a small core, a generous free tier, and a plugin ecosystem (ScrollTrigger for scroll-driven motion, MorphSVG for shape morphing, SplitText for character animation). Motion One (the spiritual successor to Popmotion, by the same team as Framer Motion) is a smaller, modern alternative built on the Web Animations API. Framer Motion is the choice if you are building a React app and want declarative variants. Lottie plays pre-designed After Effects animations in JSON format — useful for illustrations and onboarding, less so for core UI.
当你需要基于物理的运动、复杂时序、滚动驱动、手势驱动,或沿路径运动时,JavaScript 动画库就派上用场了。GreenSock(GSAP)是复杂 UI 的事实标准 —— 核心小、免费版慷慨、插件生态完整(ScrollTrigger 处理滚动驱动、MorphSVG 处理形状变形、SplitText 处理字符级动画)。Motion One(Framer Motion 同一团队的 Popmotion 精神续作)是一个更小、更现代、基于 Web Animations API 的替代品。如果你用 React,Framer Motion是声明式 variants 的首选。Lottie用于播放 After Effects 预设计的 JSON 动画 —— 在插画和引导流程里很有用,核心 UI 场景就少了。
A common mistake is using a library for what CSS could do. If you find yourself importing GSAP for a single 200 ms fade, you are paying the bundle-size and learning-curve tax for no return. The libraries earn their keep when the animation is complex, the choreography is non-trivial, or the interaction model cannot be expressed in CSS at all.
一个常见错误是:明明 CSS 能做的事,非要拉一个库。如果你为一个 200 毫秒的淡入引入 GSAP,你只是在为没有回报的"包大小"和"学习成本"交税。这些库真正的价值在于:动画很复杂、编排不小、或者交互模型在 CSS 里根本表达不出来。
Respecting prefers-reduced-motion
尊重 prefers-reduced-motion
Some users get nauseated, dizzy, or disoriented by motion. People with vestibular disorders, migraines, post-concussion symptoms, or simply a sensitivity to movement can find even a small parallax effect or a long animated transition painful. Operating systems expose a setting — "Reduce motion" on macOS and iOS, "Remove animations" on Windows — that propagates to the browser as the prefers-reduced-motion: reduce media query.
有些用户会因为动效感到恶心、头晕或失焦。前庭功能障碍、偏头痛、脑震荡后症状或单纯对运动敏感的人,甚至会觉得一点点视差或一个稍长的过渡动画都难以忍受。操作系统提供了"减少动效"的设置(macOS/iOS 上的"减少动态效果"、Windows 上的"删除动画"),它会通过 prefers-reduced-motion: reduce 媒体查询传递到浏览器。
The respectful pattern is to wrap any motion in a check:
有礼貌的写法是把所有动效都包在一个判断里:
@media (prefers-reduced-motion: no-preference) { /* the full animation */ }
@media (prefers-reduced-motion: no-preference) { /* 完整动画 */ }
For users who have opted out, the UI simply appears in its final state — no fade, no slide, no parallax. Information is still communicated; only the motion is removed. The functionality is identical; the comfort is not.
对已经选择关闭动效的用户,UI 直接以最终状态出现 —— 没有淡入、没有滑入、没有视差。信息仍然被传达,只是去掉了动效。功能完全相同,但舒适度不再被牺牲。
In JavaScript, the equivalent is window.matchMedia('(prefers-reduced-motion: reduce)').matches. Animation libraries expose this as a top-level option — gsap.matchMedia() and Framer Motion's useReducedMotion hook are two examples. Ignoring this setting is not just an oversight; it is an accessibility failure on par with low color contrast.
在 JavaScript 里,等价物是 window.matchMedia('(prefers-reduced-motion: reduce)').matches。动画库通常把它暴露为顶层选项 —— gsap.matchMedia() 和 Framer Motion 的 useReducedMotion hook 就是两个例子。忽略这个设置不只是"疏忽",它和"颜色对比度低"一样,是一种无障碍失败。
Common mistakes
常见错误
A short list of patterns to avoid: animation that blocks input (an element animating into place and being non-interactive until the animation finishes), animation that is too long (anything over 600 ms for state transitions feels broken), animation that loops without purpose (a continuously pulsing logo is a battery drain, not a feature), animation that has no easing (linear motion is the universal tell of "this was not designed, it was default"), animation that competes with content (motion on a hero image during the first 200 ms when the user is still reading it), and animation that ignores reduced-motion preferences. Each of these is a small failure of design discipline; together they explain why a surprising number of "modern" interfaces feel hostile.
一个简短的"避坑清单":阻塞输入的动效(元素动到位的过程中不接受交互,直到结束才能点)、太长的动效(任何状态过渡超过 600 毫秒都会显得坏掉了)、无意义地循环的动效(持续跳动的 logo 是耗电,不是功能)、没有缓动的动效(线性运动是"这不是设计出来的,是默认的"的通用标志)、与内容抢戏的动效(用户在读 hero 图的前 200 毫秒里,hero 图本身在动)、忽略减少动效偏好的动效。每一个都是一点点设计纪律的失败;它们合在一起,解释了为什么相当多"现代"界面会让人感到敌意。
Try the tools
试试这些工具
Motion design starts with the values that drive the animation: durations, easings, color stops. Preview color transitions in a UI with the color picker, and time keyframe sequences with the timestamp converter. All processing happens locally in your browser; nothing is uploaded.
动效设计从驱动动画的那些"值"开始:时长、缓动、颜色停点。用颜色选择器预览 UI 中的颜色过渡,用时间戳转换对齐关键帧序列。所有处理都在你的浏览器本地完成,不上传任何内容。