动效指南

用动效解释,而非干扰

这是一份实用指南,帮助判断何时应当产生位移、选择合适的令牌,并交付快速、一致且可访问的动效。

判断框架

动起来之前的四个问题

最好的动效决定,往往发生在设置时长或弹簧参数之前。

01

先说明变化

指出用户刚刚操作的对象,以及操作后需要看见的位置或状态。

用连续的位置关系帮助理解,避免无关元素一起移动。

02

控制等待

高频控件立即响应。弹层可以解释来源,但不应阻塞用户继续输入。

反复切换时也要支持中断,不能等待上一段动画播完。

03

保持物理连续

同一个对象移动或变形时,共享布局和弹簧用于衔接当前位置与新目标。

进度条使用线性时间;手势从释放时的位置和速度继续。

04

提供静态路径

减少动态效果时仍需显示状态、焦点和操作结果,移除装饰性的位移与循环。

不要让可访问性设置改变功能或键盘操作。

动效令牌

处处使用同一种语言

orvui 将有意图的动效保存在共享令牌中。按用途选择,让组件彼此协调,而非以完全相同的方式移动。

对比曲线

相同距离、相同时长,不同用途。

EASE_OUT[0.16, 1, 0.3, 1]

进入和退出立即响应,再平稳结束。

EASE_IN_OUT[0.77, 0, 0.175, 1]

屏幕上已有的对象自然地加速与减速。

SPRING_PRESS

为按钮和其他可按压表面提供快速、有分量的反馈。

SPRING_LAYOUT

用于需要持续空间运动的共享表面与指示器。

文档中的切换

内容变化,阅读位置不变

页面导航与局部视图使用不同节奏。切换下面的章节,观察正文变化与稳定的导航框架。

orvui文档
开始使用

安装

添加组件、加载样式,再连接应用状态。

pnpm add @orvui/vue
准备继续
页面切换120ms / 220ms
只切换阅读区域,保留导航框架。旧正文淡出,新正文轻移 8px 后落稳;快速切换以最后一次选择为准。
预览、用法与源码180ms
保持操作栏位置稳定,在当前示例内切换视图。隐藏内容不能接收键盘焦点,也不重新播放整页入场。
锚点、查询与减少动态效果0ms / 80ms
同页锚点和查询更新不触发整页转场;减少动态效果时只保留 80ms 淡入,取消位移。首次 SSR 与水合直接显示正文。
nuxt.config.ts
app.vue

时长

快到足以感觉即时

时长取决于大小、距离和频率。这些范围是起点,不是必须机械达到的指标。

按钮按压立即响应跟随输入
提示和菜单120–180ms快速到位
内容替换180–240ms保留上下文
抽屉与对话框200–300ms交代空间关系
拖动和滚动由手势决定连续、可中断

界面动效默认控制在 300ms 以内。更长的动效适合解释性演示、有意图的手势和大范围空间变化。

配方

可以复制的模式

每个配方都把目的与可用于生产的令牌、减少动态效果状态和具体失败模式联系起来。

输入

按压反馈

让可按压表面在指针按下时轻微收缩,松开后回到原位。

目的在异步结果返回前确认操作已被接收。
避免把按压反馈绑定到操作完成,或把位移放大。
查看配方+
<motion.button :while-press="reduce ? undefined : { scale: 0.97 }" :transition="SPRING_PRESS">Continue</motion.button>
动作

图标响应

按操作含义选择短暂运动,并让键盘聚焦获得同等反馈。

目的让用户更容易辨认刚刚操作的控件。
避免所有图标都播放同一个循环,或把反馈只给鼠标用户。
查看配方+
<motion.span :animate="active && !reduce ? { rotate: [0, 10, -6, 0] } : { rotate: 0 }" :transition="{ duration: 0.24 }"><Bell /></motion.span>
出现

新内容出现

先让整体表面出现,再呈现内容,避免读者追逐多个运动目标。

目的说明哪些内容是刚刚加入的。
避免对整个页面逐字逐项编排,延迟用户阅读。

新表面

在获得焦点前就绪。

查看配方+
<motion.section :initial="{ opacity: 0, y: reduce ? 0 : 8 }" :animate="{ opacity: 1, y: 0 }" :transition="{ duration: 0.2 }"><slot /></motion.section>
布局

表面展开

触发器与展开区域属于同一个对象时,用共享布局保留这条联系。

目的在不同尺寸之间保留对象身份。
避免缩放长段文字,或在容器到位前显示全部内容。
查看配方+
<motion.div layout :transition="CONTINUITY_SPRING"><button @click="open = !open">Toggle</button><div v-if="open">Panel content</div></motion.div>
状态

视图切换

保持外层布局稳定,在内部用短距离和透明度切换两种视图。

目的说明内容已变化,同时保留操作位置。
避免让频繁切换的标签页触发整页转场。
实时组件视图
查看配方+
<div class="grid rounded-xl bg-background p-4"><motion.div v-for="current in views" :key="current" class="col-start-1 row-start-1" :aria-hidden="current !== view" :inert="current !== view" :initial="false" :animate="{ opacity: current === view ? 1 : 0 }" :transition="{ duration: reduce ? 0.08 : 0.18 }"><slot :view="current" /></motion.div></div>

可访问性

减少动态效果是一种设计状态

不要移除全部过渡。保留帮助理解的反馈,移除可能引起不适的运动。

保留含义,移除位移。

保留透明度、颜色和即时状态变化。去掉视差、大幅变换、反复缩放和弹簧过冲。

const reduce = useReducedMotion();
const start = computed(() => ({ opacity: 0, y: reduce.value ? 0 : 8 }));
const target = { opacity: 1, y: 0 };