motion

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Motion

动效

This skill holds how things move on every screen. It covers what animates, the curve and duration, gestures, exits, motion blur and reduced motion.
本 skill 规定各个屏幕上的元素如何运动。它涵盖哪些元素应设置动画、动画曲线与时长、手势、退出效果、动态模糊以及减少动态效果。

Durations and curves are exact

时长与曲线必须精确

Use the project's motion tokens wherever they exist. Where none exists, the durations, curves and scales here are exact. Motion the user starts that runs past 300ms is a finding in any style.
Motion blur draws with
@web-kits/motion-blur
, whose defaults are exact: a 180° shutter, Echo at one copy per 1.5px up to 16 copies and a 1.05 smear limit.
Whether an animation should exist at all belongs to
restraint
. Taking an existing animation apart belongs to
deconstruct
. Whether it holds its frame rate belongs to
frames
. Surfaces, loading, hover styles and sound belong to
defaults
.
只要项目中存在相应的动效 token,就应使用这些 token。若项目中没有,则本文给出的时长、曲线和缩放值均视为精确值。任何样式中,由用户触发且持续超过 300ms 的动效都应记为一项问题。
动态模糊由
@web-kits/motion-blur
绘制,其默认参数是精确值:180° 快门、每 1.5px 生成一个 Echo 副本且最多 16 个副本,以及 1.05 的拖影限制。
是否应该存在动画属于
restraint
。拆解现有动画属于
deconstruct
。是否维持帧率属于
frames
。界面表面、加载状态、悬停样式和声音属于
defaults
。

App chrome fades

应用框架层使用淡入淡出

Page switches, panels and loading states fade with
opacity
and do not slide or scale. That is
motion-opacity-for-chrome
. Animate only the axis that changes. Animating width when only height changes is
motion-changing-axis-only
.
页面切换、面板和加载状态应通过
opacity
淡入淡出,不应滑动或缩放。这种做法对应
motion-opacity-for-chrome
。只应对发生变化的轴设置动画。高度变化时还动画宽度属于
motion-changing-axis-only
。

Entrances do not replay

入场动画不重复播放

Switching pages does not replay entrances. Set
initial={false}
on the route-level
AnimatePresence
, and on any that wraps a state swap. That is
motion-no-replay-on-entry
. A cached view renders at once, with no fade or skeleton flash. That is
motion-no-flash-on-return
. See recipes.md.
切换页面时不应重放入场动画。在路由级
AnimatePresence
上设置
initial={false}
,并对所有包裹状态切换的
AnimatePresence
执行相同设置。这对应
motion-no-replay-on-entry
。缓存视图应立即渲染,不出现淡入或骨架屏闪烁。这对应
motion-no-flash-on-return
。参见 recipes.md。

Name the properties you animate

明确指定动画属性

A transition lists the properties it animates, usually
opacity
and
transform
.
transition: all
is
motion-name-the-properties
.
transition 应列出它所动画的属性,通常是
opacity
和
transform
。使用
transition: all
属于
motion-name-the-properties
。

Toggled state transitions, keyframes run once

切换状态使用过渡,keyframes 只播放一次

Interactive state changes use transitions or springs, which retarget when the user reverses. A keyframe animation on a toggled class is
motion-transitions-for-state
. Keep keyframes for sequences that run once.
交互式状态变化使用过渡或弹簧,当用户反向操作时,它们可以重新设定目标。在被切换的 class 上使用 keyframe animation 属于
motion-transitions-for-state
。仅将 keyframes 用于只播放一次的连续动作。

Motion starts where it comes from

动效从来源位置开始

A dropdown or popover scales from its trigger, with
transform-origin
set to that edge. A menu growing from its own centre is
motion-origin-from-trigger
. Modals stay centred, because no trigger anchors them.
下拉菜单或弹出层从触发它的元素处缩放,并将
transform-origin
设置到对应边缘。菜单从自身中心放大属于
motion-origin-from-trigger
。模态框保持居中,因为没有触发元素为其提供锚点。

Nothing grows from zero

元素不从零开始放大

