2-1 使用 Vue 实现基础的 CSS 过渡与动画效果
过渡和动画是什么?
过渡:一个div
的颜色从黑色变成红色
动画:一个div
元素左右移动
举个简单的动画例子:
<style>
@keyframes leftToRight {
0% {
transform: translateX(0);
}
50% {
transform: translateX(-50px);
}
}
.animation {
animation: leftToRight 3s;
}
</style>
<script>
const app = Vue.createApp({
data () {
return {
animate: {
animation: true
}
}
},
template: `
<div :class="animate">hello Jennie</div>
`
})
const vm = app.mount('#root');
</script>
这个动画就是很简单的…可以自己试试^ ^
然后就是这个@keyframes
做一个简单的解释:
以百分比来规定改变发生的时间,或者通过关键词"from"和"to",等价于0%和100%
0%是动画的开始时间,100%动画的结束时间
然后再举个过渡的例子:
<style>
.transition {
transition: 3s background-color ease;
}
.blue {
background:blue;
}
.green {
background: green;
}
</style>
<script>
const app = Vue.createApp({
data () {
return {
animate: {
transition: true,
blue: true,
green: false
}
}
},
methods: {
handleBtnClick() {
this.animate.blue = !this.animate.blue
this.animate.green = !this.animate.green
}
},
template: `
<div :class="animate">hello Jennie</div>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
这个也挺简单的,试一下就行了
其实上面的跟直接用css
写差不多的,上面我是通过控制class
来修改样式的,那能不能直接控制style
呢? 是可以的(`・ω・´)
<head>
<style>
.transition {
transition: 3s background-color ease;
}
.blue {
background:blue;
}
.green {
background: green;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
data () {
return {
styleObj: {
background: 'blue'
}
}
},
methods: {
handleBtnClick() {
this.styleObj.background === 'blue' ? this.styleObj.background = 'green' : this.styleObj.background = 'blue'
}
},
template: `
<div class="transition" :style="styleObj">hello Jennie</div>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
太基础了,看看就行了
2-2 使用 transition 标签实现单元素/组件的过渡和动画效果
过渡效果
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@next"></script>
<style>
.v-enter-from {
opacity: 0;
}
.v-enter-active {
transition: opacity 3s ease-out;
}
.v-enter-to {
opacity: 1;
}
.v-leave-from {
opacity: 1;
}
.v-leave-active {
transition: opacity 3s ease-out;
}
.v-leave-to {
opacity: 0;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
data () {
return {
show: false
}
},
methods: {
handleBtnClick() {
this.show = !this.show
}
},
template: `
<transition>
<div v-if="show">hello Jennie</div>
</transition>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
</html>
其实这个很简单,但是还是记录一下:
v-enter-from
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:定义进入过渡的结束状态。在元素被插入之后下一帧生效(与此同时v-enter-from
被移除),在过渡/动画完成之后移除。v-leave-from
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:离开过渡的结束状态。在离开过渡被触发之后下一帧生效(与此同时v-leave-from
被删除),在过渡/动画完成之后移除。
对于上面<style>
里的代码其实还可以简化一下:
<style>
.v-enter-from {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 3s ease-out;
}
.v-enter-to {
opacity: 1;
}
.v-leave-to {
opacity: 0;
}
</style>
这里的v-enter-to
其实也可以删掉,但是涉及一些原理性的东西,后面再探讨
动画效果:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue@next"></script>
<style>
@keyframes shake {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
100% {
transform: translateX(50px);
}
}
.v-enter-active,
.v-leave-active {
animation: shake 3s ;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
data () {
return {
show: false
}
},
methods: {
handleBtnClick() {
this.show = !this.show
}
},
template: `
<transition>
<div v-if="show">hello Jennie</div>
</transition>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
</html>
补充一个:
对于这些在过渡中切换的类名来说,如果你使用一个没有名字的 <transition>
,则 v-
是这些class名的默认前缀。如果你使用了 <transition name="my-transition">
,那么 v-enter-from
应该写成 my-transition-enter-from
然后就是我们还可以自定义过渡class类名
<style>
@keyframes shake {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
100% {
transform: translateX(50px);
}
}
.hello,
.by {
animation: shake 3s ;
}
</style>
template: `
<transition
enter-active-class="hello"
leave-active-class="bye"
>
<div v-if="show">hello Jennie</div>
</transition>
<button @click="handleBtnClick">切换</button>
`
用这种自定义class类名一个很方便的地方是可以引用第三方动画库,举个例子(百度搜索animate.css):
// 头部引用
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
// 之前写的style可以删除了
template: `
<transition
enter-active-class="animate__animated animate__bounce"
leave-active-class="animate__animated animate__bounce"
>
<div v-if="show">hello Jennie</div>
</transition>
<button @click="handleBtnClick">切换</button>
`
这样引用第三方的动画库是很方便的^ ^
然后回到一开始,我们看下面的代码:
.v-enter-from {
color: red;
}
.v-enter-active{
animation: shake 3s;
transition: color 3s ease-in;
color: green;
}
这样写是不会显示红色的,动画在入场的时候就是绿色,因为v-enter-from
是包含在v-enter-active
里面的,所以绿色会覆盖掉红色
接下来我们看一下这种情况:
.v-enter-from {
color: red;
}
.v-enter-active{
animation: shake 10s;
transition: color 3s ease-in;
}
.v-leave-active {
color: red;
animation: shake 10s;
transition: color 3s ease-out;
}
这个过渡和动画的时间不一致,我们运行之后会发现,实际上整体的时间是以时间长的那个为准的,也就是过渡执行了3s,动画执行了10s。那我们可以自己控制这个时长吗?是可以的^ ^
第一种是:
<transition type="transition">
<div v-if="show">hello Jennie</div>
</transition>
type="transition">
的意思是以transition
为准,运行结果就是3s之后动画也停止了
第二种是:
<transition duration="1000">
<div v-if="show">hello Jennie</div>
</transition>
入场和出场时间固定为1s
第三种是:
<transition :duration="{enter: 3000,leave: 4000}">
<div v-if="show">hello Jennie</div>
</transition>
入场出场时间自定义^ ^
其实上面的方法都是用css来执行动画过渡效果的,那么我们怎么用js来控制呢?
首先是关闭使用css制作动画<transition :css="false">
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleBtnClick() {
this.show = !this.show
},
handleBeforeEnter(el) {
el.style.color = 'red'
},
handleEnterActive(el, done) {
const animation = setInterval(() => {
const color = el.style.color
if(color === 'red') {
el.style.color = 'green'
} else {
el.style.color = 'red'
}
}, 1000)
setTimeout(() => {
clearInterval(animation);
done()
}, 3000)
},
handleEnterEnd() {
alert(123)
}
},
template: `
<transition
:css="false"
@before-enter="handleBeforeEnter"
@enter="handleEnterActive"
@after-enter="handleEnterEnd"
>
<div v-if="show">hello Jennie</div>
</transition>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
@before-enter
、@enter
、@after-enter
,这三个钩子就不介绍了,应该能懂,讲一下setInterval
是在执行颜色切换的效果,setTimeout
是设置过渡结束的时间,这个参数done
,也就是在setTimeout
里执行了done()
是让transition
感知到动画已经结束了,才会继续执行后面@after-enter
钩子里的代码
还需要注意到的一点是@before-enter
和@after-enter
执行的方法只接收一个参数el
,@enter
接收两个参数el
和done
,下面的出场一样
对应的还有三个出场的钩子@before-leave
、@leave
、@leave-after
,就不细说了,自己去试试就知道了^ ^
2-3 组件和元素切换动画的实现
先看第一种多个元素切换:
<head>
<script src="https://unpkg.com/vue@next"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
<style>
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 1s ease-in;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
</style>
</head>
<body>
<div id="root"></div>
</body>
<script>
const app = Vue.createApp({
data() {
return {
show: true
}
},
methods: {
handleBtnClick() {
this.show = !this.show
},
},
template: `
<transition>
<div v-if="show">hello Jennie</div>
<div v-else>Meiko</div>
</transition>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
这样写运行出来还挺丑的,第二个div
出现的时候,第一个div
并没有完全消失,我们可以这样做:
<transition mode="out-in">
就ok了,然后就是在刚进入页面的时候,第一个div
展示是没有过渡效果的,我们可以再加一个<transition mode="out-in" appear>
这样就行了
最后再说一下组件之间的切换
<script>
const componentA = {
template: `<div>hello Jennie</div>`
}
const componentB = {
template: `<div>Meiko</div>`
}
const app = Vue.createApp({
data() {
return {
show: true
}
},
components: {
'component-a': componentA,
'component-b': componentB
},
methods: {
handleBtnClick() {
this.show = !this.show
},
},
template: `
<transition mode="out-in" appear>
<component-a v-if="show" />
<component-b v-else="show" />
</transition>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
其实还可以写的更简洁一点:(动态绑定组件)
<script>
const componentA = {
template: `<div>hello Jennie</div>`
}
const componentB = {
template: `<div>Meiko</div>`
}
const app = Vue.createApp({
data() {
return {
show: true,
components: 'component-a'
}
},
components: {
'component-a': componentA,
'component-b': componentB
},
methods: {
handleBtnClick() {
this.show = !this.show
this.components === 'component-a' ? this.components = 'component-b' : this.components = 'component-a'
},
},
template: `
<transition mode="out-in" appear>
<component :is='components' />
</transition>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
哎,总之这些都挺基础的,看一看敲一敲就好了
2-4 状态动画
举个简单的例子:
<script>
const app = Vue.createApp({
data() {
return {
number: 1,
currentNumber: 1
}
},
methods: {
handleBtnClick() {
if (this.number < 10) {
const animation = setInterval(() => {
this.number += 1
if(this.number === 10) {
clearInterval(animation)
}
}, 100);
}
},
},
template: `
<div>{{number}}</div>
<button @click="handleBtnClick">切换</button>
`
})
const vm = app.mount('#root');
</script>
状态动画可以理解为,通过改变数据来改变内容的展示,然后不断改变数据,内容也跟着改变,就形成了动画的效果,常用的svg
图就是基于这种原理实现的。