vue3-过渡&动画
一、动画基本使用
1.1 vue的transition动画
-
Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡:
- 条件渲染 (使用 v-if)条件展示 (使用 v-show)
- 动态组件
- 组件根节点
<template> <button @click="btnClick">显式/隐藏</button> <transition name="fade"> <h2 v-if="isShow">Hello Vue3</h2> </transition> </template> <script setup> import { ref } from "vue"; const isShow = ref(true); const btnClick = () => { isShow.value = !isShow.value; }; </script> <style scoped> .fade-enter-from, .fade-leave-to { opacity: 0; } .fade-enter-to, .fade-leave-from { opacity: 1; } .fade-enter-active, .fade-leave-active { transition: opacity 2s ease; } </style>
1.2 transition组件的原理
-
当插入或删除包含在 transition 组件中的元素时,Vue 将会做以下处理:
-
自动嗅探目标元素是否应用了CSS过渡或者动画,如果有,那么在恰当的时机添加/删除 CSS类名
-
如果 transition 组件提供了JavaScript钩子函数,这些钩子函数将在恰当的时机被调用
-
如果没有找到JavaScript钩子并且也没有检测到CSS过渡/动画,DOM插入、删除操作将会立即执行
-
-
事实上Vue就是帮助我们在class之间来回切换完成的动画:
-
v-enter-from:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除
-
v-enter-active:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数
-
v-enter-to:定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时 v-enter-from 被移除),在过渡/动画完成之后移除
-
v-leave-from:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除
-
v-leave-active:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数
-
v-leave-to:定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时 v-leave-from 被删除),在过渡/动画完成之后移除
-
-
class 添加时机和命名规则
- 添加时机
- class的name命名规则如下:
- 如果使用的是一个没有name的transition,那么所有的class是以
v-
作为默认前缀 - 如果添加了一个name属性,比如
<transtion name="fade">
,那么所有的class会以fade-
开头
- 如果使用的是一个没有name的transition,那么所有的class是以
1.3 css 动画
- 通过 animation 实现动画
<template> <div> <button @click="isShow = !isShow">切换</button> </div> <transition name="fade"> <h2 class="title" v-if="isShow"> 它们是一片朦胧的温馨与寂寥,是一片成熟的希望与绝望,它们的领地只有两处:心与坟墓。比如说邮票,有些是用于寄信的,有些仅仅是为了收藏。 </h2> </transition> </template> <script setup> import { ref } from "vue"; const isShow = ref(true); </script> <style scoped> .title { display: inline-block; } .fade-enter-active { animation: anim 1s ease; } .fade-leave-active { animation: leaveAnim 1s ease; } @keyframes anim { 0% { transform: scale(0); opacity: 0; } 50% { transform: scale(1.2); opacity: 0.5; } 100% { transform: scale(1); opacity: 1; } } @keyframes leaveAnim { 0% { transform: translateX(0); } 100% { transform: translateX(-500px); } } </style>
1.4 过渡和动画同时设置
-
Vue为了知道过渡的完成,内部是在监听 transitionend 或 animationend,到底使用哪一个取决于元素应用的CSS规则:如果我们只是使用了其中的一个,那么Vue能自动识别类型并设置监听
-
如果同时使用过渡和动画(开发中应该避免同时使用),并且某一个动画执行结束时,另外一个动画还没有结束
-
在这种情况下,我们可以设置 type 属性为 animation 或者 transition 来明确的告知Vue监听的类型
-
可以显示的来指定过渡的时间,通过 duration 属性
-
duration可以设置两种类型的值:
- number类型:同时设置进入和离开的过渡时间
- object类型:分别设置进入和离开的过渡时间
-
<template> <div> <button @click="isShow = !isShow">切换</button> </div> <transition name="fade" type="transition" :duration="{ enter: 800, leave: 1000 }" > <h2 class="title" v-if="isShow"> 它们是一片朦胧的温馨与寂寥,是一片成熟的希望与绝望,它们的领地只有两处:心与坟墓。比如说邮票,有些是用于寄信的,有些仅仅是为了收藏。 </h2> </transition> </template> <script setup> import { ref } from "vue"; const isShow = ref(true); </script> <style scoped> .title { display: inline-block; } .fade-enter-from, .fade-leave-to { opacity: 0; } .fade-enter-to, .fade-leave-from { opacity: 1; } .fade-enter-active, .fade-leave-active { transition: opacity 1s ease; } .fade-enter-active { animation: anim 1s ease; } .fade-leave-active { animation: leaveAnim 1s ease; } @keyframes anim { 0% { transform: scale(0); } 50% { transform: scale(1.2); } 100% { transform: scale(1); } } @keyframes leaveAnim { 0% { transform: translateX(0); } 100% { transform: translateX(-500px); } } </style>
-
1.5 过渡的模式 mode
-
在两个元素之间切换的时候会存在问题(有卡顿)
- 因为默认情况下进入和离开动画是同时发生的
-
如果我们不希望同时执行进入和离开动画,需要设置transition的过渡模式:
- in-out: 新元素先进行过渡,完成之后当前元素过渡离开
- out-in: 当前元素先进行过渡,完成之后新元素过渡进入
<transition name="fade" mode="out-in"> <h2 class="title" v-if="isShow"> 要是有些事我没说,地坛,你别以为是我忘了,我什么也没忘,但是有些事只适合收藏。不能说,也不能想,却又不能忘。 </h2> <h2 class="title" v-else> 它们不能变成语言,它们无法变成语言,一旦变成语言就不再是它们了。它们是一片朦胧的温馨与寂寥,是一片成熟的希望与绝望,它们的领地只有两处:心与坟墓。比如说邮票,有些是用于寄信的,有些仅仅是为了收藏。 </h2> </transition>
1.7 动态组件的切换
- 首次渲染的时候是没有动画的,如果希望添加上去动画,那么就可以增加另外一个属性
appear(布尔值),默认为false
<transition name="fade" mode="out-in" :appear="true"> <component :is="isShow ? Home : About"></component> </transition>
二、结合第三方库使用
2.1 animate.css库的使用
Animate.css是一个已经准备好的、跨平台的动画库为我们的web项目,对于强调、主页、滑动、注意力引导非常有用
2.1.1 安装及使用
- 安装:`npm install animate.css`
- 导入:`import "animate.css";`
- 两种用法:
- 用法一:直接使用animate库中定义的 keyframes 动画
- 用法二:直接使用animate库提供给的类
```html
<template>
<div class="app">
<button @click="isShow = !isShow">显式/隐藏</button>
<transition name="fade">
<h2 v-if="isShow">Hello Animate</h2>
</transition>
</div>
</template>
<script setup>
import { ref } from "vue";
const isShow = ref(true);
</script>
<style scoped>
.app {
width: 300px;
margin: 0 auto;
}
.title {
display: inline-block;
}
.fade-enter-active {
animation: bounceInLeft 1s ease-in;
}
.fade-leave-active {
animation: bounceInRight 1s ease-in reverse;
}
</style>
```
2.1.2 自定义过渡class
- enter-from-class
- enter-active-class
- enter-to-class
- leave-from-class
- leave-active-class
- leave-to-class
他们的优先级高于普通的类名,这对于 Vue 的过渡系统和其他第三方 CSS 动画库,如 Animate.css. 结合使用十分有用。
<transition
enter-active-class="animate__animated animate__fadeInDown"
leave-active-class="animate__animated animate__flipInY"
>
<h2 v-if="isShow">Hello Animate</h2>
</transition>
2.2 gsap库的使用
2.2.1 介绍
-
什么是gsap呢?
-
GSAP是The GreenSock Animation Platform(GreenSock动画平台)的缩写
-
它可以通过JavaScript为CSS属性、SVG、Canvas等设置动画,并且是浏览器兼容的
-
-
安装及使用
- 安装:
npm install gsap
- 导入:
import gsap from 'gsap';
- 安装:
2.2.2 钩子函数
<template>
<div class="app">
<button @click="isShow = !isShow">显式/隐藏</button>
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@afterLeave="afterLeave"
>
<h2 v-if="isShow">Hello Animate</h2>
</transition>
</div>
</template>
<script setup>
import { ref } from "vue";
const isShow = ref(true);
function beforeEnter() {
console.log("beforeEnter");
}
function enter() {
console.log("enter");
}
function afterEnter() {
console.log("afterEnter");
}
function beforeLeave() {
console.log("beforeLeave");
}
function leave() {
console.log("leave");
}
function afterLeave() {
console.log("afterLeave");
}
</script>
2.2.3 简单动画
-
当使用JavaScript来执行过渡动画时,需要进行 done 回调,否则它们将会被同步调用,过渡会立即完成。
-
添加 :css="false",也会让 Vue 会跳过 CSS 的检测,除了性能略高之外,这可以避免过渡过程中 CSS 规则的影响。
<template> <div class="app"> <button @click="isShow = !isShow">显式/隐藏</button> <transition @enter="enter" @leave="leave" :css="false"> <h2 v-if="isShow">Hello Animate</h2> </transition> </div> </template> <script setup> import { ref } from "vue"; import gsap from "gsap"; const isShow = ref(true); function enter(el, done) { console.log("enter"); gsap.from(el, { scale: 0, x: 200, onComplete: done, }); } function leave(el, done) { console.log("leave"); gsap.to(el, { scale: 0, x: 200, onComplete: done, }); } </script> <style scoped> .title { display: inline-block; } </style>
2.2.4 数字变化动画
-
数字增加或减少的动画
<template> <div class="app"> <input type="number" step="100" v-model="counter" /> <h2>当前计数: {{ showNumber.toFixed(0) }}</h2> </div> </template> <script setup> import { ref, watch } from "vue"; import gsap from "gsap"; const counter = ref(0); const showNumber = ref(0); watch(counter, (newVal) => { gsap.to(showNumber, { duration: 1, value: newVal, }); }); </script>
三、列表动画使用
3.1 列表的过渡
-
目前为止,过渡动画主要要是针对单个元素或者组件的:
-
要么是单个节点
-
要么是同一时间渲染多个节点中的一个
-
-
如果希望渲染的是一个列表,并且该列表中添加删除数据也希望有动画执行呢?
-
这个时候我们要使用 组件来完成
-
使用
<transition-group>
有如下的特点:-
默认情况下,它不会渲染一个元素的包裹器,但是可以指定一个元素并以 tag attribute 进行渲染;
-
过渡模式不可用,因为不再相互切换特有的元素;
-
内部元素总是需要提供唯一的 key attribute 值;
-
CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身;
-
-
3.2 基本动画
-
一列数字,可以继续添加或者删除数字:
在添加和删除数字的过程中,对添加的或者移除的数字添加动画
-
虽然新增的或者删除的节点是有动画的,但是对于那些其他需要移动的节点是没有动画的:
- 我们可以通过使用一个新增的 v-move 的class来完成动画;
- 它会在元素改变位置的过程中应用;
- 像之前的名字一样,我们可以通过name来自定义前缀
<template> <div> <button @click="addNum">添加数字</button> <button @click="removeNum">删除数字</button> <button @click="shuffleNum">打乱数字</button> <transition-group tag="div" name="fade"> <span v-for="item in nums" :key="item" class="item">{{ item }}</span> </transition-group> </div> </template> <script setup> import { ref } from "vue"; import { shuffle } from "underscore"; const nums = ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); const addNum = () => { nums.value.splice(randomIndex(), 0, nums.value.length); }; const removeNum = () => { nums.value.splice(randomIndex(), 1); }; const shuffleNum = () => { nums.value = shuffle(nums.value); }; const randomIndex = () => { return Math.floor(Math.random() * nums.value.length); }; </script> <style scoped> .item { margin-right: 10px; display: inline-block; } .fade-enter-from, .fade-leave-to { opacity: 0; transform: translateY(30px); } .fade-enter-active, .fade-leave-active { transition: all 2s ease; } .fade-leave-active { /* 防止移除时占位影响其他元素 */ position: absolute; } /* 针对其他移动的阶段需要的动画 */ .fade-move { transition: all 2s ease; } </style>
3.3 列表交错动画
-
通过gsap的延迟delay属性,做一个交替消失的动画
<template> <div> <input v-model="keyword" /> <transition-group tag="ul" name="fade" :css="false" @before-enter="beforeEnter" @enter="enter" @leave="leave" > <li v-for="(item, index) in showNames" :key="item" :data-index="index"> {{ item }} </li> </transition-group> </div> </template> <script setup> import { ref, computed } from "vue"; import gsap from "gsap"; const names = ref(["abc", "cba", "nba", "john", "lilei", "hmm", "kobe", "james"]); const keyword = ref(""); const showNames = computed(() => { return names.value.filter((item) => item.indexOf(keyword.value) !== -1); }); const beforeEnter = (el) => { el.style.opacity = 0; el.style.height = 0; }; const enter = (el, done) => { gsap.to(el, { opacity: 1, height: "1.5em", // 每一项单独动画 delay: el.dataset.index * 0.5, onComplete: done, }); }; const leave = (el, done) => { gsap.to(el, { opacity: 0, height: 0, // 每一项单独动画 delay: el.dataset.index * 0.5, onComplete: done, }); }; </script>
转载自:https://juejin.cn/post/7128665458500173837