返回 导航

Vue.js

hangge.com

Vue.js - GSAP动画库使用详解(实现过渡动画)

作者:hangge | 2026-04-17 08:44
    在某些情况下,我们希望通过 JavaScript 实现一些动画效果,这时可以选择非常流行的 GSAP 动画库。GSAPGreenSock Animation Platform 的缩写,适用于 DOMSVG、数值、时间轴组合等复杂动画场景。它可以通过 JavaScript 的方式为 CSS 属性、SVGCanvas 等设置动画。先我将通过样例演示如何安装并使用 GSAP 动画库。

1,安装 GSAP 动画库

在终端中进行项目根目录,执行如下命令安装即可:
npm install gsap

2,CSS 属性动画及 timeline 样例

(1)下面使用 GSAP 实现一个带有淡入、淡出动画的列表切换效果:
  • 组件加载时会通过 playIn() 对列表项执行由下向上的逐项(stagger)进入动画。
  • 点击“显示/隐藏”按钮时,通过 toggle() 根据 visible 状态决定播放进入或反向播放离场动画。
  • playOut() 使用 GSAPreverse() 让时间线倒放,使列表以相反顺序隐去,并在动画结束后销毁 timeline,避免重复叠加。
<template>
    <div>
        <button @click="toggle">显示/隐藏</button>
        <div class="list">
            <div v-for="(item, index) in list" :key="item.id" class="list-item" ref="items">
                {{ item.text }}
            </div>
        </div>
    </div>
</template>

<script>
import { gsap } from "gsap";

export default {
    data() {
        return {
            visible: true,
            list: [
                { id: 1, text: "列表项一" },
                { id: 2, text: "列表项二" },
                { id: 3, text: "列表项三" },
            ],
            tl: null,
        };
    },
    mounted() {
        this.playIn();
    },
    methods: {
        // 显示动画
        playIn() {
            if (this.tl) this.tl.kill();
            const nodes = this.$refs.items;
            gsap.set(nodes, { opacity: 1, y: 0 });
            this.tl = gsap.timeline();
            this.tl.from(nodes, {
                opacity: 0,
                y: 20,
                stagger: 0.08,
                duration: 0.45,
                ease: "power2.out",
            });
        },
        // 隐藏动画
        playOut() {
            if (!this.tl) return;
            // 反向播放并在结束后销毁 timeline
            this.tl.timeScale(1.2).reverse().then(() => {
                this.tl.kill();
                this.tl = null;
            });
        },
        // 切换显示/隐藏
        toggle() {
            this.visible = !this.visible;
            if (this.visible) {
                this.playIn();
            } else {
                this.playOut();
            }
        },
    },
    beforeDestroy() {
        if (this.tl) this.tl.kill();
    },
};
</script>

<style>
.list-item {
    margin: 8px 0;
    padding: 10px;
    background: #f7f7f7;
    border-radius: 4px;
}
</style>

(2)运行效果如下,当点击“显示/隐藏”按钮时,列表项会以平滑的动画依次进入或反向退出,实现一个带有交错(stagger)效果的动态展示切换。

3,实现数字变化效果

(1)下面借助 GSAP 动画库实现一个数字变化效果:
  • 首先,在 watch 中监听 <input> 的输入,当用户输入数字时,会触发 watch 中定义的 counter 函数。
  • 接着,该函数使用 gsapto 函数为 Vue 实例的 showNumber 属性应用动画。即在 1 秒内,showNumber 的值将由 1 过渡到 <input> 输入的数值。这样就可以看到 showNumber 数字递增的效果。
<template>
    <div class="app">
        <input type="number" v-model="counter" style="margin-bottom:10px">
        <h2>当前计数: {{ showNumber.toFixed(0) }}</h2>
    </div>
</template>

<script>
import gsap from 'gsap';

export default {
    data() {
        return {
            counter: 1,
            showNumber: 0
        }
    },
    watch: {
        counter(newValue) {
            // 参数一:目标对象(这里是Vue实例)
            // 参数二:给目标对象中showNumber属性添加动画(在1s中showNumber由1过度到input输入的数值)
            gsap.to(this, { duration: 1, showNumber: newValue })
        }
    }
}
</script>

(2)运行效果如下,当我们在输入框中输入“9999”时,当前计数将会从 1 递增到 9999

4,配合 <transition> 组件实现过渡动画