An entrance starts from
scale(0.95)
or higher, together with
opacity: 0
.
scale(0)
is
motion-no-scale-from-zero
.
入场动画从
scale(0.95)
或更高比例开始,同时使用
opacity: 0
。使用
scale(0)
属于
motion-no-scale-from-zero
。

Gestures use springs

手势使用弹簧

Drag, swipe and any motion the user can reverse mid-flight use a spring. On release, pass the pointer velocity into it. A drag release on a fixed
ease-out
is
motion-spring-for-gestures
. See recipes.md.
拖动、滑动以及任何可在进行中反向的动效应使用弹簧。释放时,将指针速度传入弹簧。拖动释放时仍采用固定
ease-out
属于
motion-spring-for-gestures
。参见 recipes.md。

Flicks dismiss, edges resist

轻扫可关闭,边缘提供阻力

A swipe dismisses on distance or on speed, so past about
0.11
px per millisecond a short flick is enough. A sheet that ignores a fast flick is
motion-flick-dismisses
.
Dragging past a boundary meets growing friction rather than a wall. A drawer that stops dead at the top is
motion-friction-at-edges
. Capture the pointer once a drag starts and ignore extra touches. See recipes.md.
滑动操作可依据距离或速度触发关闭,因此当速度超过约
0.11
px/ms 时,即使短促轻扫也足以完成。侧滑面板忽略快速轻扫属于
motion-flick-dismisses
。
拖动越过边界时,应施加逐渐增大的阻力,而不是突然撞墙。抽屉在顶部戛然而止属于
motion-friction-at-edges
。拖动开始后只捕获一次指针,并忽略额外触控。参见 recipes.md。

Every gesture has a button

每种手势都有按钮替代方式

Each drag, swipe or pinch also works with a click and with the keyboard. A list that reorders only by dragging is
motion-gesture-alternative
.
每一种拖动、滑动或捏合操作,也都应能通过点击和键盘完成。只能通过拖动来重排的列表属于
motion-gesture-alternative
。

Easing fits the change

缓动应匹配变化类型

System changes such as a toast use easing, not a spring. Entrances and exits ease out, and a change of view eases in and out. Linear is for progress bars and clocks only. A modal entering on
ease-in
is
motion-ease-by-direction
.
The built-in
ease-out
keyword is too weak. Use
cubic-bezier(0.23, 1, 0.32, 1)
to enter and exit, and
cubic-bezier(0.77, 0, 0.175, 1)
for movement on screen. A bare
ease-out
is
motion-strong-ease-out
.
toast 等系统变化使用缓动,而不使用弹簧。进入和退出动效使用 ease-out,视图切换采用缓入缓出。Linear 仅用于进度条和时钟。模态框使用
ease-in
进入属于
motion-ease-by-direction
。
内置的
ease-out
关键字力度太弱。进入和退出应使用
cubic-bezier(0.23, 1, 0.32, 1)
,屏幕上的位移动画应使用
cubic-bezier(0.77, 0, 0.175, 1)
。直接使用
ease-out
属于
motion-strong-ease-out
。

Closes are faster than opens

关闭比打开更快

A close runs shorter than its open. Nothing delays a close or a hover-out. A modal that takes as long to leave as it took to arrive is
motion-close-faster
.
关闭动画的时长应短于打开动画。不得延迟关闭或悬停移出。关闭与打开耗时相同的模态框属于
motion-close-faster
。

Motion stays under 300ms

动效保持在 300ms 以内

Hover colour changes are instant, with no transition. A button fading its background over 200ms on hover is
motion-instant-hover-color
.
Press and hover movement take 120–180ms. Small state changes take 180–260ms. Nothing the user starts runs longer than 300ms, except a full panel or drawer at up to 400ms. A 500ms dropdown is
motion-under-300ms
.
Under
mode="wait"
the exit and the entrance play in series, so halve each. When motion feels slow, shorten it before changing the curve. Samples are in recipes.md.
悬停时的颜色变化应即时完成,不使用 transition。按钮在 hover 时用 200ms 淡变背景色属于
motion-instant-hover-color
。
按下与悬停移动应控制在 120–180ms。小幅状态变化应控制在 180–260ms。除完整面板或抽屉最长可达 400ms 外,任何由用户触发的动效都不得长于 300ms。时长 500ms 的下拉菜单属于
motion-under-300ms
。
在
mode="wait"
下,退出和入场会串行播放,因此应将各自的时长减半。当动效显得迟缓时,先缩短时长,再调整曲线。示例见 recipes.md。

