动画基本使用
本文是依据
vue中文文档,再结合自己的理解,使用vue3的setup语法糖敲一遍代码,仅供学习记录和参考
类名+过渡实现
用transition包裹单元素(或者多个元素只渲染一个,或动态组件),添加name,再定义样式类名,vue会帮助你在特定时间给被包裹元素添加类名,类名如下:
v-enter-from:进入过渡开始时的状态,在元素被插入之后立马生效,打开浏览器调试查看元素时基本上看不到此类名;v-enter-active:进入过渡生效时的状态,过渡的整个阶段都存在,过渡完成后立马移除,这个类常用于定义添加css属性transition来实现平缓过渡效果;v-enter-to:进入过渡结束的状态,元素被插入后的下一帧就生效,通过定义的css属性transition和v-enter-from定义的可平缓过渡的不同样式来实现平缓过渡效果;v-leave-from:离开过渡开始时的状态,在元素即将要被移除时生效;v-leave-active:离开过渡生效时的状态,与v-enter-active同理;v-leave-to:离开过渡的结束状态,与v-enter-to同理,到达这个状态之后,将立马移除元素。
一般情况下,
v-enter-active和v-leave-active的css代码一样,都是添加css样式transition实现平缓过渡;v-enter-from和v-leave-to的css代码一样,都是定义元素消失时的状态;v-enter-to和v-leave-from的css代码一样,都是定义元素存在时的状态
<button @click="show = !show">toggle</button>
<transition name="noice">
<h3 v-if="show">hello world</h3>
</transition>
……
<style>
.noice-enter-active,
.noice-leave-active {
transition: all 0.3s linear;
}
.noice-enter-from,
.noice-leave-to {
opacity: 0;
}
.noice-enter-to,
.noice-leave-from {
opacity: 1;
}
</style>
官方配图很形象
类名+动画实现
可通过自定义动画@keyframes name来定义动画来实现动画效果
<style>
.noice-enter-active {
animation: bounce-in 1s;
}
.noice-leave-active {
animation: bounce-in 1s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.25);
}
100% {
transform: scale(1);
}
}
</style>
自定义类名
此处按照官网来使用常用的animate.css样式库
// vue3中
yarn add animate.css
// main.js中直接引入即可用
import 'animate.css';
开始使用
<template>
<button @click="show = !show">toggle</button>
<transition
enter-active-class="animate__animated animate__tada"
leave-active-class="animate__animated animate__bounceOutRight"
>
<h3 v-if="show">hello world</h3>
</transition>
</template>
同时使用过渡和动画
Vue本身有帮助监听过渡或者动画结束的功能,但是如果同时使用了过渡和动画,监听可能会出现异常,这时就需要告诉Vue要监听是动画结束还是过渡结束,通过给标签设置type来控制,可选值为animation和transition
<transition type="animation">
<h3 v-if="show">hello world</h3>
</transition>
显性的过渡持续时间
Vue可自动得出过渡完成的时间,但可能在某些情况下如定义了一系列的过渡骚操作,可能会导致过渡完成效果不如预期,此时可通过定义duration来自定义时间,毫秒为单位
<transition :duration="1000">
<h3 v-if="show">hello world</h3>
</transition>
js钩子
可以声明钩子函数,会在对应时间执行该函数,可以配合过渡或者动画使用。需注意enter和leave钩子需手动执行done回调,否则会视为直接过渡完成。Vue默认会添加CSS类名,如果未使用类名时,可添加:class="false"来关闭添加对应类名,略微提高性能且避免类名所带来影响
before-enter:开始进入之前触发enter:开始进入时触发after-enter:开始进入之后触发enter-cancelled:进入完成之后触发
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
:css="false"
>
...
</transition>
js钩子结合gsap库实现过渡
gsap是一个js动画框架,此处配合js钩子实现过渡动画,了解更多gsap后可实现更炫酷的动画
此处立下
flag,后面要学习一下这个
先安装
yarn add gsap -S
再使用
<transition
@before-enter="before"
@enter="enter"
@leave="leave"
:css="false"
>
</transition>
<script setup>
const before = el => {
gsap.set(el, {
opacity: 0,
});
};
const enter = (el, done) => {
gsap.to(el, {
opacity: 1,
onComplete: done,
});
};
const leave = (el, done) => {
gsap.to(el, {
opacity: 0,
onComplete: done,
});
};
</script>
其他属性
-
初始渲染的过渡
appear默认情况下,在初次渲染时,元素不会执行定义好的过渡效果,添加
appear属性来指定初次渲染也执行过渡效果,<transition appear> -
过渡模式
mode当定义的过渡是两个组件相互过渡时会出现一个问题,每次过渡时都是新的元素先过渡出现,完了之后已有元素才离开,效果看起来特别不合适,
Vue提供了一个过渡模式,有两个可选项in-out:新元素先进行进入过渡,完成之后当前已有元素才过渡离开out-in:当前元素先过渡离开,完成之后新元素再过渡进入
指定
<transition mode="out-in">即可实现旧元素先离开新元素再进场的效果
列表过渡
列表的进入/离开
将<transition>改为<transition-group>,还是添加之前的类名
<template>
<div id="list-demo">
<button @click="add">添加</button>
<button @click="remove">移除</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item" class="list-item">
{{ item }}
</li>
</transition-group>
</div>
</template>
<script setup>
import { ref } from 'vue';
const items = ref([1, 2, 3, 4, 5, 6]);
let nextNum = items.value.length + 1;
function random () {
return Math.floor(Math.random() * items.value.length);
};
function add () {
items.value.splice(random(), 0, nextNum++);
// items.value.push(random())
};
function remove () {
items.value.splice(random(), 1);
};
</script>
<style lang="scss" scoped>
.list-enter-active,
.list-leave-active {
transition: all 0.3s;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>
列表的移动过渡
给<transition-group>添加name-move类可以为定位的元素添加移动过渡,两个需要注意的点,一是给每一个元素添加定位,二是给对应的name-move添加过渡样式,此处引用lodash库的shuffle方法实现重新排序
<button @click="shuffle">重排</button>
<script setup>
import _ from 'lodash';
const shuffle = () => {
items.value = _.shuffle(items.value);
};
</script>
<style>
.list-item {
position: relative;
}
</style>
列表的交错过渡
配合gsap的动画延迟delay,可实现列表的依次出现或消失
<template>
<div id="list-demo">
<input type="text" v-model="key">
<transition-group
name="list"
tag="ul"
:css="false"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<li v-for="(item, i) of showList" :key="item" :data-i="i">{{ item }}</li>
</transition-group>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { gsap } from 'gsap';
const items = ref(['Li Ming', 'Jery', 'Honey', 'Jim', 'Tim', 'Tony']);
const key = ref('');
const showList = computed(() => {
return items.value.filter(item => {
return item.toLowerCase().indexOf(key.value.toLocaleLowerCase()) !== -1;
})
});
const beforeEnter = el => {
el.style.opacity = 0
el.style.height = 0
};
const enter = (el, done) => {
gsap.to(el, {
opacity: 1,
height: '1.4em',
delay: el.dataset.i * 0.1,
onComplete: done,
});
};
const leave = (el, done) => {
gsap.to(el, {
opacity: 0,
height: 0,
delay: el.dataset.i * 0.1,
onComplete: done,
});
};
</script>
可复用过渡
封装一个组件,用过渡标签包裹根元素,过渡标签内加上插槽,在组件内定义统一的过渡效果,父组件使用时传入插槽内容即可
- 封装子组件
TheTransition.vue
<template>
<transition name="noice" mode="out-in" appear>
<slot></slot>
</transition>
</template>
<script setup>
</script>
<style lang="scss" scoped>
.noice-leave-active,
.noice-enter-active {
transition: all 0.3s;
}
.noice-leave-to,
.noice-enter-from {
opacity: 0;
}
</style>
- 父组件内使用
<button @click="show = !show">toggle</button>
<the-transition>
<h3 v-show="show">阿西</h3>
</the-transition>
动态过渡
过渡标签上面的属性都是可以进行动态绑定的,而且js钩子中亦可使用js变量,这就代表着过渡有着极高的灵活性,官网目前没有完整的示例,在此不做实例,有需要时再做研究
唯一的限制是你的想象力。
状态过渡
数字状态过渡
结合gsap库,可实现数字唰唰唰改变的效果
<template>
<div>
<input type="number" v-model="value">
<h3>{{ showVal.toFixed(0) }}</h3>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import { gsap } from 'gsap';
const value = ref(0);
const showVal = ref(0);
watch(value, val => {
console.log(val)
gsap.to(showVal, {
duration: 0.3,
value: val,
})
});
</script>
将过渡封装成组件
思路是将每个可能改变的数字封装进组件,组件内部监听数字改变,执行gsap的方法
- 子组件
GsapShua.vue
<template>
<span>
{{ showVal.toFixed(0) }}
</span>
</template>
<script setup>
import { ref, watch } from 'vue';
import { gsap } from 'gsap';
const props = defineProps({
value: '',
});
const showVal = ref(props.value);
watch(() => props.value, val => {
gsap.to(showVal, {
duration: 0.3,
value: val,
})
});
</script>
- 父组件使用
<template>
<div>
<input type="number" v-model="num1" /> +
<input type="number" v-model="num2" /> = {{ total }}
<br />
<GsapShua :value="num1" /> + <GsapShua :value="num2" /> = <GsapShua :value="total" />
</div>
</template>
<script setup>
import { computed, ref, watch } from 'vue';
import GsapShua from '@/components/GsapShua.vue';
const num1 = ref(0);
const num2 = ref(0);
const total = computed(() => {
return num1.value + num2.value;
})
</script>
另外两个看着比较高级
复杂的demo就不做实现了