唯心主义蠢货的[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等时,元素变成响应式,其本质会变成响应式数据,会经历这个阶段在这里插入图片描述
  • 后思路(直接数据绑定)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值