Frequent actions do not animate

高频操作不设置动画

Typing, keyboard shortcuts, keyboard navigation and fast toggles do not animate. A command palette opened from the keyboard appears at once. A context menu appears at once and may fade out. A scale pulse on every keystroke is
motion-no-motion-on-repeat
.
输入、键盘快捷键、键盘导航和快速切换不应设置动画。通过键盘打开的命令面板应立即出现。上下文菜单也应立即出现,可以淡出。每次按键都产生缩放脉冲属于
motion-no-motion-on-repeat
。

Tooltips wait once

Tooltip 仅首次等待

The first tooltip in a group opens after a delay. Its neighbours open at once and without animation while one is showing, and every tooltip closes instantly. A toolbar where each tooltip waits the full delay is
motion-tooltip-delay
.
一组 Tooltip 中,第一个在延迟后打开。当其中一个显示时,其相邻 Tooltip 应立即且无动画地打开,并且每个 Tooltip 都应立即关闭。工具栏中每个 Tooltip 都等待完整延迟属于
motion-tooltip-delay
。

Icons cross-fade when they swap

图标切换时交叉淡化

An icon that swaps in place, such as copy to copied, cross-fades. Scale runs
0.25
to
1
, opacity
0
to
1
and blur
4px
to
0
. A hard swap is
motion-icon-crossfade
. See recipes.md.
Two icons in the morph form from
icons
morph instead, animating each path's
d
as a small state change on the movement curve. Under reduced motion they cross-fade. A cross-fade between two morph-form icons is
motion-icon-morph
. See recipes.md.
原地切换的图标,例如从“复制”变为“已复制”,应进行交叉淡化。缩放从
0.25
到
1
,透明度从
0
到
1
,模糊从
4px
到
0
。硬切换属于
motion-icon-crossfade
。参见 recipes.md。
两个 morph 形式的图标应使用
icons
morph,而不是交叉淡化;将每条路径的
d
作为小幅状态变化,沿移动曲线设置动画。在减少动态效果模式下,它们改为交叉淡化。两个 morph 形式图标之间交叉淡化属于
motion-icon-morph
。参见 recipes.md。

Blur bridges a cross-fade

用模糊衔接交叉淡化

When a cross-fade between two states still reads as two objects, add
filter: blur(2px)
while it runs. Keep blur under 20px, which gets expensive in Safari. Two labels visibly overlapping mid-swap is
motion-blur-the-crossfade
.
如果两个状态交叉淡化时仍看起来像两个对象,请在过渡期间添加
filter: blur(2px)
。模糊值应低于 20px,因为 Safari 中其代价较高。切换过程中两个标签明显重叠属于
motion-blur-the-crossfade
。

Motion blur only where the eye cannot follow

仅在眼睛无法追踪时使用动态模糊

A screen already smears what the eye tracks, so added blur turns text into grey bars while someone reads it. Blur a thrown card, a dismissed sheet, a panel snapping in or a spinner. Blur on scrolling, a carousel page, a list sliding into place or text being read is
motion-blur-untracked-only
.
Draw the streak with
useMotionBlur
in React and
motionBlur(element)
elsewhere. The package sizes it from speed, frame time and shutter, follows curves and clears it at rest. A hand-written
filter: blur()
or copy loop on a moving element is
motion-blur-package
. See recipes.md.
屏幕本身已经会对眼睛追踪的内容产生拖影;额外添加模糊会在用户阅读时把文字变成灰色条状。可以对被抛出的卡片、被关闭的侧滑面板、吸附到位的面板或 spinner 使用模糊。对滚动内容、轮播页、滑入定位的列表或正在阅读的文本使用模糊属于
motion-blur-untracked-only
。
在 React 中使用
useMotionBlur
绘制拖影,其他场景使用
motionBlur(element)
。该包会根据速度、帧时间和快门调整尺寸,沿曲线生成拖影,并在静止时清除。移动元素上手写
filter: blur()
或使用复制循环属于
motion-blur-package
。参见 recipes.md。

