vue 动画
1 使用 Vue 实现基础的 CSS 过渡与动画效果
1.1 过渡
<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: {
handleClick() {
this.animate.blue = !this.animate.blue
this.animate.green = !this.animate.green
}
},
template: `
<div>
<div :class="animate">hello world</div>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
</script>
- 通过数据来控制元素的
DOM
和style
<style>
.transition {
transition: 3s background-color ease;
}
</style>
<script>
const app = Vue.createApp({
data() {
return {
styleObj: {
background: 'blue'
}
}
},
methods: {
handleClick() {
if(this.styleObj.background === 'blue') {
this.styleObj.background = 'green'
} else if(this.styleObj.background === 'green') {
this.styleObj.background = 'blue'
}
}
},
template: `
<div>
<div class="transition" :style="styleObj">hello world</div>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
</script>
1.2 动画
<style>
@keyframes leftToRight {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
0% {
transform: translateX(0px);
}
}
.animation {
animation: leftToRight 3s;
}
</style>
<script>
const app = Vue.createApp({
data() {
return {
animate: {
animation: false
}
}
},
methods: {
handleClick() {
this.animate.animation = !this.animate.animation
}
},
template: `
<div>
<div :class="animate">hello world</div>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
</script>
2 使用 transition 标签实现单元素组件的过渡和动画效果
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show
}
},
// <transition> --- `v-`
// <transition name='hello'> --- `hello-`
// v-if v-show
template: `
<div>
<transition>
<div v-show="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
2.1 过渡
.v-enter-from {
opacity: 0;
}
.v-enter-to {
opacity: 1;
}
.v-leave-to {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: opacity 2s ease-in;
}
2.2 动画
@keyframes shake {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
100% {
transform: translateX(50px);
}
}
.v-enter-active,
.v-leave-active {
animation: shake 2s;
}
2.3 自定义 class 类名
<style>
@keyframes shake {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
100% {
transform: translateX(50px);
}
}
.hello,
.bye {
animation: shake 2s;
}
</style>
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show
}
},
template: `
<div>
<transition
enter-active-class="hello"
leave-active-class="bye"
>
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
</script>
2.4 使用第三动画库 animate
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css"
/>
<script>
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show
}
},
template: `
<div>
<transition
enter-active-class="animate__animated animate__bounce"
leave-active-class="animate__animated animate__flash"
>
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
</script>
2.5 动画 + 过渡
@keyframes shake {
0% {
transform: translateX(-100px);
}
50% {
transform: translateX(-50px);
}
100% {
transform: translateX(50px);
}
}
.v-enter-from {
color: red;
}
.v-enter-active {
animation: shake 2s;
transition: color 3s ease-in;
}
.v-leave-active {
color: red;
animation: shake 2s;
transition: all 3s ease-in;
}
- 动画 & 过渡同时存在,
<transition>
添加属性type
实现以动画animation
| 过渡transition
的时间为准结束该过程
const app = Vue.createApp({
template: `
<div>
<transition type="transition">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
- 动画 & 过渡同时存在,
<transition>
添加属性duration
实现以指定时间 (ms为单位
) 为准
const app = Vue.createApp({
template: `
<div>
<transition :duration="{ enter: 1000, leave: 3000 }">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
2.6 通过数据来控制元素的 DOM 和 style
const app = Vue.createApp({
data() {
return {
show: false
}
},
methods: {
handleClick() {
this.show = !this.show
},
handleBeforeEnter(el) {
el.style.color = "red"
},
handleEnter(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() // 通知 handleAfterEnter
}, 5000);
},
handleAfterEnter() {
alert(123)
}
},
template: `
<div>
<transition
:css="false"
@before-enter="handleBeforeEnter"
@enter="handleEnter"
@after-enter="handleAfterEnter"
@before-leave="handleBeforeLeave"
@leave="handleLeave"
@after-leave="handleAfterLeave"
>
<div v-show="show">hello world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
3 组件和元素切换动画的实现
3.1 多个单元素标签之间的切换
- 动画同时进行
<style>
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-enter-active,
.v-leave-active {
transition: all 3s ease-in;
}
.v-enter-to,
.v-leave-from {
opacity: 1;
}
</style>
<script>
const app = Vue.createApp({
data() {
return { show: false }
},
methods: {
handleClick() {
this.show = !this.show
}
},
template: `
<div>
<transition>
<div v-if="show">hello world</div>
<div v-else="show">bye world</div>
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
</script>
1. <transition>
添加属性 mode
实现多元素的过渡顺序
-
先进场再出场:
mode="in-out"
-
先出场再进场:
mode="out-in"
2. <transition>
添加属性 appear
实现初始动画效果
3.2 多个单组件之间的切换
const ComponentA = {
template: '<div>hello world</div>'
}
const ComponentB = {
template: '<div>bye world</div>'
}
const app = Vue.createApp({
data() {
return { show: false }
},
methods: {
handleClick() {
this.show = !this.show
}
},
components: {
'component-a': ComponentA,
'component-b': ComponentB,
},
template: `
<div>
<transition mode="out-in" appear>
<component-a v-if="show">hello world</component-a>
<component-b v-else="show">bye world</component-b>
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
- 动态组件
const ComponentA = {
template: '<div>hello world</div>'
}
const ComponentB = {
template: '<div>bye world</div>'
}
const app = Vue.createApp({
data() {
return { component: 'component-a' }
},
methods: {
handleClick() {
if(this.component === 'component-a') {
this.component = 'component-b'
} else {
this.component = 'component-a'
}
}
},
components: {
'component-a': ComponentA,
'component-b': ComponentB,
},
template: `
<div>
<transition mode="out-in" appear>
<component :is="component" />
</transition>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
4 列表动画
<transition-group>
<style>
.list-item {
display: inline-block;
margin-right: 10px;
}
</style>
<script>
const app = Vue.createApp({
data() {
return { list: [1, 2, 3] }
},
methods: {
handleClick() {
this.list.unshift(this.list.length + 1)
}
},
template: `
<div>
<transition-group>
<span class="list-item" v-for="item in list" :key="item">{{ item }}</span>
</transition-group>
<button @click="handleClick">切换</button>
</div>
`
})
const vm = app.mount('#root')
</script>
4.1 新增项的动画效果
.v-enter-from {
opacity: 0;
transform: translateY(30px);
}
.v-enter-active {
transition: all 1s ease-in;
}
.v-enter-to {
opacity: 1;
transform: translateY(0);
}
4.2 所有项的动画效果
.v-enter-from {
opacity: 0;
transform: translateY(30px);
}
.v-enter-active {
transition: all 1s ease-in;
}
.v-enter-to {
opacity: 1;
transform: translateY(0);
}
.v-move {
transition: all 1s ease-in;
}
5 状态动画
- 通过
数据
控制动画
const app = Vue.createApp({
data() {
return {
number: 1,
animateNumber: 1
}
},
methods: {
handleClick() {
this.number = 10;
if(this.animateNumber < this.number) {
const animation = setInterval(() => {
this.animateNumber += 1;
if(this.animateNumber === 10) {
clearInterval(animation)
}
}, 100);
}
}
},
template: `
<div>
<span>{{ animateNumber }}</span>
<button @click="handleClick">增加</button>
</div>
`
})
const vm = app.mount('#root')