需求简介
这几天接了个新项目,需要实现下图中左侧边栏的菜单切换。这种功能其实就是一个折叠面板,实现方式多种多样。
实现上面的功能,无非就是一个v-show的事儿,但没有过渡,会显得非常生硬。想添加一些过渡效果,
最简单的就是使用element ui、或者ant的折叠面板组件了。但可惜的是,我们的项目不能使用任何第三方组件库。
为了做好产品,我还是略施拳脚,实现了一个简单且丝滑的过渡效果:
老板看后,觉得我的细节处理的很好,给我一顿画饼,承诺只要我好好坚持,一定可以等到升职加薪!当然,我胃口小,老板的饼消化不了。我还是分享一下自己在不借助第三方组件的情况下,如何快速的实现这样一个效果。
技术实现方案
业务分析
仔细观察需求,我们可以分析出其实动画主要是两个部分:一级标题的箭头旋转和二级标题区域的折叠展开。
我们先实现一下基本的html结构:
<template>
<div class="nav-bar-content">
<div class="header-wrap" @click="open = !open">
<span class="text">自动化需求计算条件输如</span>
<span class="arrow">
>
</span>
</div>
<div v-show="open" class="content">
<p>算法及跃变计算条件</p>
<p>空间品质判断条件</p>
<p>需求自动计算条件</p>
<p>通风系统</p>
</div>
</div>
</template>
<script setup>
const open = ref(false);
</script>
上述代码非常简单,点击一级标题时,更改open的值,从而实现二级标题的内容区域展示与隐藏。
箭头旋转动画
实现箭头旋转动画其实非常容易,我们只要在红色面板展开时,给箭头添加一个新的类名,在这个类名中做一些动画处理即可。
<template>
<div class="header-wrap" @click="open = !open">
<span class="text">自动化需求计算条件输如</span>
<span class="arrow flex-be-ce" :class="{ open: open }">
>
</span>
</div>
</template>
<style lang="less" scoped>
.arrow {
width: 16px;
height: 16px;
cursor: pointer;
margin-left: 1px;
transition: transform 0.2s ease;
}
.open {
transform: rotate(90deg);
transition: transform 0.2s ease;
}
</style>
上述的代码通过 CSS 的 transform 属性和动态绑定open类名实现了箭头的旋转效果。
注意:arrow也需要定义过渡效果
折叠区域动画效果
要实现折叠区域的动画效果,大致思路和上面一样。
使用vue的transition组件实现
借助vue的transition组件,我们可以实现折叠区域进入(v-show='true')和消失(v-show='fasle')的动画。一种可行的动画方案就是让面板进入前位置在y轴-100%的位置,进入后处于正常位置。
<template>
<div class="nav-bar-content">
<div class="header-wrap" @click="open = !open">
<span class="text">自动化需求计算条件输如</span>
<span class="arrow" :class="{ open: open }">
>
</span>
</div>
<div class="content-wrap">
<Transition>
<div v-show="open" class="content">
<p>算法及跃变计算条件</p>
<p>空间品质判断条件</p>
<p>需求自动计算条件</p>
<p>通风系统</p>
</div>
</Transition>
</div>
</div>
</template>
<script setup>
const open = ref(false);
</script>
<style lang="less" scoped>
<style lang="less" scoped>
.v-enter-active,
.v-leave-active {
transition: transform 0.5s ease;
}
.v-enter-from,
.v-leave-to {
transform: translateY(-100%);
}
</style>
上述效果有一点瑕疵,就是出现位置把一级标题盖住了,我们稍微修改下
<div class="content-wrap">
<Transition>
<div v-show="open" class="content">
<p>算法及跃变计算条件</p>
<p>空间品质判断条件</p>
<p>需求自动计算条件</p>
<p>通风系统</p>
</div>
</Transition>
</div>
.content-wrap {
overflow: hidden;
}
使用动态类名的方式实现
效果好很多!但这种效果和第三方组件库的效果不太一致,我们以element的折叠面板效果为例:
我们可以发现,它的这种动画,是折叠面板的高度从0逐渐增高的一个过程。所以最简单的就是,如果我们知道折叠面板的高度,一个类名就可以搞定!
<template>
<div class="nav-bar-content">
<div class="header-wrap" @click="open = !open">
<span class="text">自动化需求计算条件输如</span>
<span class="arrow flex-be-ce" :class="{ open: open }">
>
</span>
</div>
<div class="content-wrap" :style="{ height: open ? '300px' : 0 }">
<div class="content">
<p>算法及跃变计算条件</p>
<p>空间品质判断条件</p>
<p>需求自动计算条件</p>
<p>通风系统</p>
</div>
</div>
</div>
</template>
<script setup>
const open = ref(false);
</script>
<style lang="less" scoped>
.content-wrap {
height: 0;
transition: height 0.5s ease;
}
</style>
如果这个折叠面板的内容通过父组件传递,高度是动态的,我们只需要使用js计算这里的高度即可:
<template>
<div class="nav-bar-content">
<div class="header-wrap" @click="open = !open">
<span class="text">自动化需求计算条件输如</span>
<span class="arrow flex-be-ce" :class="{ open: open }">
>
</span>
</div>
<div class="content-wrap" :style="{ height: open ? '300px' : 0 }">
<div class="content" ref="contentRef">
<slot></slot>
</div>
</div>
</div>
</template>
<script setup>
const open = ref(false);
const contentRef = ref();
const height = ref(0);
onMounted(() => {
height.value = contentRef.value.offsetHeight + 'px';
});
</script>
<style lang="less" scoped>
.content-wrap {
height: 0;
transition: height 0.5s ease;
}
</style>
这样,我们就通过几行代码就实现了一个非常简单的折叠面板手风琴效果!
总结
要想实现一个折叠面板的效果,最简单的还是直接使用第三方组件库,但是如果项目不能使用其他组件库的话,手写一个也是非常简单的!也希望大家能在评论区给出更好的实现方式,供大家学习!