前言:
这篇文章是基于vue过渡&动画&Animate.css动画库(1)
继续编写的。
初始渲染时的过渡
简介
可以通过 appear
设置节点在初始渲染的过渡
<transition appear>
<!-- ... -->
</transition>
案例:
<div class="a1">
<button @click="but=!but">点我 </button>
<transition enter-active-class="animate__animated animate__rollIn"
leave-active-class="animate__animated animate__rollOut" appear>
<div class="box" v-if="but"></div>
</transition>
</div>
效果:(刷新页面初始化渲染)
单组件的过渡
简介
在<transition>中的元素为一个子组件
案例
父组件中定义:
<div>
<button @click="but=!but">点我 </button>
<transition enter-active-class="animate__animated animate__rollIn"
leave-active-class="animate__animated animate__rollOut" appear>
<div class="box" v-if="but"></div>
</transition>
</div>
子组件:
<template>
<div class="about"></div>
</template>
<style lang="less" scoped>
.about {
width: 200px;
height: 200px;
background-color: red;
}
</style>
效果:
多个元素的过渡
简介
对于原生标签可以使用 v-if
/v-else
。最常见的多标签过渡是一个列表和描述这个列表为空消息的元素
案例
<template>
<div>
<button @click="add">Add</button>
<div>
<transition name="fade">
<ul v-if="items.length > 0" key="ul">
<li v-for="(item, index) in items" :key="index">{{ item }}</li>
</ul>
<p key="empty" v-else>数组中没有任何元素</p>
</transition>
</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [],
};
},
methods: {
add() {
this.items.push(4);
},
},
};
</script>
<style scoped>
.fade-enter-active {
position: absolute;
animation: bounceInLeft 1s;
}
.fade-leave-active {
position: absolute;
animation: bounceOutRight 1s;
}
@keyframes bounce-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
效果:
注意: 当有相同标签名的元素切换时,需要通过 key
attribute 设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在 <transition>
组件中的多个元素设置 key 是一个更好的实践。
过渡模式
-
in-out
:新元素先进行过渡,完成之后当前元素过渡离开。 -
out-in
:当前元素先进行过渡,完成之后新元素过渡进入。
案例2
<template>
<div>
<div>
<button @click="payMethod = 'wechat'">微信支付</button>
<button @click="payMethod = 'alipay'">支付宝支付</button>
</div>
<transition name="fade">
<img :key="payMethod" :src="payMethod === 'wechat' ? wechat : alipay" alt="" />
</transition>
</div>
</template>
<script>
export default {
data() {
return {
payMethod: "wechat",
wechat:
"https://img1.baidu.com/it/u=1320610702,3769627335&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=317",
alipay:
"https://img2.baidu.com/it/u=3840063115,3619456710&fm=253&fmt=auto&app=138&f=JPEG?w=751&h=500",
};
},
};
</script>
<style scoped>
.fade-enter-active {
/* animation: bounceInLeft 1s; */
animation: bounce-in 1s;
}
.fade-leave-active {
/* animation: bounceOutRight 1s; */
animation: bounce-in 1s reverse;
}
@keyframes bounce-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
效果:
此时的效果时非常不美好的, 两个img都被重绘了,一个离开过渡的时候另一个开始进入过渡。这是 <transition>
的默认行为 - 进入和离开同时发生。
我们可以使用out-in过渡模式:
<transition name="fade" mode="out-in">
<img :key="payMethod" :src="payMethod === 'wechat' ? wechat : alipay" alt="" />
</transition>
效果:
多组件过渡
简介
多个组件的过渡简单很多 - 我们不需要使用 key
attribute。相反,我们只需要使用动态组件:
案例
<template>
<div>
<nav>
<input type="radio" name="" id="man" value="Man" v-model="gender" />
<label for="man">男</label>
<input type="radio" name="" id="woman" value="Woman" v-model="gender" />
<label for="woman">女</label>
</nav>
<div>
<transition name="fade" mode="out-in">
<component :is="gender"></component>
</transition>
</div>
</div>
</template>
<script>
import Man from "./Man.vue";
import Woman from "./Woman.vue";
export default {
data() {
return {
gender: "Man",
};
},
components: {
Man,
Woman,
},
};
</script>
<style scoped>
.fade-enter-active {
/* animation: bounceInLeft 1s; */
animation: bounce-in 1s;
}
.fade-leave-active {
/* animation: bounceOutRight 1s; */
animation: bounce-in 1s reverse;
}
@keyframes bounce-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
子组件Woman
<template>
<h1>我是女孩</h1>
</template>
子组件Man
<template>
<h1>我是男孩</h1>
</template>
效果:
列表的过渡
简介
如何同时渲染整个列表(<transition>内有多个元素),比如使用 v-for
?在这种场景中,使用 <transition-group>
组件。在我们深入例子之前,先了解关于这个组件的几个特点:
- 不同于
<transition>
,它会以一个真实元素呈现:默认为一个<span>
。你也可以通过tag
attribute 更换为其他元素。 - 过渡模式不可用,因为我们不再相互切换特有的元素。
- 内部元素总是需要提供唯一的
key
attribute 值。 - CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
列表的进入/离开过渡
let app = new Vue({
el: '#app',
data: {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
nextNum: 10
},
methods: {
randomIndex: function () {
return Math.floor(Math.random() * this.items.length)
},
add: function () {
this.items.splice(this.randomIndex(), 0, this.nextNum++)
},
remove: function () {
this.items.splice(this.randomIndex(), 1)
},
}
})
<div id="list-demo" class="demo">
<button v-on:click="add">Add</button>
<button v-on:click="remove">Remove</button>
<transition-group name="list" tag="p">
<span v-for="item in items" v-bind:key="item" class="list-item">
{{ item }}
</span>
</transition-group>
</div>
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active,
.list-leave-active {
transition: all 1s;
}
.list-enter,
.list-leave-to {
opacity: 0;
transform: translateY(30px);
}
效果
v-move
简介
从上面例子有个问题,当添加和移除元素的时候,周围的元素会瞬间移动到他们的新布局的位置,而不是平滑的过渡,使用 v-move 类就可以解决这个问题:
利用v-move现实过渡增加删除
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active,
.list-leave-active {
transition: all 1s;
}
.list-enter,
.list-leave-to {
opacity: 0;
transform: translateY(30px);
}
//控制删除时的动画效果
.list-leave-active {
position: absolute;
}
//控制添加时的动画效果
.list-move {
transition: transform 1s;
}
效果(是不是这样看起来更加丝滑了很多)
利用v-move现实乱序并过渡
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
<div id="flip-list-demo" class="demo">
<button v-on:click="shuffle">Shuffle</button>
<transition-group name="flip-list" tag="ul">
<li v-for="item in items" v-bind:key="item">
{{ item }}
</li>
</transition-group>
</div>iv>
data: {
items: [1, 2, 3, 4, 5, 6, 7, 8, 9],
},
methods: {
shuffle: function () {
this.items = _.shuffle(this.items)
}
}
.flip-list-move {
transition: transform 1s;
}
效果:
这个看起来很神奇,这个案例引用了 Lodash.js 中的 _.shuffle()方法打乱了列表各个元素顺序,加上v-move使用 transforms 将元素从之前的位置平滑过渡新的位置。关于Lodash.js的使用官网写的很详细,可以自己去看。
利用v-move现实图片乱序过渡
当然这个我们可以利用上个案例所学做出更加炫酷的效果(和上面代码换汤不换药)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
<div id="flip-list-demo" class="demo">
<button v-on:click="shuffle">Shuffle</button>
<transition-group name="flip-list" tag="div">
<img :src="item" alt="" v-for="item in items" :key="item">
</transition-group>
</div>
data: {
items: [
'./imgs/1.webp',
'./imgs/2.webp',
'./imgs/3.webp',
'./imgs/4.webp',
'./imgs/5.webp',
'./imgs/6.webp',
'./imgs/7.webp',
'./imgs/8.webp',
'./imgs/9.webp',
],
},
methods: {
shuffle: function () {
this.items = _.shuffle(this.items)
}
}
img {
width: 200px;
height: 200px;
}
.flip-list-move {
transition: transform 1s;
}
.demo div {
width: 600px;
}
效果:
多维网格过渡
这里需要注意key的唯一性,许多同学在这里栽坑
<div id="list-demo" class="demo">
<button v-on:click="shuffle">Shuffle</button>
<transition-group name="list" tag="div" class="list-div">
<div v-for="item in items" v-bind:key="item" class="list-item">{{ item[0]}}</div>
</transition-group>
</div>
data: {
items: [[1, 1], [2, 1], [3, 1], [4, 1], [2, 2], [1, 2], [4, 2], [3, 2], [4, 3], [2, 3], [3, 3], [1, 3], [3, 4], [4, 4], [1, 4], [2, 4]]
},
methods: {
shuffle: function () {
this.items = _.shuffle(this.items)
}
}
.list-item {
display: inline-block;
height: 20px;
width: 20px;
border: 1px solid #ccc;
}
.list-enter-active,
.list-leave-active {
transition: all 1s;
}
.list-enter,
.list-leave-to {
opacity: 0;
}
.list-div {
width: 90px;
}
.list-move{
transition: transform 1s;
}
效果:
需要注意的是使用 FLIP 过渡的元素不能设置为 display: inline
。作为替代方案,可以设置为 display: inline-block
或者放置于 flex 中
状态过渡
简介
Vue 的过渡系统提供了非常多简单的方法设置进入、离开和列表的动效。那么对于数据元素本身的动效呢,比如:
- 数字和运算
- 颜色的显示
- SVG 节点的位置
- 元素的大小和其他的 property
这些数据要么本身就以数值形式存储,要么可以转换为数值。有了这些数值后,我们就可以结合 Vue 的响应式和组件系统,使用第三方库来实现切换元素的过渡状态
gsap.js
简介:一个“轻量级”、“高效率”、强大的2D动画引擎
先介绍一个 gsap 基础函数:
gsap.to(targets, second, options)
targets :元素节点或者选择器字符串(如 .className )
second :动画执行秒数
options :动画末状态的配置对象(所有可用选项详见 CSSPlugin )
案例:
<div>
<input v-model.number="number" type="number" step="20">
<p>{{ animatedNumber }}</p>
</div>
data: {
number: 0,
tweenedNumber: 0
},
computed: {
animatedNumber: function () {
return this.tweenedNumber.toFixed(0);
}
},
watch: {
number: function (newValue) {
gsap.to(this.$data, 0.5, { tweenedNumber: newValue });
}
}
效果:
路由过渡
简介
简单来说就是路由之间的过渡效果与组件过渡差不多,话不多说,上案例
案例
先看router, 在B路由中有两个子路由:A,C
B路由展示:
<template>
<div>
<router-link to="/A">点击进入A路由</router-link>
<router-link to="/C">点击进入C路由</router-link>
<transition appear mode="out-in">
<router-view></router-view>
</transition>
</div>
</template>
<script>
export default {};
</script>
<style lang="less" scoped>
a {
margin: 0 60px;
}
.v-enter-active {
animation: fade 0.5s;
}
.v-leave-active {
animation: fade 0.5s reverse;
}
@keyframes fade {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
</style>
A,C路由展示:
<template>
<div key="diaa">我是AAAAA</div>
</template>
<template>
<div key="diaa">我是CCCCC</div>
</template>
效果: