Vue 3 与 GSAP 深度集成

概述

GSAP(GreenSock Animation Platform)是一个功能强大的JavaScript动画库,提供了比原生CSS动画更丰富的控制能力和更好的性能。本集将深入探讨Vue 3与GSAP的深度集成,包括如何在Vue组件中高效使用GSAP,创建复杂的动画序列,以及优化动画性能。

核心知识点

1. GSAP基础回顾

GSAP提供了多种动画API,包括:

// 基础动画
gsap.to('.element', {
  duration: 1,
  x: 100,
  opacity: 1,
  ease: 'power2.out'
});

// 从某个状态动画
gsap.from('.element', {
  duration: 1,
  y: -50,
  opacity: 0
});

// 从一个状态到另一个状态
gsap.fromTo('.element', 
  { opacity: 0, scale: 0.5 },
  { opacity: 1, scale: 1, duration: 0.5 }
);

2. 在Vue 3中集成GSAP

安装GSAP

npm install gsap

基本使用

在Vue组件中使用GSAP:

<template>
  <div ref="boxRef" class="box"></div>
  <button @click="animateBox">动画</button>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import gsap from 'gsap'

const boxRef = ref(null)

const animateBox = () => {
  gsap.to(boxRef.value, {
    duration: 1,
    x: 200,
    rotation: 360,
    ease: 'elastic.out(1, 0.3)'
  })
}

onMounted(() => {
  // 初始动画
  gsap.from(boxRef.value, {
    duration: 0.8,
    opacity: 0,
    y: -50,
    ease: 'back.out(1.7)'
  })
})
</script>

<style scoped>
.box {
  width: 100px;
  height: 100px;
  background-color: #42b883;
  border-radius: 8px;
}
</style>

3. GSAP Timeline与Vue集成

Timeline允许创建复杂的动画序列:

<template>
  <div class="container">
    <div ref="titleRef" class="title">Vue 3 + GSAP</div>
    <div ref="subTitleRef" class="subtitle">深度集成教程</div>
    <div ref="boxRef" class="box"></div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import gsap from 'gsap'

const titleRef = ref(null)
const subTitleRef = ref(null)
const boxRef = ref(null)

onMounted(() => {
  // 创建时间线
  const tl = gsap.timeline({
    defaults: {
      duration: 0.8,
      ease: 'power2.out'
    }
  })

  // 添加动画序列
  tl.from(titleRef.value, {
    opacity: 0,
    y: -30
  })
  .from(subTitleRef.value, {
    opacity: 0,
    y: -20
  }, '-=0.5') // 重叠50%的动画时间
  .from(boxRef.value, {
    opacity: 0,
    scale: 0.5,
    rotation: -180
  }, '-=0.3')
})
</script>

4. GSAP与Vue过渡系统结合

将GSAP与Vue的&lt;Transition&gt;组件结合:

<template>
  <div>
    <button @click="show = !show">切换显示</button>
    <Transition
      name="custom-fade"
      @enter="onEnter"
      @leave="onLeave"
    >
      <div v-if="show" class="box"></div>
    </Transition>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import gsap from 'gsap'

const show = ref(false)

const onEnter = (el, done) => {
  gsap.fromTo(el, 
    { opacity: 0, y: -50, scale: 0.8 },
    { 
      opacity: 1, 
      y: 0, 
      scale: 1,
      duration: 0.6,
      ease: 'back.out(1.7)',
      onComplete: done // 必须调用done()通知Vue过渡完成
    }
  )
}

const onLeave = (el, done) => {
  gsap.to(el, {
    opacity: 0,
    y: 50,
    scale: 0.8,
    duration: 0.4,
    ease: 'power2.in',
    onComplete: done
  })
}
</script>

5. GSAP ScrollTrigger与Vue

ScrollTrigger允许创建基于滚动的动画:

<template>
  <div class="scroll-container">
    <div class="section">
      <h2>滚动动画</h2>
    </div>
    <div ref="animateRef" class="animate-element">
      滚动到此处查看动画
    </div>
    <div class="section"></div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'

// 注册ScrollTrigger插件
gsap.registerPlugin(ScrollTrigger)

const animateRef = ref(null)

onMounted(() => {
  gsap.fromTo(animateRef.value, 
    { opacity: 0, x: -100 },
    {
      opacity: 1,
      x: 0,
      duration: 1,
      scrollTrigger: {
        trigger: animateRef.value,
        start: 'top 80%', // 元素顶部到达视口80%时开始
        end: 'bottom 20%', // 元素底部到达视口20%时结束
        toggleActions: 'play pause resume reverse',
        markers: true // 开发时显示标记
      }
    }
  )
})
</script>

<style scoped>
.scroll-container {
  height: 200vh;
  padding: 20px;
}