The blur sits on what moves

模糊应作用于运动元素

The package watches the host's own transform. A ref on a wrapper while its child animates draws nothing, and is
motion-blur-ref-on-mover
. With the
MotionBlur
component, animate the component itself and style what sits inside it.
The host's first child is the layer, and the streak is drawn into it. Backgrounds, borders and shadows go on the layer or inside it. A background on the host stays sharp under the streak and is
motion-blur-transparent-host
.
The techniques write the layer's transform, so the layer has none of its own. A tilt or scale on the layer is lost while it moves, which is
motion-blur-bare-layer
. Put it on an element inside.
该包会监听宿主元素自身的 transform。如果 ref 放在外层 wrapper 上,而实际运动的是其子元素,则不会绘制任何拖影,这属于
motion-blur-ref-on-mover
。使用
MotionBlur
组件时,应直接动画该组件本身,并为其内部元素设置样式。
宿主的第一个子元素就是图层,拖影会绘制到其中。背景、边框和阴影应放在图层上或图层内部。宿主上的背景在拖影下仍保持清晰,属于
motion-blur-transparent-host
。
这些技术会写入图层的 transform,因此图层本身不应拥有自己的 transform。对图层施加倾斜或缩放会在移动过程中丢失,属于
motion-blur-bare-layer
。应将其放到图层内部的元素上。

Echo draws the streak

Echo 用于绘制拖影

Keep the default
echo()
. It is the only technique shaped like a camera's blur, and the only one that follows rotation, scale and curves. Reach for
squeeze()
,
svg()
or
smear()
only when a
frames
trace shows the copies cost too much.
Those three blur the whole layer one amount in one direction, but a spinning element's edges move at different speeds. A spinner or orbit on anything but Echo is
motion-blur-echo-for-turns
. A smear limit past 1.05 on anything with text is
motion-blur-smear-limit
.
squeeze()
and
svg()
turn the layer and its child in opposite directions, so the layer wraps exactly one element. A layer with two children throws, and is
motion-blur-one-child
.
保留默认的
echo()
。它是唯一具有相机模糊形态的技术,也是唯一能跟随旋转、缩放和曲线变化的技术。只有当
frames
trace 显示副本开销过高时,才应改用
squeeze()
、
svg()
或
smear()
。
这三种技术会以单一方向、按同一模糊量模糊整个图层,但旋转元素的边缘移动速度各不相同。spinner 或轨道动画使用 Echo 之外的技术属于
motion-blur-echo-for-turns
。对带文字的内容将 smear limit 设置为超过 1.05 属于
motion-blur-smear-limit
。
squeeze()
和
svg()
会朝相反方向旋转图层及其子元素,因此图层必须恰好包裹一个元素。包含两个子元素的图层会出问题,属于
motion-blur-one-child
。

Curves pass a path

沿曲线传递路径

For a position set by hand, such as a physics loop, call
blur.render(previous, current)
once a frame. When the motion bends, pass a path instead so the copies sit along the curve. Two transforms on an orbit is
motion-blur-follow-path
. See recipes.md.
A demo played at a quarter of its speed multiplies the shutter by four, so
shutter: 720
keeps the full-speed streak. A slow-motion control left at 180° is
motion-blur-slowdown-shutter
.
对于手动设置的位置,例如物理循环,每帧调用一次
blur.render(previous, current)
。当运动路径弯曲时,应传入路径,让各副本沿曲线排列。在轨道动画上同时使用两个 transform 属于
motion-blur-follow-path
。参见 recipes.md。
以四分之一速度播放的演示会将快门乘以 4,因此
shutter: 720
可保持全速时的拖影效果。慢动作控制仍使用 180° 属于
motion-blur-slowdown-shutter
。

Custom techniques undo everything

自定义技术必须撤销所有修改

