Vue2 过渡与动画
单元素/组件过渡
transition 组件
Vue 提供了 transition 组件来为元素或组件的进入/离开添加过渡效果。
vue
<template>
<div>
<button @click="show = !show">切换</button>
<transition name="fade">
<p v-if="show">淡入淡出效果</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return { show: true }
}
}
</script>
<style>
/* 定义过渡类名 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style><template>
<div>
<button @click="show = !show">切换</button>
<transition name="fade">
<p v-if="show">淡入淡出效果</p>
</transition>
</div>
</template>
<script>
export default {
data() {
return { show: true }
}
}
</script>
<style>
/* 定义过渡类名 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>过渡类名
进入:
v-enter-from → v-enter-active → v-enter-to
(进入前) (进入过程) (进入后)
离开:
v-leave-from → v-leave-active → v-leave-to
(离开前) (离开过程) (离开后)进入:
v-enter-from → v-enter-active → v-enter-to
(进入前) (进入过程) (进入后)
离开:
v-leave-from → v-leave-active → v-leave-to
(离开前) (离开过程) (离开后)若 transition 未设置 name,类名前缀为 v-;设置后使用自定义前缀:
vue
<template>
<transition name="slide">
<p v-if="show">滑动效果</p>
</transition>
</template>
<style>
.slide-enter-active,
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-enter-from {
transform: translateX(100%);
opacity: 0;
}
.slide-leave-to {
transform: translateX(-100%);
opacity: 0;
}
</style><template>
<transition name="slide">
<p v-if="show">滑动效果</p>
</transition>
</template>
<style>
.slide-enter-active,
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-enter-from {
transform: translateX(100%);
opacity: 0;
}
.slide-leave-to {
transform: translateX(-100%);
opacity: 0;
}
</style>CSS 动画
vue
<template>
<transition name="bounce">
<p v-if="show">弹跳效果</p>
</transition>
</template>
<style>
@keyframes bounce-in {
0% { transform: scale(0); }
50% { transform: scale(1.25); }
100% { transform: scale(1); }
}
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
</style><template>
<transition name="bounce">
<p v-if="show">弹跳效果</p>
</transition>
</template>
<style>
@keyframes bounce-in {
0% { transform: scale(0); }
50% { transform: scale(1.25); }
100% { transform: scale(1); }
}
.bounce-enter-active {
animation: bounce-in 0.5s;
}
.bounce-leave-active {
animation: bounce-in 0.5s reverse;
}
</style>自定义过渡类名(配合第三方动画库)
vue
<template>
<!-- 使用 animate.css -->
<transition
enter-active-class="animate__animated animate__fadeIn"
leave-active-class="animate__animated animate__fadeOut"
>
<p v-if="show">使用 animate.css</p>
</transition>
</template>
<style>
@import 'animate.css';
</style><template>
<!-- 使用 animate.css -->
<transition
enter-active-class="animate__animated animate__fadeIn"
leave-active-class="animate__animated animate__fadeOut"
>
<p v-if="show">使用 animate.css</p>
</transition>
</template>
<style>
@import 'animate.css';
</style>初始渲染过渡
appear 属性
vue
<template>
<transition appear appear-class="custom-appear-class" appear-active-class="custom-active-class">
<p>页面加载时就有动画</p>
</transition>
</template><template>
<transition appear appear-class="custom-appear-class" appear-active-class="custom-active-class">
<p>页面加载时就有动画</p>
</transition>
</template>多个元素过渡
使用 key 区分
vue
<template>
<transition name="fade">
<!-- 必须添加 key,Vue 通过 key 区分不同元素 -->
<button :key="'save'" v-if="editing" @click="save">保存</button>
<button :key="'edit'" v-else @click="edit">编辑</button>
</transition>
</template><template>
<transition name="fade">
<!-- 必须添加 key,Vue 通过 key 区分不同元素 -->
<button :key="'save'" v-if="editing" @click="save">保存</button>
<button :key="'edit'" v-else @click="edit">编辑</button>
</transition>
</template>列表过渡
transition-group
vue
<template>
<div>
<button @click="addItem">添加</button>
<button @click="removeItem">移除</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.text }}
<button @click="removeItem(item)">x</button>
</li>
</transition-group>
</div>
</template>
<script>
let id = 0
export default {
data() {
return { items: [] }
},
methods: {
addItem() {
this.items.push({ id: id++, text: `项目 ${id}` })
},
removeItem(item) {
this.items = this.items.filter(i => i !== item)
}
}
}
</script>
<style>
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style><template>
<div>
<button @click="addItem">添加</button>
<button @click="removeItem">移除</button>
<transition-group name="list" tag="ul">
<li v-for="item in items" :key="item.id">
{{ item.text }}
<button @click="removeItem(item)">x</button>
</li>
</transition-group>
</div>
</template>
<script>
let id = 0
export default {
data() {
return { items: [] }
},
methods: {
addItem() {
this.items.push({ id: id++, text: `项目 ${id}` })
},
removeItem(item) {
this.items = this.items.filter(i => i !== item)
}
}
}
</script>
<style>
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
</style>列表移动过渡
vue
<template>
<transition-group name="flip-list" tag="ul">
<li v-for="item in items" :key="item.id">{{ item.text }}</li>
</transition-group>
</template>
<style>
.flip-list-move {
transition: transform 0.5s ease;
}
.flip-list-enter-active,
.flip-list-leave-active {
transition: all 0.5s ease;
}
.flip-list-enter-from,
.flip-list-leave-to {
opacity: 0;
transform: translateX(30px);
}
.flip-list-leave-active {
/* 使列表项在离开时不影响其他项布局 */
position: absolute;
}
</style><template>
<transition-group name="flip-list" tag="ul">
<li v-for="item in items" :key="item.id">{{ item.text }}</li>
</transition-group>
</template>
<style>
.flip-list-move {
transition: transform 0.5s ease;
}
.flip-list-enter-active,
.flip-list-leave-active {
transition: all 0.5s ease;
}
.flip-list-enter-from,
.flip-list-leave-to {
opacity: 0;
transform: translateX(30px);
}
.flip-list-leave-active {
/* 使列表项在离开时不影响其他项布局 */
position: absolute;
}
</style>JavaScript 钩子
完整钩子
vue
<template>
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
>
<p v-if="show">JS 钩子控制动画</p>
</transition>
</template>
<script>
export default {
data() {
return { show: false }
},
methods: {
beforeEnter(el) {
// 设置起始状态
el.style.opacity = '0'
el.style.transform = 'translateY(20px)'
},
enter(el, done) {
// 强制回流使起始状态生效
el.offsetHeight
// 设置结束状态
el.style.transition = 'all 0.3s ease'
el.style.opacity = '1'
el.style.transform = 'translateY(0)'
// 调用 done 标记动画结束
done()
},
afterEnter(el) {
console.log('进入动画完成')
},
enterCancelled(el) {
console.log('进入动画取消')
},
beforeLeave(el) {
el.style.opacity = '1'
},
leave(el, done) {
el.style.transition = 'all 0.3s ease'
el.style.opacity = '0'
el.style.transform = 'translateY(20px)'
done()
},
afterLeave(el) {
console.log('离开动画完成')
}
}
}
</script><template>
<transition
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@enter-cancelled="enterCancelled"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
@leave-cancelled="leaveCancelled"
>
<p v-if="show">JS 钩子控制动画</p>
</transition>
</template>
<script>
export default {
data() {
return { show: false }
},
methods: {
beforeEnter(el) {
// 设置起始状态
el.style.opacity = '0'
el.style.transform = 'translateY(20px)'
},
enter(el, done) {
// 强制回流使起始状态生效
el.offsetHeight
// 设置结束状态
el.style.transition = 'all 0.3s ease'
el.style.opacity = '1'
el.style.transform = 'translateY(0)'
// 调用 done 标记动画结束
done()
},
afterEnter(el) {
console.log('进入动画完成')
},
enterCancelled(el) {
console.log('进入动画取消')
},
beforeLeave(el) {
el.style.opacity = '1'
},
leave(el, done) {
el.style.transition = 'all 0.3s ease'
el.style.opacity = '0'
el.style.transform = 'translateY(20px)'
done()
},
afterLeave(el) {
console.log('离开动画完成')
}
}
}
</script>velocity.js 搭配
vue
<template>
<transition
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<p v-if="show">Velocity 动画</p>
</transition>
</template>
<script>
import Velocity from 'velocity-animate'
export default {
data() {
return { show: false }
},
methods: {
beforeEnter(el) {
el.style.opacity = '0'
},
enter(el, done) {
Velocity(el, { opacity: 1, translateY: '0px' }, { duration: 300, complete: done })
},
leave(el, done) {
Velocity(el, { opacity: 0, translateY: '20px' }, { duration: 300, complete: done })
}
}
}
</script><template>
<transition
@before-enter="beforeEnter"
@enter="enter"
@leave="leave"
>
<p v-if="show">Velocity 动画</p>
</transition>
</template>
<script>
import Velocity from 'velocity-animate'
export default {
data() {
return { show: false }
},
methods: {
beforeEnter(el) {
el.style.opacity = '0'
},
enter(el, done) {
Velocity(el, { opacity: 1, translateY: '0px' }, { duration: 300, complete: done })
},
leave(el, done) {
Velocity(el, { opacity: 0, translateY: '20px' }, { duration: 300, complete: done })
}
}
}
</script>
Cao Note