Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏
程序员文章站
2022-06-18 11:17:54
...
最终效果
基本功能
添加伸长收缩动画
获取dom,设置element.style.height这样的方法,以下是踩到的坑:
- element.style.height不能获取class中对某个属性的操作,也就是说style.height只能获得标签内联的style,如果想要获得某个属性的值需要用:window.getComputedStyle(el)
- 但是这个方法也带来一个问题,这个方法是只读的方法,所以只能再通过el.style.xx进行写入数据
- Vue中获取元素的简便方法:
- 给元素绑定ref
- 通过this.$refs.refName获得元素
- 但是在mounted阶段,获得ref元素为undefined,根据Vue的生命周期可知,created阶段为初始化数据阶段,dom还未渲染,mounted为渲染dom阶段,理论上可以获得dom节点。查阅资料后发现,当ref元素绑定v-if v-for v-show等时,此时元素变成响应式,根据后台数据进行改变,不会在mounted时渲染,在第一次update时进行渲染
- 个人理解 当绑定v-if v-show v-for等时,元素变成响应式,其本质会变成响应式数据,会经历这个阶段
在style中声明height为响应式:
- 当前li处于active状态时,则计算根据其子li的个数,修改其高度
- 当li处于非响应状态时,则高度为基础高度
当**时增加高度
当退出时降低高度
slide动画
- height作为transition的属性时,需要设置一个初始值 一个终值才能实现动画效果
- 父元素的overflow:hidden,当子元素超过这个部分时,则直接隐藏掉,所以enter-to设置一个一定达不到的高度,那么submenu的高度就可以只根据父元素来进行显示
- 对于overflow:hidden的用法,这篇文章写的很好:link
实现代码
<template>
<div>
<p ref="test"></p>
<ul :id="'menu_'+Theme">
<li :class="'menuItemLi_'+Theme"
:style="{height:((index == menuActive) ? liHeight:baseLiHeight)+'rem'}"
v-for="(item,index) in MenuList">
<p :class="[ 'menuItem_'+Theme,(index == menuActive ? 'activeFontColor':'')]"
@click="handleClick(index)">{{ item.menuName }}</p>
<template v-if="item.subMenu">
<transition name="slide">
<template v-if="menuActive == index">
<ul>
<li v-for="(subItem,subIndex) in item.subMenu" ref="subLi">
<p :class="[(subIndex == subMenuActive)?'activeFontColor':'','subMenuItem_'+Theme]"
@click="handleSubMenuClick(subIndex)">
{{subItem.submenuName }}</p>
</li>
</ul>
</template>
</transition>
</template>
</li>
</ul>
</div>
</template>
<script>
export default {
/**
* Menulist:{
* menuName:一级菜单名
* menuUrl:一级菜单链接
* subMenu{
* subMenuItem:二级菜单名
* subMenuUrl:二级菜单链接
* }
* }
* Theme:dark/light 主题
* openItem:number 默认展开的菜单
* accordion:boolean 是否展开手风琴模式
*/
name: 'beike-menu',
props: {
MenuList: {
type: Array,
default: () => [],
},
Theme: {
type: String,
default: 'dark',
},
DefaultActive: {
type: String,
default: '2',
},
},
data() {
return {
offsetHeight: 0.1,
liHeight: 0,
subLiHeight: 0,
baseLiHeight: 0,
activeItem: null,
menuActive: 0,
subMenuActive: 0,
subItemStretchTime: 100,
};
},
methods: {
clearSubStatus: function () {
this.liHeight = this.baseLiHeight;
this.subMenuActive = 0;
this.activeItem = null;
},
handleClick: function ( index ) {
//点击一级菜单 首先子菜单清零 设置为默认值
this.clearSubStatus();
//判断如果点击的是已经显示的菜单 则收回菜单 设置mentActive为-1
if (index === this.menuActive) {
this.removeActive();
return;
}
this.activeItem = this.MenuList[ index ];
this.menuActive = index;
//如果是新的菜单 则判断是否有子菜单
// 如果无 1.则设置index为active值 2. push route
if (!this.activeItem.subMenu) {
// console.log('asa');
this.$router.push(this.activeItem.menuUrl);
return;
}
//如果有 1.设置默认子菜单为0 push route 2.添加active class的状态
this.$router.push(this.activeItem.subMenu[ 0 ].submenuUrl);
let num = this.activeItem.subMenu.length;
this.addActive(num);
},
handleSubMenuClick: function ( subIndex ) {
this.subMenuActive = subIndex;
this.$router.push(this.activeItem.subMenu[ subIndex ].submenuUrl);
},
addActive: function ( num ) { // 传入当前点击li标签 和li标签下的subli的个数
//1.获取子菜单li个数
//2.根据个数和li高度进行setIntervel递增
//3.当达到最大值时height停止
if (this.liHeight >= (this.baseLiHeight + this.subLiHeight * num)) {
return;
}
this.liHeight += 0.1;
// el.style.height = (this.liHeight) + 'rem';
setInterval(this.addActive(num), this.subItemStretchTime);
},
removeActive: function () {
if (this.liHeight <= this.baseLiHeight) {
this.menuActive = -1;
return;
}
this.liHeight -= 0.1;
setInterval(this.removeActive(), this.subItemStretchTime);
},
initLisHeight: function () {
this.baseLiHeight = 3;
this.liHeight = 3;
this.subLiHeight = 1.5;
},
initDefaultActive: function () {
this.menuActive = this.DefaultActive;
this.activeItem = this.MenuList[ this.menuActive ];
if (!this.activeItem.subMenu) {
this.$router.push(this.activeItem.menuUrl);
} else {
this.$router.push(this.activeItem.subMenu[ 0 ].submenuUrl);
let num = this.activeItem.subMenu.length;
console.log(num);
this.addActive(num);
}
},
},
mounted() {
this.initLisHeight();
this.initDefaultActive();
console.log(this.MenuList);
},
};
</script>
<style scoped>
.slide-enter-active, .slide-leave-active {
transition: all 0.5s;
height: auto;
overflow: hidden;
}
.slide-enter-to, .slide-leave {
height: 1000rem;
}
.slide-enter, .slide-leave-to {
opacity: 0%;
height: 0px;
overflow: hidden;
}
</style>
<style lang="scss">
$light: light;
$dark: dark;
$lightFontColor: black;
$darkFontColor: white;
$lightBgColor: white;
$darkBgColor: rgb(54, 62, 79);
$lightHoverColor: rgba(200, 200, 200, 0.4);
$darkHoverColor: rgb(45, 140, 140);
$activeFontColor: rgb(45, 180, 180);
@function getHoverColor( $x ) {
@if ($x == 'light') {
@return $lightHoverColor;
} @else if ($x == 'dark') {
@return $darkHoverColor;
}
}
@function getBgColor( $x ) {
@if ($x == 'light') {
@return $lightBgColor;
} @else if ($x == 'dark') {
@return $darkBgColor;
}
}
@function getFontColor( $x ) {
@if ($x == 'light') {
@return $lightFontColor;
} @else if ($x == 'dark') {
@return $darkFontColor;
}
}
.activeFontColor {
color: $activeFontColor
}
@each $theme in $light, $dark {
#menu_#{$theme} {
position: relative;
height: 100vh;
width: 20rem;
background-color: getBgColor($theme);
border: 0.05rem solid rgba(220, 220, 220, 0.8);
color: getFontColor($theme);
.menuItemLi_#{$theme} {
position: relative;
overflow: hidden;
height: 6rem;
transition: height 0.4s ease-in-out;
.menuItem_#{$theme} {
position: relative;
display: block;
line-height: 100%;
background-color: getBgColor($theme);
padding-left: 1rem;
height: 3rem;
width: 100%;
padding-top: 0.8rem;
transition: 0.3s all ease-in-out;
&:hover {
background-color: getHoverColor($theme);
}
}
.subMenuItem_#{$theme} {
display: block;
overflow: hidden;
background-color: getBgColor($theme);
padding-left: 1.5rem;
font-size: 0.9rem;
font-weight: lighter;
transition: 0.3s all ease-in-out;
&:hover {
background-color: getHoverColor($theme);
}
}
}
}
}
</style>
上一篇: 把红包纸递了出去
下一篇: 详解C++ 的STL迭代器原理和实现