A custom technique returns a painter whose
clear
removes every style
paint
set, and whose
destroy
removes anything it added to the page. A technique built inside a render also sets
key
, so an equal one does not rebuild the painter. A filter left on a still element, or an inline technique with no
key
, is
motion-blur-custom-technique
. See recipes.md.
自定义技术返回一个 painter;其
clear
会移除
paint
设置的每一种样式,
destroy
会移除添加到页面的任何内容。在 render 内构建的技术还应设置
key
,这样相同的实现不会重建 painter。静止元素上残留 filter,或没有
key
的内联技术,属于
motion-blur-custom-technique
。参见 recipes.md。

Presses scale lightly

按下时仅轻微缩放

A pressed control scales to
0.98
, and squash or stretch stays within
0.95–1.05
.
whileTap={{ scale: 0.8 }}
is
motion-press-scale
. List items stagger by less than 50ms each.
staggerChildren: 0.15
on a list is
motion-short-stagger
. A staged entrance that plays once, such as a title, text and actions, staggers by 100ms. Routine interactions never stagger, and a stagger never blocks input.
控件按下时缩放到
0.98
,挤压或拉伸保持在 0.95–1.05。
whileTap={{ scale: 0.8 }}
属于
motion-press-scale
。列表项的 stagger 每个间隔应小于 50ms。列表中使用
staggerChildren: 0.15
属于
motion-short-stagger
。只播放一次的分阶段入场,例如标题、文字和操作项,可以按 100ms 错开。常规交互绝不使用 stagger,且 stagger 不得阻塞输入。

Exits need a presence boundary

退出动画需要 Presence 边界

A conditional element with an
exit
sits inside
AnimatePresence
, keyed by a stable id, and exits to its
initial
values. An exit that never plays because the wrapper is missing is
motion-exit-in-presence
. See recipes.md.
A dialog from a component library stays mounted and toggles its
open
prop. A dialog rendered behind
open &&
, which loses its close animation, is
motion-dialogs-stay-mounted
.
带
exit
的条件渲染元素必须位于
AnimatePresence
内部,使用稳定的 id 作为 key,并退出至其
initial
值。由于缺少 wrapper 而始终无法播放的退出动画,属于
motion-exit-in-presence
。参见 recipes.md。
组件库中的对话框保持挂载,并切换其
open
prop。通过
open &&
渲染、因而失去关闭动画的对话框,属于
motion-dialogs-stay-mounted
。

Size follows content from a second element

通过第二个元素让尺寸跟随内容

To animate a container's width or height, measure an inner element with a
ResizeObserver
and animate the outer one. Measuring and animating the same node is
motion-measure-inner
. See recipes.md.
While a panel collapses, its content keeps its open width, so nothing reflows mid-animation. Sidebar content set to
w-full
as the sidebar closes is
motion-hold-content-size
.
要动画容器的宽度或高度,使用
ResizeObserver
测量内部元素,再动画外层元素。对同一节点执行测量和动画属于
motion-measure-inner
。参见 recipes.md。
面板折叠时,其内容保持打开时的宽度,因此动画过程中不会发生重排。侧边栏关闭时其内容仍设置为
w-full
,属于
motion-hold-content-size
。

Reduced motion removes movement

减少动态效果会移除位移动画

When
prefers-reduced-motion: reduce
matches, slides, scales and blurs become an opacity cross-fade. A sliding drawer that ignores the setting is
motion-reduced-motion
.
Motion blur drops its streak under the setting while
reducedMotion
stays at
"user"
.
reducedMotion: "never"
is
motion-blur-reduced-motion
.
当
prefers-reduced-motion: reduce
匹配时,滑动、缩放和模糊应改为 opacity 交叉淡化。忽略该设置而滑动的抽屉属于
motion-reduced-motion
。
在该设置下,动态模糊应移除拖影,同时
reducedMotion
保持为
"user"
。设置
reducedMotion: "never"
属于
motion-blur-reduced-motion
。

The pair

成对呈现

