logo伸缩——vue中条件渲染和动画过渡(transition,v-if,v-show)
最近在优化老的项目组件时,想利用vue自身的条件渲染和动画过渡来达到logo随侧边栏进行伸缩。目的效果如下:在进入正题前,先说说我的实现思路(部分含关键代码)。大牛请跳过,若有不足也请指正。优化方向和解决方案:伸缩状态能够被保存(F5保存,页面关闭清除):使用vuex和cookie中(可用sessionStorage代替)。// store.jsexport defau...
最近在优化老的项目组件时,想利用vue自身的条件渲染和动画过渡来达到logo随侧边栏进行伸缩。目的效果如下:
在进入正题前,先说说我的实现思路(部分含关键代码)。大牛请跳过,若有不足也请指正。
优化方向和解决方案:
- 伸缩状态能够被保存(F5保存,页面关闭清除):使用vuex和cookie中(可用sessionStorage代替)。
// store.js
export default new Vuex.Store({
state: {
sidebar: {
opened: !+Cookies.get('sidebarStatus')
}
},
mutations: {
TOGGLE_SIDEBAR: state => {
if (state.sidebar.opened) {
Cookies.set('sidebarStatus', 1)
} else {
Cookies.set('sidebarStatus', 0)
}
state.sidebar.opened = !state.sidebar.opened
}
},
actions: {
toggleSideBar ({ commit }) {
commit('TOGGLE_SIDEBAR')
}
}
})
- logo优化:svg上传阿里图标库,制作成字体来使用,一是节省图片大小,二是利于控制图片(颜色大小etc)。
- 尽量减少代码以及页面渲染消耗
<template>
<div class="sidebar-container">
<div class="logo">
<div style="height:50px;width:100%;">
<transition name="fade">
<i v-show="!isCollapse" key="logo-open" class="iconfont logo-open"></i>
<i v-show="isCollapse" key="logo-close" class="iconfont logo-close"></i>
</transition>
</div>
<!-- <transition name="fade">
<div v-show="!isCollapse" key="logo-open" style="width:200px;height:50px;"><i class="iconfont iconweizhuLOGO logo-open"></i></div>
<div v-show="isCollapse" key="logo-close" style="width:50px;height:50px;"><i class="iconfont iconweizhuLOGO1 logo-close"></i></div>
</transition> -->
</div>
<div class="nav-container">
<el-scrollbar class="page-scroll">
<el-menu class="navBar"
mode="vertical"
:default-active="$route.path"
:collapse="isCollapse"
background-color="#304156"
text-color="#ccc"
active-text-color="#fff"
unique-opened
router>
<template v-for="(item, index) in navList">
<el-submenu v-if="item.children" :index="`${index}`" :key="index">
<template slot="title">
<i class="iconfont" :class="item.icon"></i>
<span slot="title">{{item.title}}</span>
</template>
<template v-for="(item2, index2) in item.children">
<el-submenu v-if="item2.children" :index="`${index}-${index2}`" :key="`${index}-${index2}`">
<template slot="title">{{item2.title}}</template>
<el-menu-item v-for="(item3, index3) in item2.children" :index="item3.url" :key="`${index}-${index2}-${index3}`">{{item3.title}}</el-menu-item>
</el-submenu>
<el-menu-item v-else :index="item2.url" :key="`${index}-${index2}`">{{item2.title}}</el-menu-item>
</template>
</el-submenu>
<el-menu-item v-else :index="item.url" :key="index">
<i class="iconfont" :class="item.icon"></i>
<span slot="title">{{item.title}}</span>
</el-menu-item>
</template>
</el-menu>
</el-scrollbar>
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data () {
return {
navList: [
{ title: '我的应用', icon: 'iconapp', url: '/myapp/list' }
]
}
},
computed: {
...mapState([
'sidebar'
]),
isCollapse () {
return !this.sidebar.opened
}
}
}
</script>
<style lang="scss" scoped>
.logo{
position: absolute;
width: 100%;
z-index: 999;
top: 0px;
left: 0px;
height: 50px;
>div{
text-align: center;
}
.iconfont{
font-size: 28px;
color: #fff;
line-height: 50px;
}
}
.navBar /deep/{
padding-top: 90px;
border: none;
.el-menu-item, .el-submenu__title{
height: 50px;
line-height: 50px;
> i.iconfont{
position: relative;
display: inline-block;
width: 16px;
height: 16px;
font-size: 16px;
margin-right: 4px;
}
> i.iconfont::before{
position: absolute;
width: 16px;
height: 16px;
top: -16px;
left: 0;
}
}
>.el-submenu>.el-menu .el-menu-item{
position: relative;
padding-left: 53px !important;
}
>.el-submenu>.el-menu .el-menu-item::before{
content: "";
position: absolute;
top: 22px;
left: 40px;
width: 6px;
height: 6px;
border-radius: 50%;
background: #fff;
}
}
.el-menu--collapse /deep/{
width: 50px;
.el-tooltip, .el-submenu__title{
i.iconfont{
font-size: 14px;
}
}
}
.navBar:not(.el-menu--collapse){
width: 200px;
}
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
</style>
好了,进入正题。
在优化开始之前其实是没有使用vue中的transition组件的,这导致了一个logo在随侧边栏打开或者关闭的时候,过渡期间跟侧边栏无法保持同步(请原谅我的手速无法解决到哪个尴尬的场景)。很明显地可以看到,侧边是宽度有过渡效果的放大缩小,而logo是直接在放大和缩小之间进行转换,没有过渡效果的logo宽度会在侧边栏过渡期间撑爆侧边栏,看着特别不舒服。
第一个想到的是用js监听侧边栏宽度,然后动态改变logo宽度,在恰当时间点对logo进行神不知鬼不觉地替换,达到完美同步。
在理了理思路和考虑渲染成本和代码长度之后我放弃了。
因为第二个办法就是直接使用transition和v-show组合,并让logo宽度进行自适应。也就是以上代码。结果却是这样
transition里面只能使用单个元素,如果多个元素,请使用transition-group。
这里要说一下条件渲染的两个元素,v-if和v-show。
官方有详细说明,我这里只是简单总结一下。
v-if | v-show |
重新渲染dom节点,条件为假时dom不存在 | 控制css的display属性来决定是否隐藏,条件真假dom都存在 |
支持template,有附属元素v-else | 不支持template和v-else |
也就是说,上面的bug有两个解决方案,一个是v-show + transition-group或者v-if + v-else + transition。后者v-if + v-else会被视为一个元素(亲测)而v-show在这种情况下只能算两个元素。官方似乎没有说明这个隐藏属性,当然逻辑上其实也不需要太多说明。最后我这里选择了v-show + transition的方案,因为不需要重复渲染dom。
知识点GET到了之后,又进入题外,不需要的可以直接alt+f4了。
到这里为止,我以为优化工作基本完成,事实上,这种方式还有bug。在logo切换时,会有同时存在大小logo的情况,还是会错位。
官方文档有解决方案:mode=“out-in”
自定义过渡动画
<transition-group name="fade">
<i v-show="!isCollapse" key="logo-open" class="iconfont logo-open"></i>
<i v-show="isCollapse" key="logo-close" class="iconfont logo-close"></i>
</transition-group>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-leave-to{
display: none;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
当然,如果不建议这点dom节点渲染的消耗,也可以使用
<transition name="fade" mode="out-in">
<i v-if="!isCollapse" key="logo-open" class="iconfont logo-open"></i>
<i v-else key="logo-close" class="iconfont logo-close"></i>
</transition>
更多推荐
所有评论(0)