.section {
  height: 80vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.animate-element {
  padding: 40px;
  background-color: #42b883;
  color: white;
  border-radius: 8px;
  margin: 40px 0;
}
</style>

6. 创建可复用的GSAP Composable

将GSAP动画封装为可复用的Composable:

// composables/useGSAP.js
import { onMounted, onUnmounted, ref } from 'vue'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'

gsap.registerPlugin(ScrollTrigger)

export function useGSAP() {
  const timelines = ref([])

  // 创建时间线
  const createTimeline = (options = {}) => {
    const tl = gsap.timeline(options)
    timelines.value.push(tl)
    return tl
  }

  // 清除所有动画
  const clearAnimations = () => {
    timelines.value.forEach(tl => tl.kill())
    timelines.value = []
    ScrollTrigger.getAll().forEach(trigger => trigger.kill())
  }

  // 元素进入动画
  const animateIn = (element, options = {}) => {
    const defaultOptions = {
      duration: 0.8,
      opacity: 0,
      y: 30,
      ease: 'power2.out',
      ...options
    }
    return gsap.fromTo(element, 
      { opacity: 0, y: 30 },
      defaultOptions
    )
  }

  onUnmounted(() => {
    clearAnimations()
  })

  return {
    createTimeline,
    clearAnimations,
    animateIn,
    gsap,
    ScrollTrigger
  }
}

使用Composable:

<template>
  <div ref="elementRef" class="element">
    动画元素
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useGSAP } from '../composables/useGSAP'

const elementRef = ref(null)
const { animateIn, createTimeline } = useGSAP()

onMounted(() => {
  animateIn(elementRef.value, {
    duration: 1,
    x: 100
  })
})
</script>

最佳实践

1. 性能优化

  • 避免频繁创建新的动画实例:使用时间线复用动画
  • 使用CSS变量:允许动态修改动画参数
  • 优化选择器:直接传递DOM元素而不是字符串选择器
  • 使用will-change:提示浏览器哪些属性将要变化
.element {
  will-change: transform, opacity;
}

2. 动画控制

  • 暂停/恢复动画:使用timeline.pause()timeline.resume()
  • 反向动画:使用timeline.reverse()
  • 重新开始动画:使用timeline.restart()
  • 进度控制:使用timeline.progress(0.5)直接跳转到50%进度

3. 响应式动画

  • 使用ResizeObserver:监听元素大小变化并调整动画
  • 响应式断点:根据屏幕尺寸调整动画参数
  • 流动动画:使用相对单位确保动画在不同尺寸下表现一致

4. 可访问性

  • 提供动画开关:允许用户关闭非必要动画
  • 尊重系统动画偏好
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (!reduceMotion) {
  // 执行动画
}

常见问题与解决方案

1. 动画不执行

问题:GSAP动画没有执行或没有效果

解决方案

  • 确保DOM元素已挂载(使用onMountednextTick
  • 检查选择器是否正确
  • 确保GSAP已正确安装和导入
  • 检查动画属性是否支持(例如,某些属性需要特定的CSS设置)

2. 动画性能差

问题:动画卡顿或不流畅

解决方案

  • 优先使用transformopacity属性
  • 避免在动画中修改布局属性(如widthheighttopleft
  • 使用will-change属性
  • 减少同时执行的动画数量
  • 考虑使用gsap.ticker来优化动画循环

3. 组件卸载后动画仍在运行

问题:组件卸载后,GSAP动画仍在执行,导致内存泄漏

解决方案

  • onUnmounted钩子中清除所有动画
  • 使用Composable封装动画逻辑,自动处理清理

4. ScrollTrigger在Vue组件中不工作

问题:ScrollTrigger无法正确检测滚动位置

解决方案

  • 确保在onMounted中初始化ScrollTrigger
  • 检查滚动容器是否正确设置
  • 考虑使用ScrollTrigger.refresh()在DOM更新后刷新

进阶学习资源

  1. 官方文档

  2. 视频教程

  3. 社区资源

  4. 书籍

    • 《GreenSock Animation Platform Cookbook》

实践练习

练习1:创建一个复杂的入场动画

要求

  • 创建一个包含标题、副标题和多个卡片的页面
  • 使用GSAP Timeline创建流畅的入场动画序列
  • 标题和副标题依次淡入并上移
  • 卡片从不同方向飞入,带有缩放效果
  • 动画之间有适当的重叠

练习2:实现滚动触发动画

要求

  • 创建一个长页面,包含多个内容区块
  • 使用GSAP ScrollTrigger实现以下效果:
    • 元素进入视口时淡入并上移
    • 滚动时数字计数器动画
    • 图片滚动时的视差效果
    • 进度条随滚动更新

练习3:创建交互式动画组件

要求

  • 创建一个可复用的按钮组件,带有GSAP悬停和点击动画
  • 实现以下效果:
    • 悬停时背景色变化和轻微放大
    • 点击时的按压效果
    • 加载状态的动画
  • 使用Composable封装动画逻辑

练习4:结合Vue状态管理

要求

  • 使用Pinia创建一个动画状态管理
  • 实现以下功能:
    • 全局动画开关(尊重系统偏好)
    • 动画速度控制
    • 动画缓动函数选择
    • 保存和恢复动画状态

总结

Vue 3与GSAP的深度集成提供了强大的动画能力,可以创建复杂、流畅的用户体验。通过合理使用GSAP的时间线、ScrollTrigger和Composable模式,可以在Vue应用中高效地实现各种动画效果,同时保持代码的可维护性和性能。

在下一集中,我们将探讨Vue 3与Framer Motion的高级应用,敬请期待!

« 上一篇 Vue 3 高级动画和交互:构建流畅用户体验 下一篇 » Vue 3 与 Framer Motion 高级应用:构建流畅交互体验