Every check is a pair. The broken line sits beside the line that replaces it.
RuleBrokenReplacement
motion-opacity-for-chrome
initial={{ x: 20, scale: 0.95 }}
on a page
initial={{ opacity: 0 }}
motion-changing-axis-only
animate={{ width, height }}
when only height changes
animate={{ height }}
motion-no-replay-on-entry
<AnimatePresence>
around routes, entrances replay
<AnimatePresence initial={false}>
motion-no-flash-on-return
A skeleton flash on a cached viewThe cached view rendered at once
motion-name-the-properties
transition: all 200ms
transition: opacity 200ms, transform 200ms
motion-transitions-for-state
.open { animation: slide-in 200ms }
transition: transform 200ms
on the toggled property
motion-origin-from-trigger
A dropdown scaling from
center
transform-origin: top left
under a left-aligned trigger
motion-no-scale-from-zero
initial={{ scale: 0 }}
on a popover
initial={{ scale: 0.95, opacity: 0 }}
motion-spring-for-gestures
A drag release on
duration: 0.3, ease: "easeOut"
type: "spring"
with
velocity: info.velocity.x
motion-flick-dismisses
Dismiss only past a fixed distanceDismiss past the distance or above
0.11
px per ms
motion-friction-at-edges
A drawer that stops dead at its topMovement damped the further it is dragged
motion-gesture-alternative
Rows that reorder only by drag
Move Up
and
Move Down
in the row menu
motion-ease-by-direction
A modal entering on
ease-in
Ease-out in and out, the exit shorter
motion-strong-ease-out
transition: transform 200ms ease-out
transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1)
motion-close-faster
A modal at
200ms
in and
200ms
out
200ms
in,
150ms
out
motion-instant-hover-color
transition: background-color 200ms
on a button
The hover colour applied at once
motion-under-300ms
transition={{ duration: 0.5 }}
on a dropdown
duration: 0.2
motion-no-motion-on-repeat
animate={{ scale: [1, 1.02, 1] }}
on each keystroke
No motion on the input
motion-tooltip-delay
Each toolbar tooltip waiting the full delayThe first waits, its neighbours open at once
motion-icon-crossfade
{copied ? <CheckIcon /> : <CopyIcon />}
Both icons cross-fading with scale and blur
motion-icon-morph
Two morph-form icons cross-fadingEach
motion.path
animating
d
to its partner
motion-blur-the-crossfade
Two button labels overlapping mid-swap
filter: blur(2px)
while the swap runs
motion-blur-untracked-only
Blur on a carousel page swipeNo blur, or blur only on the flung card
motion-blur-package
filter: blur(8px)
while a card flies
useMotionBlur()
on the card
motion-blur-ref-on-mover
ref={blur.ref}
on a wrapper around the
motion.div
ref={blur.ref}
on the
motion.div
motion-blur-transparent-host
bg-white rounded-xl
on the host
The same classes on its first child
motion-blur-bare-layer
rotate-3
on the layer
rotate-3
on an element inside the layer
motion-blur-echo-for-turns
technique: squeeze()
on a spinner
technique: echo()
motion-blur-smear-limit
smear({ limit: 1.3 })
on a card with text
smear()
at its 1.05 default, or
echo()
motion-blur-one-child
squeeze()
on a layer holding an icon and a label
The icon and label wrapped in one element
motion-blur-follow-path
blur.render(previous, current)
on an orbit
blur.render((t) => at(angle + t * speed * frameTime))
motion-blur-slowdown-shutter
A ¼× demo at
shutter: 180
shutter: 720
motion-blur-custom-technique
clear() {}
, or
technique: ({ layer }) => …
inside a render
clear()
removing every style
paint
set, and a
key
motion-press-scale
whileTap={{ scale: 0.8 }}
whileTap={{ scale: 0.98 }}
motion-short-stagger
staggerChildren: 0.15
staggerChildren: 0.03
motion-exit-in-presence
{open && <motion.div exit={{ opacity: 0 }} />}
The same element inside
<AnimatePresence>
motion-dialogs-stay-mounted
{open && <Dialog.Root open>}
<Dialog.Root open={open} onOpenChange={setOpen}>
motion-measure-inner
ref
and
animate={{ height: bounds.height }}
on one node
The outer node animates, the inner node holds the
ref
motion-hold-content-size
w-full
content inside a collapsing sidebar
w-64
content that keeps its width
motion-reduced-motion
A drawer that slides under reduced motionAn opacity cross-fade
motion-blur-reduced-motion
reducedMotion: "never"
The default
"user"
每项检查都由一对内容组成:问题写法紧挨着替代写法。
规则问题替换方案
motion-opacity-for-chrome
页面上的
initial={{ x: 20, scale: 0.95 }}
initial={{ opacity: 0 }}
motion-changing-axis-only
仅高度变化时使用
animate={{ width, height }}
animate={{ height }}
motion-no-replay-on-entry
路由外层使用
<AnimatePresence>
,导致入场动画重放
<AnimatePresence initial={false}>
motion-no-flash-on-return
缓存视图出现骨架屏闪烁缓存视图立即渲染
motion-name-the-properties
transition: all 200ms
transition: opacity 200ms, transform 200ms
motion-transitions-for-state
切换 class 使用
.open { animation: slide-in 200ms }
在被切换的属性上使用
transition: transform 200ms
motion-origin-from-trigger
下拉菜单从
center
缩放
在左对齐触发器下使用
transform-origin: top left
motion-no-scale-from-zero
弹出层使用
initial={{ scale: 0 }}
initial={{ scale: 0.95, opacity: 0 }}
motion-spring-for-gestures
拖动释放采用
duration: 0.3, ease: "easeOut"
使用
type: "spring"
并传入
velocity: info.velocity.x
motion-flick-dismisses
仅超过固定距离后才关闭超过距离或高于
0.11
px/ms 时关闭
motion-friction-at-edges
抽屉在顶部突然停止拖动距离越远,运动会受到越强阻尼
motion-gesture-alternative
行只能通过拖动重排行菜单中提供
Move Up
和
Move Down
motion-ease-by-direction
模态框使用
ease-in
进入
进入和退出均使用 ease-out,退出时长更短
motion-strong-ease-out
transition: transform 200ms ease-out
transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1)
motion-close-faster
模态框进入
200ms
、退出也为
200ms
进入
200ms
,退出
150ms
motion-instant-hover-color
按钮使用
transition: background-color 200ms
即时应用 hover 颜色
motion-under-300ms
下拉菜单使用
transition={{ duration: 0.5 }}
duration: 0.2
motion-no-motion-on-repeat
每次按键都执行
animate={{ scale: [1, 1.02, 1] }}
输入框不设置动效
motion-tooltip-delay
每个工具栏 Tooltip 都等待完整延迟第一个等待,其相邻项立即打开
motion-icon-crossfade
{copied ? <CheckIcon /> : <CopyIcon />}
两个图标以缩放和模糊交叉淡化
motion-icon-morph
两个 morph 形式图标交叉淡化每个
motion.path
的
d
动画过渡到对应图标
motion-blur-the-crossfade
切换过程中两个按钮标签重叠切换运行时使用
filter: blur(2px)
motion-blur-untracked-only
轮播页面滑动时使用模糊不使用模糊,或仅对被甩出的卡片使用模糊
motion-blur-package
卡片飞动时使用
filter: blur(8px)
在卡片上使用
useMotionBlur()
motion-blur-ref-on-mover
在包裹
motion.div
的 wrapper 上使用
ref={blur.ref}
在
motion.div
上使用
ref={blur.ref}
motion-blur-transparent-host
在 host 上使用
bg-white rounded-xl
将相同类放到它的第一个子元素上
motion-blur-bare-layer
在 layer 上使用
rotate-3
在 layer 内部的元素上使用
rotate-3
motion-blur-echo-for-turns
在 spinner 上使用
technique: squeeze()
technique: echo()
motion-blur-smear-limit
在带文字的卡片上使用
smear({ limit: 1.3 })
使用默认限制为 1.05 的
smear()
,或使用
echo()
motion-blur-one-child
在包含图标和标签的 layer 上使用
squeeze()
将图标和标签包裹在一个元素中
motion-blur-follow-path
在 orbit 上使用
blur.render(previous, current)
blur.render((t) => at(angle + t * speed * frameTime))
motion-blur-slowdown-shutter
¼ 倍速演示使用
shutter: 180
shutter: 720
motion-blur-custom-technique
clear() {}
,或在 render 内使用
technique: ({ layer }) => …
clear()
移除
paint
设置的每一种样式,并提供
key
motion-press-scale
whileTap={{ scale: 0.8 }}
whileTap={{ scale: 0.98 }}
motion-short-stagger
staggerChildren: 0.15
staggerChildren: 0.03
motion-exit-in-presence
{open && <motion.div exit={{ opacity: 0 }} />}
将同一元素放入
<AnimatePresence>
内部
motion-dialogs-stay-mounted
{open && <Dialog.Root open>}
<Dialog.Root open={open} onOpenChange={setOpen}>
motion-measure-inner
在同一节点上使用
ref
和
animate={{ height: bounds.height }}
外层节点执行动画,内层节点保存
ref
motion-hold-content-size
折叠中的侧边栏内使用
w-full
内容
使用
w-64
内容并保持其宽度
motion-reduced-motion
抽屉在减少动态效果模式下仍滑动改为 opacity 交叉淡化
motion-blur-reduced-motion
reducedMotion: "never"
默认的
"user"

