Vueuse/Motion 组件库使用说明

212 阅读6分钟

在 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/MotionCSS Animation
语法风格声明式,状态驱动命令式,关键帧定义
状态联动支持响应式数据绑定需 JS 手动控制类名切换
动效能力内置物理引擎(弹簧 / 惯性)仅基础缓动函数
性能开销复杂动画≈CSS,简单动画 + 5% 开销原生渲染,开销最低
调试难度支持动画实例断点调试依赖浏览器 DevTools 动画面板

适配场景:交互密集型动画优先选 Vueuse/Motion,静态过渡效果可考虑 CSS。

3.2 与主流动画库对比

特性Vueuse/MotionGSAPanime.js
核心体积~10KB~30KB~15KB
Vue 集成度无缝支持 Composition API需额外封装需额外封装
高级功能基础路径 / 手势动画3D/SVG/Canvas 全场景支持路径动画完善,无 3D 引擎
学习成本低(Vue 开发者零门槛)中(需学专用语法)中(API 较繁琐)
社区生态Vue 生态内丰富跨框架,资源极多跨框架,资源中等

3.3 优劣势总结

核心优势

  1. Vue 原生融合:与响应式系统深度绑定,动画参数可直接使用 ref 变量
  1. 轻量高效:核心体积仅 10KB,支持 Tree-Shaking,比 GSAP 小 60%
  1. 物理动效:弹簧系统(stiffness/damping 参数)实现自然弹性效果
  1. 多端适配:手势系统自动兼容桌面端 / 移动端交互

明显劣势

  1. 高级功能缺失:无 GSAP 的时间线(Timeline)和复杂贝塞尔曲线控制
  1. SSR 局限:指令式动画在服务端渲染时需额外处理 hydration
  1. 版本兼容:Vue 2 版本停止维护,仅支持 Vue 3

四、实战避坑指南

4.1 性能优化 3 原则

  1. 优先使用 transform/opacity:避免修改 width、margin 等触发重排的属性
  1. 合理使用 will-change:复杂动画元素添加will-change: transform提示浏览器优化
  1. 禁用不必要动画:结合usePreferredReducedMotion尊重用户系统设置
import { usePreferredReducedMotion } from '@vueuse/core'
const disableMotion = usePreferredReducedMotion() // 自动检测系统动画设置

4.2 常见问题排查

  • 动画不触发:检查是否绑定正确变体(如enter需元素挂载时触发)
  • 卡顿掉帧:减少同时执行的动画数量,复杂场景用requestAnimationFrame包裹
  • 状态冲突:避免同时使用v-motion指令和useMotion组合式 API

4.3 动画复用技巧

  1. 全局指令注册:如快速上手部分的fade-in指令,支持全项目复用
  1. 变体对象提取:将复杂变体定义为单独文件,通过 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)动手实践,你会发现动画开发可以如此丝滑。