(1)下面样例通过 <transition> 组件的事件和 JavaScript 钩子,配合 GSAP 动画库实现元素的显示或隐藏动画:
  • <transition> 组件中监听了 enterleave 事件。在元素被插入 DOM 之后的下一帧,会调用 enter 方法,该方法接收两个参数:eldone。其中,el 是需要做动画的 DOM 对象(<h4> 元素),done 表示过渡结束的回调函数。 
  • <transition> 组件设置:css="false" 后,表示 JavaScript 全权负责控制动画的过渡。在这种情况下,对 @enter @leave 钩子来说,调用回调函数 done 就是必需的。 
  • 接着,在 enter 方法中使用 gsapfrom 函数为 <h4> 元素应用动画,即 <h4> 元素从右边 200 处由小到大移动到默认位置。在 gsap 中动画执行完成之后会回调 onComplete 函数,这里通过直接将 done 函数传递给 onComplete 来完成这次动画。 
  • 同样,当离开过渡开始时会调用 leave 方法。我们在该方法中使用 gsapto 函数为 <h4> 元素应用动画,即 <h4> 元素从默认位置向右移动 200 并缩小为 0。 
<template>
    <div class="app">
        <button @click="isShow = !isShow">显示/隐藏</button>
        <transition @enter="enter" @leave="leave" :css="false">
            <h4 v-if="isShow">Hello World</h4>
        </transition>
    </div>
</template>

<script>
import gsap from 'gsap';
export default {
    data() {
        return {
            isShow: true,
        }
    },
    methods: {
        // 进入动画
        enter(el, done) {
            console.log("enter", el);
            // h4元素在整个进入过渡的阶段中应用了下面动画
            gsap.from(el, {
                scale: 0,
                x: 200,
                onComplete: done
            })
        },
        // 离开动画
        leave(el, done) {
            console.log("leave", el);
            // h4元素在整个离开过渡的阶段中应用了下面动画
            gsap.to(el, {
                scale: 0,
                x: 200,
                onComplete: done
            })
        }
    }
}
</script>
<style scoped>
h4 {
    border: 1px solid #ddd;
    width: 120px;
    margin-top: 20px;
    padding: 5px;
    text-align: center;
}
</style>

(2)运行效果如下,当单击“显示/隐藏”按钮时,<h4> 元素将从默认位置缓慢向右移动 200,并逐渐缩小为 0。 当再次单击“显示/隐藏”按钮时,<h4> 元素将从右侧 200 处缓慢移动到默认位置,并逐渐放大。

5,配合 <transition-group> 组件实现过渡动画

(1)下面是一个通过输入框筛选列表项条目的样例,我们利用 GSAP 库的延迟(delay)属性实现筛选时,列表条目交替消失的动画效果。
  • 首先在 <transition-group> 组件中添加:css="false" 属性,以禁用 <style> 标签中的 CSS 动画。
  • 接着,监听 before-enterenterleave 事件。 
    • beforeEnter 方法中初始化 <li> 元素,即 <li> 元素插入 <ul> 前是全透明的,并且高度为 0
    • enter 方法中指定所有添加的 <li> 元素是从全透明且高度为 0 过渡到不透明且高度为 1em 的动画,为每个 <li> 元素指定依次递增的延时以实现交替效果;
    • leave 方法中指定所有需移除的 <li> 元素是从不透明且高度为 1.5em 过渡到全透明且高度为 0 的动画,为 <li> 元素指定依次递增的延时以实现交替效果。 
  • 最后,在动画执行完成时都需要回调 done 函数,结束动画。 
<template>
    <div>
        <input v-model="keyword">
        <transition-group tag="ul" :css="false" 
            @before-enter="beforeEnter" @enter="enter" @leave="leave">
            <!-- 每个li绑定了data-index属性,该值用来计算当前li动画的delay延时  -->
            <li v-for="(item, index) in showNames" :key="item" :data-index="index">
                {{ item }}
            </li>
        </transition-group>
    </div>
</template>

<script>
import gsap from 'gsap';
export default {
    data() {
        return {
            names: ["abc", "cba", "nba", "hangge", "lilei", "hmm", "kobe", "james"],
            keyword: ""
        }
    },
    computed: {
        showNames() {
            return this.names.filter(item => item.indexOf(this.keyword) !== -1)
        }
    },
    methods: {
        // 进入前
        beforeEnter(el) {
            console.log('beforeEnter=>', el)
            el.style.opacity = 0;
            el.style.height = 0;
        },
        // 进入动画
        enter(el, done) {
            console.log('enter=>', el)
            gsap.to(el, {
                opacity: 1,
                height: "1.5em",
                // el.dataset.index 是拿到li元素上data-index绑定的属性的值
                delay: el.dataset.index * 0.1,
                onComplete: done
            })
        },
        // 离开动画
        leave(el, done) {
            console.log('leave=>', el)
            gsap.to(el, {
                opacity: 0,
                height: 0,
                delay: el.dataset.index * 0.1,
                onComplete: done
            })
        }
    }
}
</script>

(2)运行效果如下,当我们在输入框中输入或删除关键字时,在列表中移除或添加 <li> 元素都会有交替动画。 
评论

全部评论(0)

回到顶部