Reporting

报告

Severity.
HIGH
breaks use or access. That is an exit that never plays, a container that measures itself or a gesture with no button. It is also motion blur on text being read, or a layer with two children under
squeeze()
or
svg()
.
MEDIUM
is a sliding page, a replayed entrance, a gesture on a fixed curve or motion over 300ms. It is also motion on typing, a wrong curve,
transition: all
, keyframes on toggled state or ignored reduced motion. It is also a conditionally rendered dialog, content reflowing as it collapses, a
scale(0)
entrance or a sheet that ignores a flick. It is also a hand-written blur, a blur ref on a wrapper, an opaque host or a transform on the layer. It is also a spinner on anything but Echo, a smear past 1.05 or
reducedMotion: "never"
.
LOW
is a faded hover colour, a heavy press scale or a long stagger. It is also motion from the wrong origin, a close as slow as its open, a slow tooltip or a hard icon swap. It is also a bare
ease-out
, an unblurred cross-fade or a drawer with no edge friction. It is also two transforms on a curve, a slowed demo at the full-speed shutter or a custom technique that leaves styles or rebuilds.
Format. One finding per root cause:
file:line - [rule-id] what is wrong
Then a
Rule | Count | Severity
summary.
Block
when any
HIGH
remains,
Approve
otherwise. Never
Approve
a file you did not read. Play the motion when a check depends on timing, and freeze it at its fastest point when a check depends on a streak. Mark every check you could not run as
Not verified
.
严重程度。
HIGH
会破坏使用或无障碍访问。包括退出动画从不播放、容器对自身进行测量,或手势没有按钮替代方式。也包括对正在阅读的文本使用动态模糊,或在
squeeze()
或
svg()
下图层包含两个子元素。
MEDIUM
包括滑动页面、重放入场动画、使用固定曲线的手势或超过 300ms 的动效。还包括输入时出现动效、使用错误曲线、
transition: all
、在切换状态上使用 keyframes,或忽略减少动态效果设置。也包括条件渲染的对话框、折叠时发生内容重排、使用
scale(0)
的入场动画,或侧滑面板忽略轻扫。也包括手写模糊、wrapper 上使用 blur ref、不透明 host,或在 layer 上设置 transform。也包括 spinner 使用 Echo 之外的技术、smear 超过 1.05,或设置
reducedMotion: "never"
。
LOW
包括渐变的 hover 颜色、过度按压缩放或过长的 stagger。也包括从错误原点开始的动效、关闭与打开一样慢的动画、缓慢的 Tooltip 或生硬的图标切换。也包括直接使用
ease-out
、未使用模糊的交叉淡化,或没有边缘阻力的抽屉。也包括在曲线上使用两个 transform、以全速快门播放的慢动作演示,或会残留样式或重复构建的自定义技术。
格式。 每个根本原因只报告一项问题:
file:line - [rule-id] what is wrong
然后提供
Rule | Count | Severity
汇总。只要仍有任何
HIGH
问题,就标记
Block
;否则标记
Approve
。绝不能批准未读取的文件。当检查依赖时序时,实际播放动效;当检查依赖拖影时,将画面定格在拖影最明显的时刻。将每个未能运行的检查标记为
Not verified
。