欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏

程序员文章站 2022-06-18 11:17:54
...

最终效果

Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏


基本功能

  • 判断是否有二级菜单

    代码结构如下:

Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏

  • 点击显示/隐藏二级菜单

    代码结构如下:
    Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏
    根据是否存在submenu控制二级菜单的显示
  • 关于route部分 (可看代码位置)


添加伸长收缩动画

  • 原思路(获取dom)

获取dom,设置element.style.height这样的方法,以下是踩到的坑:

  1. element.style.height不能获取class中对某个属性的操作,也就是说style.height只能获得标签内联的style,如果想要获得某个属性的值需要用:window.getComputedStyle(el)
  2. 但是这个方法也带来一个问题,这个方法是只读的方法,所以只能再通过el.style.xx进行写入数据
  3. Vue中获取元素的简便方法:
    • 给元素绑定ref
    • 通过this.$refs.refName获得元素
  4. 但是在mounted阶段,获得ref元素为undefined,根据Vue的生命周期可知,created阶段为初始化数据阶段,dom还未渲染,mounted为渲染dom阶段,理论上可以获得dom节点。查阅资料后发现,当ref元素绑定v-if v-for v-show等时,此时元素变成响应式,根据后台数据进行改变,不会在mounted时渲染,在第一次update时进行渲染
  5. 个人理解 当绑定v-if v-show v-for等时,元素变成响应式,其本质会变成响应式数据,会经历这个阶段Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏
  • 后思路(直接数据绑定)

在style中声明height为响应式:
  • 当前li处于active状态时,则计算根据其子li的个数,修改其高度
  • 当li处于非响应状态时,则高度为基础高度
    Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏
    当**时增加高度
    Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏
    当退出时降低高度
    Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏
slide动画
  • height作为transition的属性时,需要设置一个初始值 一个终值才能实现动画效果
  • 父元素的overflow:hidden,当子元素超过这个部分时,则直接隐藏掉,所以enter-to设置一个一定达不到的高度,那么submenu的高度就可以只根据父元素来进行显示
  • 对于overflow:hidden的用法,这篇文章写的很好:link
    Jerry的妙妙屋-[UI组件_1]如何写一个侧边栏

实现代码

<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>