前置知识
Vue 提供了 transition 的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡:
- 条件渲染 (使用 v-if)
- 条件展示 (使用 v-show)
- 动态组件
- 组件根节点
自定义 transition 过度效果,你需要对transition组件的name属性自定义。并在css中写入对应的样式
web 动画库-CSDN博客文章浏览阅读2次。动画领域有一个比较知名的CSS库:Animate.css,它提供了60多种动画,满足一般网页的需求,比如淡入淡出、闪现等等一系列日常动画,不过虽然它能满足日常需求,但是一些复杂的场景就需要靠JS手动去操作,比如界面滚动到某个元素才开始播放动画,比如拖拽、比如滚动界面时,动态调整元素就需要使用到GreenSockhttps://blog.csdn.net/qq_37550440/article/details/142390818?sharetype=blogdetail&sharerId=142390818&sharerefer=PC&sharesource=qq_37550440&spm=1011.2480.3001.8118
安装依赖包
npm i @types/lodash lodash
npm i animate.css
npm i gsap
1.过渡的类名 name
在进入/离开的过渡中,会有 6 个 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 被移除),在过渡/动画完成之后移除。
<template>
<h2>transition name基础用法</h2>
<button @click="flag0 = !flag0">切换0</button>
<transition
:duration="{ enter: 500, leave: 800 }"
name="fade0"
>
<div class="box" v-if="flag0"></div>
</transition>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from "vue";
let flag0 = ref(false);
</script>
<style lang="less" scoped>
.box {
width: 200px;
height: 200px;
background-color: pink;
}
// 切换0
//进入之前
.fade0-enter-from {
width: 0px;
height: 0px;
}
//进入过程
.fade0-enter-active {
transition: all 2s ease;
}
// 最后一帧
.fade0-enter-to {
//进入完成最好和box保持一致
width: 200px;
height: 200px;
background: red;
}
.fade0-leave-from {
//离开之前
width: 200px;
height: 200px;
}
.fade0-leave-active {
//离开过程
transition: all 3s linear;
}
.fade0-leave-to {
//离开的最后一帧
width: 0px;
height: 0px;
}
</style>
2.自定义过渡 class 类名
trasnsition props
enter-from-class
enter-active-class
enter-to-class
leave-from-class
leave-active-class
leave-to-class
<template>
<button @click="flag = !flag">切换1</button>
<h2>
transition 自定义过渡 class 类名
每个都对应一个类名,与name的区别是能结合第三方的内库使用
</h2>
<transition
name="fade"
enter-from-class="e-from"
enter-active-class="e-active"
enter-to-class="e-to"
>
<div class="box" v-if="flag"></div>
</transition>
</template>
<script setup lang="ts">
import { ref, reactive, watch } from "vue";
let flag = ref(false);
</script>
<style lang="less" scoped>
// 切换1样式
.fade-enter-from, //进入之前
.e-from {
width: 0px;
height: 0px;
}
.fade-enter-active, //进入过度
.e-active {
transition: all 2.5s ease;
transform: rotate(360);
}
.fade-enter-to, // 进入完成
.e-to {
width: 200px;
height: 200px;
}
.fade-leave-from {
// 离开之前
width: 200px;
height: 200px;
}
.fade-leave-active {
//离开过度
transition: all 2.5s ease;
transform: rotate(360);
}
.fade-leave-to {
// 离开完成
width: 20px;
height: 20px;
}
</style>
3 自定义class结合animate.css案例
<template>
<h2>transition 结合animate使用, 更丝滑</h2>
<button @click="flag2 = !flag2">animate切换2</button>
<transition
:duration="{ enter: 500, leave: 800 }"
leave-active-class="animate__animated animate__bounceInLeft"
enter-active-class="animate__animated animate__bounceInRight"
>
<div class="box" v-if="flag2"></div>
</transition>
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
let flag2 = ref(false);
</script>
<style lang="less" scoped></style>
4.transition 生命周期8个
<template>
<h2>transition 的8个生命周期函数</h2>
<button @click="flag3 = !flag3">切换3</button>
<transition
@before-enter="beforeEnter"
@enter="enterActive"
@after-enter="enterTo"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leaveActive"
@after-leave="leaveTo"
@leave-cancelled="leaveCancelled"
>
<div class="box" v-if="flag3"></div>
</transition>
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
let flag0 = ref(false);
let beforeEnter = (el: Element) => {
//对应enter-from
console.log("进入之前", el);
};
let enterActive = (el: Element, done: Function) => {
//对应enter-active
console.log("过度曲线");
setTimeout(() => {
done();
}, 3000);
};
let enterTo = (el: Element) => {
//对应enter-to
console.log("进入完成");
};
let enterCancelled = () => {
//多点击几次
console.log("过度效果被打断");
//显示过程被打断
};
let beforeLeave = () => {
//对应leave-from
console.log("离开之前");
};
let leaveActive = (el: Element, done: Function) => {
//对应enter-activem
console.log("离开过度曲线");
setTimeout(() => {
done();
}, 3000);
};
let leaveTo = () => {
//对应leave-to
console.log("离开完成");
};
let leaveCancelled = () => {
//离开过度打断
};
</script>
<style lang="less" scoped></style>
5 transition生命周期与gsap结合使用案例
<template>
<h2>
transition生命周期钩子函数 与gsap 结合案例 npm i gsap
最健全的web动画库之一, 更丝滑
</h2>
<button @click="flag4 = !flag4">gsap切换4</button>
<transition
@before-enter="beforeEnter1"
@enter="enter1"
@leave="leave1"
>
<div class="box" v-if="flag4"></div>
</transition>
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
let flag4 = ref(false);
let beforeEnter1 = (el: Element) => {
gsap.set(el, {
width: 0,
height: 0,
});
};
let enter1 = (el: Element, done: gsap.Callback) => {
gsap.to(el, {
width: 200,
height: 200,
onComplete: done,
});
};
let leave1 = (el: Element, done: gsap.Callback) => {
gsap.to(el, {
width: 0,
height: 0,
onComplete: done,
});
};
</script>
<style lang="less" scoped></style>
6 appear自动加载的动画可用于大屏使用
<template>
<h2>transition 之appear首次页面加载完成后的动画 案例</h2>
<transition
appear
appear-from-class="appear-from"
appear-active-class="appear-active"
appear-to-class="appear-to"
>
<div class="box"></div>
</transition>
<hr />
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
</script>
<style lang="less" scoped>
.appear-from {
width: 0;
height: 0;
}
.appear-active {
transition: all 2.5s ease;
}
.appear-to {
width: 200px;
height: 200px;
}
</style>
7 transition 之appear与结合animate使用 案例
<template>
<h2>transition 之appear与结合animate使用 案例</h2>
<transition
appear
appear-active-class="animate__animated animate__bounceInRight"
>
<div class="box"></div>
</transition>
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
let flag0 = ref(false);
</script>
<style lang="less" scoped></style>
transition-group
8 transition-group过度列表
单个节点
多个节点,每次只渲染一个
那么怎么同时渲染整个列表,比如使用 v-for?在这种场景下,我们会使用 <transition-group> 组件。在我们深入例子之前,先了解关于这个组件的几个特点:
默认情况下,它不会渲染一个包裹元素,但是你可以通过 tag attribute 指定渲染一个元素。
过渡模式不可用,因为我们不再相互切换特有的元素。
内部元素总是需要提供唯一的 key attribute 值。
CSS 过渡的类将会应用在内部的元素中,而不是这个组/容器本身。
<template>
<h2>transition group使用</h2>
<button @click="add">add</button>
<button @click="pop">pop</button>
<div class="group_wraps">
<transition-group
enter-active-class="animate__animated animate__bounceInRight"
leave-active-class="animate__animated animate__bounceInLeft"
>
<div v-for="(item, index) in list" :key="index">
{{ item }}
</div>
</transition-group>
</div>
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
let flag0 = ref(false);
let list = reactive<number[]>([1, 2, 3, 4]);
let add = () => {
list.push(list.length + 1);
};
let pop = () => {
list.pop();
};
</script>
<style lang="less" scoped></style>
9 列表的(平移)移动过渡 move-class
<transition-group> 组件还有一个特殊之处。除了进入和离开,它还可以为定位的改变添加动画。只需了解新增的 v-move 类就可以使用这个新功能,它会应用在元素改变定位的过程中。像之前的类名一样,它的前缀可以通过 name attribute 来自定义,也可以通过 move-class attribute 手动设置
<template>
<h2>
transition group(底层是aerotwist FLIP这个动画库实现的)
列表过渡动画案例 平移过度move-class npm i @types/lodash
lodash
</h2>
<button @click="random">random</button>
<transition-group
tag="div"
class="wraps"
move-class="group_move"
enter-active-class="animate__animated animate__bounceInRight"
leave-active-class="animate__animated animate__bounceInLeft"
>
<div class="item" v-for="item in data" :key="item.id">
{{ item.number }}
</div>
</transition-group>
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
//区别 new Array(81)得到的是[空属性(Empty attribute) × 81] 与 Array.apply(null,[1,2,3]) 第一个参数是this指向,第二个参数是数组
// ts检测到第二个参数不是数组我们要假装他是一个数组用as number[]
/*
list = ['a','b']
list.map((item,index)=>{ // 第一个参数是item,第二个是index
console.log(item) a ,b
console.log(index) 0 ,1
})
1%1 = 0
1%9 = 1
1%10 = 1
*/
let data = ref(
Array.apply(null, {
length: 81,
} as number[]).map((_, index) => {
return {
id: index, // 作为key
number: (index % 9) + 1, // 从1开始 1-9*1-9的组合
};
})
);
let random = () => {
data.value = _.shuffle(data.value);
};
let num = reactive({
currentNum: 0,
gsapNum: 0,
});
watch(
() => num.currentNum,
(newVal) => {
gsap.to(num, {
duration: 1,
gsapNum: newVal,
});
}
);
</script>
<style lang="less" scoped>
.wraps {
display: flex;
flex-wrap: wrap;
width: calc(20px * 10 + 10px); // 整个父级的宽度,合理换行
.item {
width: 20px;
height: 20px;
border: 1px solid #ccc;
list-style-type: none;
display: flex;
justify-content: center;
align-items: center;
}
}
.group_move {
transition: all 0.8s ease;
}
</style>
10 状态过度 借助gsap库案例
<template>
<h2>状态过度 借助gsap库</h2>
<h3>
Vue 也同样可以给数字 Svg 背景颜色等添加过度动画
今天演示数字变化
</h3>
<input v-model="num.currentNum" type="number" step="20" />
<div>{{ num.gsapNum.toFixed(0) }}</div>
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
let num = reactive({
currentNum: 0,
gsapNum: 0,
});
watch(
() => num.currentNum,
(newVal) => {
gsap.to(num, {
duration: 1,
gsapNum: newVal,
});
}
);
</script>
<style lang="less" scoped>
.wraps {
display: flex;
flex-wrap: wrap;
width: calc(20px * 10 + 10px); // 整个父级的宽度,合理换行
.item {
width: 20px;
height: 20px;
border: 1px solid #ccc;
list-style-type: none;
display: flex;
justify-content: center;
align-items: center;
}
}
.group_move {
transition: all 0.8s ease;
}
</style>
完整实例代码
<template>
<h2>transition name基础用法</h2>
<button @click="flag0 = !flag0">切换0</button>
<transition
:duration="{ enter: 500, leave: 800 }"
name="fade0"
>
<div class="box" v-if="flag0"></div>
</transition>
<hr />
<button @click="flag = !flag">切换1</button>
<h2>
transition 自定义过渡 class 类名
每个都对应一个类名,与name的区别是能结合第三方的内库使用
</h2>
<transition
name="fade"
enter-from-class="e-from"
enter-active-class="e-active"
enter-to-class="e-to"
>
<div class="box" v-if="flag"></div>
</transition>
<hr />
<!--transition 结合animat使用 npm i animate.css -->
<h2>transition 结合animate使用, 更丝滑</h2>
<button @click="flag2 = !flag2">animate切换2</button>
<transition
:duration="{ enter: 500, leave: 800 }"
leave-active-class="animate__animated animate__bounceInLeft"
enter-active-class="animate__animated animate__bounceInRight"
>
<div class="box" v-if="flag2"></div>
</transition>
<hr />
<h2>transition 的8个生命周期函数</h2>
<button @click="flag3 = !flag3">切换3</button>
<transition
@before-enter="beforeEnter"
@enter="enterActive"
@after-enter="enterTo"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leaveActive"
@after-leave="leaveTo"
@leave-cancelled="leaveCancelled"
>
<div class="box" v-if="flag3"></div>
</transition>
<hr />
<h2>
transition生命周期钩子函数 与gsap 结合案例 npm i gsap
最健全的web动画库之一, 更丝滑
</h2>
<button @click="flag4 = !flag4">gsap切换4</button>
<transition
@before-enter="beforeEnter1"
@enter="enter1"
@leave="leave1"
>
<div class="box" v-if="flag4"></div>
</transition>
<hr />
<h2>transition 之appear首次页面加载完成后的动画 案例</h2>
<transition
appear
appear-from-class="appear-from"
appear-active-class="appear-active"
appear-to-class="appear-to"
>
<div class="box"></div>
</transition>
<hr />
<h2>transition 之appear与结合animate使用 案例</h2>
<transition
appear
appear-active-class="animate__animated animate__bounceInRight"
>
<div class="box"></div>
</transition>
<hr />
<h2>transition group使用</h2>
<button @click="add">add</button>
<button @click="pop">pop</button>
<div class="group_wraps">
<transition-group
enter-active-class="animate__animated animate__bounceInRight"
leave-active-class="animate__animated animate__bounceInLeft"
>
<div v-for="(item, index) in list" :key="index">
{{ item }}
</div>
</transition-group>
</div>
<hr />
<h2>
transition group(底层是aerotwist FLIP这个动画库实现的)
列表过渡动画案例 平移过度move-class npm i @types/lodash
lodash
</h2>
<button @click="random">random</button>
<transition-group
tag="div"
class="wraps"
move-class="group_move"
enter-active-class="animate__animated animate__bounceInRight"
leave-active-class="animate__animated animate__bounceInLeft"
>
<div class="item" v-for="item in data" :key="item.id">
{{ item.number }}
</div>
</transition-group>
<hr />
<h2>状态过度 借助gsap库</h2>
<h3>
Vue 也同样可以给数字 Svg 背景颜色等添加过度动画
今天演示数字变化
</h3>
<input v-model="num.currentNum" type="number" step="20" />
<div>{{ num.gsapNum.toFixed(0) }}</div>
</template>
<script setup lang="ts">
import "animate.css";
import gsap from "gsap";
import _ from "lodash"; // 报错的话就需要安装ts的声明文件 Npm I @type/lodash -D
import { ref, reactive, watch } from "vue";
let flag0 = ref(false);
let flag = ref(false);
let flag2 = ref(false);
let flag3 = ref(false);
let flag4 = ref(false);
let list = reactive<number[]>([1, 2, 3, 4]);
//区别 new Array(81)得到的是[空属性(Empty attribute) × 81] 与 Array.apply(null,[1,2,3]) 第一个参数是this指向,第二个参数是数组
// ts检测到第二个参数不是数组我们要假装他是一个数组用as number[]
/*
list = ['a','b']
list.map((item,index)=>{ // 第一个参数是item,第二个是index
console.log(item) a ,b
console.log(index) 0 ,1
})
1%1 = 0
1%9 = 1
1%10 = 1
*/
let data = ref(
Array.apply(null, {
length: 81,
} as number[]).map((_, index) => {
return {
id: index, // 作为key
number: (index % 9) + 1, // 从1开始 1-9*1-9的组合
};
})
);
let random = () => {
data.value = _.shuffle(data.value);
};
let num = reactive({
currentNum: 0,
gsapNum: 0,
});
watch(
() => num.currentNum,
(newVal) => {
gsap.to(num, {
duration: 1,
gsapNum: newVal,
});
}
);
let beforeEnter = (el: Element) => {
//对应enter-from
console.log("进入之前", el);
};
let enterActive = (el: Element, done: Function) => {
//对应enter-active
console.log("过度曲线");
setTimeout(() => {
done();
}, 3000);
};
let enterTo = (el: Element) => {
//对应enter-to
console.log("进入完成");
};
let enterCancelled = () => {
//多点击几次
console.log("过度效果被打断");
//显示过程被打断
};
let beforeLeave = () => {
//对应leave-from
console.log("离开之前");
};
let leaveActive = (el: Element, done: Function) => {
//对应enter-activem
console.log("离开过度曲线");
setTimeout(() => {
done();
}, 3000);
};
let leaveTo = () => {
//对应leave-to
console.log("离开完成");
};
let leaveCancelled = () => {
//离开过度打断
};
let beforeEnter1 = (el: Element) => {
gsap.set(el, {
width: 0,
height: 0,
});
};
let enter1 = (el: Element, done: gsap.Callback) => {
gsap.to(el, {
width: 200,
height: 200,
onComplete: done,
});
};
let leave1 = (el: Element, done: gsap.Callback) => {
gsap.to(el, {
width: 0,
height: 0,
onComplete: done,
});
};
let add = () => {
list.push(list.length + 1);
};
let pop = () => {
list.pop();
};
</script>
<style lang="less" scoped>
.box {
width: 200px;
height: 200px;
background-color: pink;
}
// 切换0
//进入之前
.fade0-enter-from {
width: 0px;
height: 0px;
}
//进入过程
.fade0-enter-active {
transition: all 2s ease;
}
// 最后一帧
.fade0-enter-to {
//进入完成最好和box保持一致
width: 200px;
height: 200px;
background: red;
}
.fade0-leave-from {
//离开之前
width: 200px;
height: 200px;
}
.fade0-leave-active {
//离开过程
transition: all 3s linear;
}
.fade0-leave-to {
//离开的最后一帧
width: 0px;
height: 0px;
}
// 切换1样式
.fade-enter-from, //进入之前
.e-from {
width: 0px;
height: 0px;
}
.fade-enter-active, //进入过度
.e-active {
transition: all 2.5s ease;
transform: rotate(360);
}
.fade-enter-to, // 进入完成
.e-to {
width: 200px;
height: 200px;
}
.fade-leave-from {
// 离开之前
width: 200px;
height: 200px;
}
.fade-leave-active {
//离开过度
transition: all 2.5s ease;
transform: rotate(360);
}
.fade-leave-to {
// 离开完成
width: 20px;
height: 20px;
}
.appear-from {
width: 0;
height: 0;
}
.appear-active {
transition: all 2.5s ease;
}
.appear-to {
width: 200px;
height: 200px;
}
.wraps {
display: flex;
flex-wrap: wrap;
width: calc(20px * 10 + 10px); // 整个父级的宽度,合理换行
.item {
width: 20px;
height: 20px;
border: 1px solid #ccc;
list-style-type: none;
display: flex;
justify-content: center;
align-items: center;
}
}
.group_move {
transition: all 0.8s ease;
}
</style>