全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-06-28_改进菜单栏动态展示样式,我被评上优秀开发!

您的位置:首页 >> 新闻 >> 行业资讯

改进菜单栏动态展示样式,我被评上优秀开发! 点击关注公众号,“技术干货”及时达!背景我们公司的导航菜单是动态可配置的,有的页面菜单数量比较多,有的比较少。 由于大多页面菜单都是比较少的,因此当菜单非常多时, 我们采用了朴实无华的滚动条:当横向超出的时候,滚动展示。 但很快,客户就打回来了:说我们的样式太丑,居然用滚动条!还质问我们产品这合理吗?产品斩钉截铁的告诉客户,我让开发去优化... 于是,领导让我们想解决方案。(我真谢谢产品!) 很快,我想到一个方案(从其他地方看到的交互),我告诉领导: 我们可以做成动态菜单栏,如果展示不下了,出现一个更多按钮,多余的菜单都放到更多里面去: 领导说这个想法不错啊,那就你来实现吧! 好家伙,我只是随便说说,没想到,自己给自己挖了个大坑啊! 不过,我最后也是顺利的完成了这个效果的开发,还被评上了本季度优秀开发!分享一下自己的实现方案吧! 技术方案基础组件样式开发既然要开发这个效果,干脆就封装一个通用组件AdaptiveMenuBar.vue吧。我们先写一下基本样式,如图,灰色区域就是我们的组件内容,也就是我们菜单栏动态展示的区域。 AdaptiveMenuBar.vue template divclass="adaptive-menu-bar" /div /template stylelang="less"scoped .adaptive-menu-bar{ width:100%; height:48px; background:gainsboro; display:flex; position:relative; overflow:hidden; } /style 我们写点假数据 template divclass="adaptive-menu-bar" divclass="origin-menu-item-wrap" divv-for="(item,index)inmenuOriginData":key="index"class="menu-item" {{item.name}} /div /div div更多/div /div /template scriptsetup constmenuOriginData=[ {name:'哆啦a梦',id:1}, {name:'宇智波佐助',id:1}, {name:'香蕉之王奥德彪',id:1}, {name:'漩涡鸣人',id:1}, {name:'雏田',id:1}, {name:'大雄',id:1}, {name:'源静香',id:1}, {name:'骨川小夫',id:1}, {name:'超级马里奥',id:1}, {name:'自来也',id:1}, {name:'孙悟空',id:1}, {name:'卡卡罗特',id:1}, {name:'万年老二贝吉塔',id:1}, {name:'小泽玛丽',id:1} ]; /script stylelang="less"scoped .adaptive-menu-bar{ width:100%; height:48px; background:gainsboro; display:flex; position:relative; overflow:hidden; .origin-menu-item-wrap{ width:100%; display:flex; } } /style 如图,由于菜单数量比较多,一部分已经隐藏在origin-menu-item-wrap这个父元素里面了。 实现思路那我们要如何才能让多余的菜单出现在【更多】按钮里呢?原理很简单,我们只要计算出哪个菜单超出展示区域即可。假设如图所示,第12个菜单被截断了,那我们前11个菜单就可以展示在显示区域,剩余的菜单就展示在【更多】按钮里。 更多按钮的展示逻辑更多按钮只有在展示区域空间不够的时候出现,也就是origin-menu-item-wrap元素的滚动区域宽度scrollWidth 大于其宽度clientWidth的时候。 用代码展示大致如下 template divref="menuBarRef"class="origin-menu-item-wrap" divv-for="(item,index)inmenuOriginData":key="index"class="menu-item" m-buttontype="default"size="small"{{item.name}}/m-button /div /div /template scriptsetup constmenuOriginData=[ {name:'哆啦a梦',id:1}, {name:'宇智波佐助',id:1}, {name:'香蕉之王奥德彪',id:1}, {name:'漩涡鸣人',id:1}, {name:'雏田',id:1}, {name:'大雄',id:1}, {name:'源静香',id:1}, {name:'骨川小夫',id:1}, {name:'超级马里奥',id:1}, {name:'自来也',id:1}, {name:'孙悟空',id:1}, {name:'卡卡罗特',id:1}, {name:'万年老二贝吉塔',id:1}, {name:'小泽玛丽',id:1} ]; //是否展示更多按钮 constshowMoreBtn=ref(false); onMounted(()={ constmenuWrapDom=menuBarRef.value; if(menuWrapDom.scrollWidthmenuWrapDom.clientWidth){ showMoreBtn.value=true; } }); /script 截断位置的计算要计算截断位置,我们需要先渲染好菜单。 然后开始对menu-item元素宽度进行加和,当相加的宽度大于菜单展示区域的宽度clientWidth时,计算终止,此时的menu-item元素就是我们要截断的位置。 菜单截断的部分,我们此时放到更多里面展示就可以了。 大致代码如下: template divref="menuBarRef"class="origin-menu-item-wrap" divv-for="(item,index)inmenuOriginData":key="index"class="menu-item" m-buttontype="default"size="small"{{item.name}}/m-button /div /div /template scriptsetup constmenuOriginData=[ {name:'哆啦a梦',id:1}, {name:'宇智波佐助',id:1}, {name:'香蕉之王奥德彪',id:1}, {name:'漩涡鸣人',id:1}, {name:'雏田',id:1}, {name:'大雄',id:1}, {name:'源静香',id:1}, {name:'骨川小夫',id:1}, {name:'超级马里奥',id:1}, {name:'自来也',id:1}, {name:'孙悟空',id:1}, {name:'卡卡罗特',id:1}, {name:'万年老二贝吉塔',id:1}, {name:'小泽玛丽',id:1} ]; //是否展示更多按钮 constshowMoreBtn=ref(false); onMounted(()={ constmenuWrapDom=menuBarRef.value; if(menuWrapDom.scrollWidthmenuWrapDom.clientWidth){ showMoreBtn.value=true; } //计算截断菜单的索引位置 letsliceIndex=0 //获取menu-item元素dom的集合 constmenuItemNodeList=menuWrapDom.querySelectorAll('.menu-item'); //将NodeList转换成数组 constnodeArray=Array.prototype.slice.call(menuItemNodeList); letaddWidth= for(leti=inodeArray.length;i++){ constnode=nodeArray[i]; //clientWidth不包含菜单的margin边距,因此我们手动补上12px addWidth+=node.clientWidth+ //76是更多按钮的宽度,我们也要计算进去 if(addWidth+76middleDom.clientWidth){ sliceIndex.value= break; }else{ sliceIndex.value= } } }); /script 样式重整当被截断的元素计算完毕时,我们需要重新进行样式渲染,但是注意,我们原先渲染的菜单列不能注销,因为每次浏览器尺寸变化时,我们都是基于原先渲染的菜单列进行计算的。 所以,我们实际需要渲染两个菜单列:一个原始的,一个样式重新排布后的。 如上图,黄色就是原始的菜单栏,用于计算重新排布的菜单栏,只不过,我们永远不在页面上展示给用户看! template divclass="adaptive-menu-bar" !--原始渲染的菜单栏-- divref="menuBarRef"class="origin-menu-item-wrap" divv-for="(item,index)inmenuOriginData":key="index"class="menu-item" m-buttontype="default"size="small"{{item.name}}/m-button /div /div !--计算优化显示的菜单栏-- divv-for="(item,index)inmenuList":key="index"class="menu-item" m-buttontype="default"size="small"{{item.name}}/m-button /div div更多/div /div /template 代码实现基础功能完善为了我们的菜单栏能动态的响应变化,我们需要再每次resize事件触发时,都重新计算样式 constmenuOriginData=[ {name:'哆啦a梦',id:1}, {name:'宇智波佐助',id:1}, {name:'香蕉之王奥德彪',id:1}, {name:'漩涡鸣人',id:1}, {name:'雏田',id:1}, {name:'大雄',id:1}, {name:'源静香',id:1}, {name:'骨川小夫',id:1}, {name:'超级马里奥',id:1}, {name:'自来也',id:1}, {name:'孙悟空',id:1}, {name:'卡卡罗特',id:1}, {name:'万年老二贝吉塔',id:1}, {name:'小泽玛丽',id:1} ]; //是否展示更多按钮 constshowMoreBtn=ref(false); constsetHeaderStyle=()={ //.... } window.addEventListener('resize',()=setHeaderStyle()); onMounted(()={ setHeaderStyle(); }); /script 完整代码完整代码剥离了一些第三方UI组件,便于大家理解。 template divclass="adaptive-menu-bar" !--原始渲染的菜单栏-- divref="menuBarRef"class="origin-menu-item-wrap" divv-for="(item,index)inmenuOriginData":key="index"class="menu-item" {{item.name}} /div /div !--计算优化显示的菜单栏-- divv-for="(item,index)inmenuList":key="index"class="menu-item" {{item.name}} /div !--更多按钮-- divv-if="showMoreBtn"class="dropdown-wrap" span更多/span !--更多里面的菜单-- divclass="menu-item-wrap" divv-for="(item,index)inmenuOriginData.slice(menuList.length)":key="index"{{item.name}}/div /div /div /div /template scriptsetup import{IconMeriComponentArrowDown}from'meri-icon'; constmenuBarRef=ref(); constopen=ref(false); constmenuOriginData=[ {name:'哆啦a梦',id:1}, {name:'宇智波佐助',id:1}, {name:'香蕉之王奥德彪',id:1}, {name:'漩涡鸣人',id:1}, {name:'雏田',id:1}, {name:'大雄',id:1}, {name:'源静香',id:1}, {name:'骨川小夫',id:1}, {name:'超级马里奥',id:1}, {name:'自来也',id:1}, {name:'孙悟空',id:1}, {name:'卡卡罗特',id:1}, {name:'万年老二贝吉塔',id:1}, {name:'小泽玛丽',id:1} ]; constmenuList=ref(menuOriginData); //是否展示更多按钮 constshowMoreBtn=ref(false); constsetHeaderStyle=()={ constmenuWrapDom=menuBarRef.value; if(!menuWrapDom)return; if(menuWrapDom.scrollWidthmenuWrapDom.clientWidth){ showMoreBtn.value=true; }else{ showMoreBtn.value=false; } constmenuItemNodeList=menuWrapDom.querySelectorAll('.menu-item'); if(menuItemNodeList){ letaddWidth=0, sliceIndex=0; //将NodeList转换成数组 constnodeArray=Array.prototype.slice.call(menuItemNodeList); for(leti=0;inodeArray.length;i++){ constnode=nodeArray[i]; addWidth+=node.clientWidth+12; if(addWidth+64+12menuWrapDom.clientWidth){ sliceIndex= break; }else{ sliceIndex=0; } } if(sliceIndex0){ menuList.value=menuOriginData.slice(0,sliceIndex); }else{ menuList.value=menuOriginData; } } }; window.addEventListener('resize',()=setHeaderStyle()); onMounted(()={ setHeaderStyle(); }); /script stylelang="less"scoped .adaptive-menu-bar{ width:100%; height:48px; background:gainsboro; display:flex; position:relative; align-items:center; overflow:hidden; .origin-menu-item-wrap{ width:100%; display:flex; position:absolute; top:49px; display:flex; align-items:center; left:0; right:0; bottom:0; height:48px; z-index:9; } .menu-item{ margin-left:12px; } .dropdown-wrap{ width:64px; display:flex; align-items:center; cursor:pointer; justify-content:center; height:28px; background:#fff; border-radius:4px; overflow:hidden; border:1pxsolid#c4c9cf; background:#fff; margin-left:12px; .icon{ width:16px; height:16px; margin-left:4px; } } } /style 代码效果可以看到,非常丝滑! 点击关注公众号,“技术干货”及时达! 阅读原文

上一篇:2025-03-13_前端国际化全场景支持方案,不入侵代码,一键完成 (vue23、react)。 下一篇:2024-05-24_总结了五点,带你看懂影视IP营销的正确打开方式

TAG标签:

19
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价