在 Vue 生态中,动画开发往往面临 "简单场景繁琐化、复杂场景陡峭化" 的困境。Vueuse/Motion 作为 Vue 官方推荐的动画库,以 "轻量 + 原生融合" 的特性解决了这一痛点。本文基于官方文档motion.net.cn/docs/vue,结合 5 个案例拆解核心用法,并对比主流方案助方便了解。
一、快速上手:3 分钟接入动画能力
1.1 核心概念
Vueuse/Motion 是基于 Vue 3 Composition API 构建的动画库,核心优势在于状态驱动动画—— 通过描述元素在不同状态(初始 / 进入 / 交互 / 离开)的样式,自动计算过渡效果,无需手动编写关键帧。
1.2 安装与配置
# 核心包(Vue 3专属)
npm install @vueuse/motion --save
# Vue 2用户请安装 vue-motion@0.16.1
全局注册(推荐):
// main.js
import { createApp } from 'vue'
import { MotionPlugin } from '@vueuse/motion'
import App from './App.vue'
const app = createApp(App)
// 可配置全局预设动画
app.use(MotionPlugin, {
directives: {
// 注册全局复用动画指令
'fade-in': {
initial: { opacity: 0 },
enter: { opacity: 1, transition: { duration: 500 } }
}
}
})
app.mount('#app')
二、案例展示
案例 1:基础入场 / 离场动画(指令式)
需求:按钮控制元素淡入淡出,附带缩放效果
<template>
<div class="demo-container">
<button @click="show = !show" class="btn">切换显示</button>
<!-- 使用全局注册的指令 -->
<div v-if="show" v-motion-fade-in class="box">
基础过渡效果
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<style scoped>
.box {
width: 200px;
height: 200px;
background: #42b983;
border-radius: 8px;
}
</style>
关键解析:通过v-motion-xxx指令复用动画配置,initial定义初始状态,enter定义进入动画,自动关联v-if的显示逻辑。
案例 2:列表交错动画(组件式)
需求:列表项依次入场,实现瀑布流效果
<template>
<MotionGroup
:variants="listVariants"
tag="ul"
class="list-container"
>
<li
v-for="(item, i) in list"
:key="i"
class="list-item"
>
{{ item }}
</li>
</MotionGroup>
</template>
<script setup>
import { MotionGroup } from '@vueuse/motion'
import { ref, onMounted } from 'vue'
const list = ref([])
const listVariants = {
initial: { opacity: 0, y: 20 },
enter: (i) => ({ // 接收索引参数实现交错
opacity: 1,
y: 0,
transition: {
delay: i * 0.1, // 每项延迟0.1s
duration: 0.5
}
})
}
onMounted(() => {
list.value = ['Item 1', 'Item 2', 'Item 3', 'Item 4']
})
</script>
核心特性:MotionGroup组件自动为子元素添加索引依赖的动画,支持tag属性定制容器标签,无额外 DOM 开销。
案例 3:手势交互动画(组合式 API)
需求:实现可拖拽的悬浮按钮,支持点击缩放反馈
<template>
<div ref="btnRef" class="float-btn">+</div>
</template>
<script setup>
import { useMotion, useMotionGesture } from '@vueuse/motion'
import { ref } from 'vue'
const btnRef = ref(null)
// 1. 定义动画状态
const { set, state } = useMotion(btnRef, {
initial: { scale: 1, x: 0, y: 0 },
hovered: { scale: 1.1 },
tapped: { scale: 0.95 }
})
// 2. 绑定手势事件
useMotionGesture(btnRef, {
drag: ({ movementX, movementY }) => {
set({ x: movementX, y: movementY })
},
hover: ({ hovering }) => {
state.value = hovering ? 'hovered' : 'initial'
},
tap: () => {
state.value = 'tapped'
setTimeout(() => state.value = 'initial', 200)
}
})
</script>
优势体现:手势系统自动适配移动端触摸事件,无需额外处理兼容性,且动画状态与交互逻辑解耦。
案例 4:滚动触发动画
需求:元素进入视口时执行动画,仅触发一次
<template>
<div class="long-page">
<div
v-motion
:initial="{ opacity: 0, scale: 0.8 }"
:visible-once="{ opacity: 1, scale: 1 }"
:viewport="{ margin: '-100px' }" // 视口偏移量
class="section"
>
滚动可见区域触发动画
</div>
</div>
</template>
性能优化:visible-once变体相比visible减少重复计算,配合viewport.margin精准控制触发时机。
案例 5:路由过渡动画
需求:实现页面切换的 3D 翻转效果
<!-- App.vue -->
<template>
<router-view v-slot="{ Component }">
<Motion
:key="$route.path"
:initial="{ opacity: 0, rotateY: 90 }"
:enter="{ opacity: 1, rotateY: 0 }"
:leave="{ opacity: 0, rotateY: -90 }"
:transition="{ duration: 300 }"
>
<component :is="Component" />
</Motion>
</router-view>
</template>
实现原理:利用key属性监听路由变化,通过 3D 变换属性rotateY实现空间过渡效果,性能优于传统 CSS 动画。
三、方案对比:为什么选 / 不选 Vueuse/Motion?
3.1 与 CSS Animation 对比
| 对比维度 | Vueuse/Motion | CSS Animation |
|---|---|---|
| 语法风格 | 声明式,状态驱动 | 命令式,关键帧定义 |
| 状态联动 | 支持响应式数据绑定 | 需 JS 手动控制类名切换 |
| 动效能力 | 内置物理引擎(弹簧 / 惯性) | 仅基础缓动函数 |
| 性能开销 | 复杂动画≈CSS,简单动画 + 5% 开销 | 原生渲染,开销最低 |
| 调试难度 | 支持动画实例断点调试 | 依赖浏览器 DevTools 动画面板 |
适配场景:交互密集型动画优先选 Vueuse/Motion,静态过渡效果可考虑 CSS。
3.2 与主流动画库对比
| 特性 | Vueuse/Motion | GSAP | anime.js |
|---|---|---|---|
| 核心体积 | ~10KB | ~30KB | ~15KB |
| Vue 集成度 | 无缝支持 Composition API | 需额外封装 | 需额外封装 |
| 高级功能 | 基础路径 / 手势动画 | 3D/SVG/Canvas 全场景支持 | 路径动画完善,无 3D 引擎 |
| 学习成本 | 低(Vue 开发者零门槛) | 中(需学专用语法) | 中(API 较繁琐) |
| 社区生态 | Vue 生态内丰富 | 跨框架,资源极多 | 跨框架,资源中等 |
3.3 优劣势总结
核心优势
- Vue 原生融合:与响应式系统深度绑定,动画参数可直接使用 ref 变量
- 轻量高效:核心体积仅 10KB,支持 Tree-Shaking,比 GSAP 小 60%
- 物理动效:弹簧系统(stiffness/damping 参数)实现自然弹性效果
- 多端适配:手势系统自动兼容桌面端 / 移动端交互
明显劣势
- 高级功能缺失:无 GSAP 的时间线(Timeline)和复杂贝塞尔曲线控制
- SSR 局限:指令式动画在服务端渲染时需额外处理 hydration
- 版本兼容:Vue 2 版本停止维护,仅支持 Vue 3
四、实战避坑指南
4.1 性能优化 3 原则
- 优先使用 transform/opacity:避免修改 width、margin 等触发重排的属性
- 合理使用 will-change:复杂动画元素添加will-change: transform提示浏览器优化
- 禁用不必要动画:结合usePreferredReducedMotion尊重用户系统设置
import { usePreferredReducedMotion } from '@vueuse/core'
const disableMotion = usePreferredReducedMotion() // 自动检测系统动画设置
4.2 常见问题排查
- 动画不触发:检查是否绑定正确变体(如enter需元素挂载时触发)
- 卡顿掉帧:减少同时执行的动画数量,复杂场景用requestAnimationFrame包裹
- 状态冲突:避免同时使用v-motion指令和useMotion组合式 API
4.3 动画复用技巧
- 全局指令注册:如快速上手部分的fade-in指令,支持全项目复用
- 变体对象提取:将复杂变体定义为单独文件,通过 import 复用
// animations/listVariants.js
export default {
initial: { opacity: 0, y: 20 },
enter: (i) => ({ /* ... */ })
}
五、总结
Vueuse/Motion 在项目中,既满足基础的业务场景需求,又保持了轻量和易用性。如果你需要:
- 快速实现交互联动动画
- 避免 CSS 与 JS 的频繁切换
- 融入 Vue 响应式生态
那么它会是比 CSS Animation 更高效、比 GSAP 更轻量的选择。建议结合官方文档的交互式案例(motion.net.cn/docs/vue)动手实践,你会发现动画开发可以如此丝滑。