一、css transition实现动画效果
在Vue中实现展开收起动画的常用方式就是使用css transition。使用transition可以给目标元素的任意状态变化提供动画效果,比如在元素高度从0到高度自适应的过程中播放动画。
下面是一个基本的例子,其中<transition>元素用于包裹需要动画效果的元素,然后可以通过指定name属性和定义对应状态的css样式来实现动画:
<template>
<div>
<button @click="isVisible = !isVisible">Toggle</button>
<transition name="slide">
<div v-if="isVisible">Some content here...</div>
</transition>
</div>
</template>
<style>
.slide-enter-active,
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-enter,
.slide-leave-to {
opacity: 0;
transform: translateY(100%);
}
</style>
<script>
export default {
data() {
return {
isVisible: false
};
}
};
</script>
二、使用第三方Vue库
虽然使用css transition可以实现基本的展开收起动画效果,但是在实现自定义复杂动画效果时就会比较困难。此时可以使用一些第三方Vue组件库,比如Vue的官方动画库vue-transition,还有比较流行的动画库animate.css。
下面以animate.css为例,介绍如何使用第三方库实现动画效果。
首先安装animate.css:
npm install animate.css -S
然后在需要使用动画效果的组件中引入该库:
import 'animate.css';
下面是一个使用animate.css实现展开收起动画的例子:
<template>
<div>
<button @click="isVisible = !isVisible">Toggle</button>
<div class="container" :class="{ 'animate__animated': isVisible, 'animate__fadeIn': isVisible, 'animate__fadeOut': !isVisible }">
<div class="content" v-if="isVisible">Some content here...</div>
</div>
</div>
</template>
<style>
.container {
display: flex;
justify-content: center;
}
.content {
width: 50%;
padding: 20px;
}
</style>
<script>
export default {
data() {
return {
isVisible: false
};
}
};
</script>
三、使用JS方法手动实现动画
如果对于css transition和第三方库都不够熟悉或者不想使用第三方库,那么可以通过JS手动实现动画效果。手动实现动画主要利用Vue提供的以下API:
- <transition>的钩子函数
- Vue的$refs属性
下面是一个手动实现展开收起动画的例子。需要注意的是,这个例子只是为了演示手动动画实现的原理,不是一个完整的方案,需要自己根据实际需求进行完善。
<template>
<div>
<button @click="toggle">Toggle</button>
<div ref="content" class="content" :style="{ height: height + 'px' }">Some content here...</div>
</div>
</template>
<style>
.content {
transition: height 0.3s ease;
overflow: hidden;
}
</style>
<script>
export default {
data() {
return {
height: 0
};
},
methods: {
toggle() {
if (this.height === 0) {
this.height = this.$refs.content.scrollHeight;
} else {
this.height = 0;
}
}
}
};
</script>
四、应用场景举例
展开收起动画在实际应用中有很多场景,比如:
- 在商品列表中,点击商品标题可以展开该商品的详细信息:
<template>
<div v-for="item in productList">
<div class="title" @click="toggle(item)">{{ item.title }}</div>
<transition name="slide">
<div class="detail" :class="{ 'isActive': item.isActive }" v-if="item.isActive">{{ item.detail }}</div>
</transition>
</div>
</template>
<style>
.slide-enter-active,
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-enter,
.slide-leave-to {
opacity: 0;
transform: translateY(100%);
}
.title {
cursor: pointer;
}
.detail {
padding: 10px;
border: 1px solid black;
margin-top: 10px;
display: none;
}
.isActive {
display: block;
}
</style>
<script>
export default {
data() {
return {
productList: [
{ title: 'Product 1', detail: 'Details of product 1', isActive: false },
{ title: 'Product 2', detail: 'Details of product 2', isActive: false },
{ title: 'Product 3', detail: 'Details of product 3', isActive: false }
]
};
},
methods: {
toggle(item) {
item.isActive = !item.isActive;
}
}
};
</script>
<template>
<div v-for="comment in commentList">
<div class="comment">{{ comment.content }}</div>
<div class="reply" @click="toggle(comment)" v-if="comment.hasReply">{{ comment.numOfReply }} replies</div>
<transition name="slide">
<div class="reply-list" :class="{ 'isActive': comment.isActive }" v-if="comment.isActive">
<div v-for="reply in comment.replyList" class="reply">{{ reply.content }}</div>
</div>
</transition>
</div>
</template>
<style>
.slide-enter-active,
.slide-leave-active {
transition: all 0.3s ease;
}
.slide-enter,
.slide-leave-to {
opacity: 0;
transform: translateY(100%);
}
.reply {
cursor: pointer;
margin-top: 10px;
}
.reply-list {
padding: 10px;
border: 1px solid black;
margin-top: 10px;
display: none;
}
.isActive {
display: block;
}
</style>
<script>
export default {
data() {
return {
commentList: [
{ content: 'Comment 1', hasReply: true, numOfReply: 2, replyList: [ { content: 'Reply 1.1' }, { content: 'Reply 1.2' } ], isActive: false },
{ content: 'Comment 2', hasReply: false },
{ content: 'Comment 3', hasReply: true, numOfReply: 1, replyList: [ { content: 'Reply 3.1' } ], isActive: false },
]
};
},
methods: {
toggle(comment) {
comment.isActive = !comment.isActive;
}
}
};
</script>