最近接受的新案子bug有点多,天天在忙着修改bug,导致更新有点慢了。。。
抽空写了个响应式的导航,个人觉得功能比较完善,有兴趣的可以一起探讨本导航主要实现了以下功能:
1.自适应浏览器窗口大小,支持pc、pad、phone等设备web浏览
2.导航栏顶部与侧边自动切换,768以下屏幕自动显示侧边导航,以上则显示顶部导航
3.本导航支持主菜单+子菜单
4.搜索样式切换,可以点击搜索展开/收起搜索框,当屏幕小于450时隐藏点击搜索时隐藏logo图案,收起时恢复。
等等功能吧,废话不多说,先来看看效果展示吧:
创建模板
还是老样式,我们先把模板搭起来,这里需要注意的事我们的菜单列表这里我写了两套,一套适应于pc端,一个适用于mobile。可能有人会说代码冗余了,所以这里我们用 的是v-if和v-else来判断的,当listShow为真时使用pc这段代码,当listShow为假时使用mobile这段代码。这里我侧边菜单用了transition过渡,如果你觉得顶部有需要可以自行添加。
<template>
<div class="navMenu">
<div class="navArea">
<div class="logo">
<div class="menuIcon">
<i class=" iconfont icon-daohang" @click="sideMenu"></i>
</div>
<div class="logoImg" v-show="logopPic">
<img :src="logo" alt="">
</div>
</div>
<ul v-if="listShow" class="menuList">
<li class="first" v-for="(menu,id) in menuData" :key="id" @click="menuShow(menu,id)" :class="{fisrtMenu:menu.bigshowclass}">
<i :class="menu.icon"></i>{{menu.title}}
<transition name="pc_fade">
<ul class="menuChild" v-show="menu.isSubShow">
<li class="second" v-for="(child,index) in menu.childs" :key="index" @click="pushpath(child,menu,index)" :class="{secondMenu:child.showclass}">
<i :class="child.icon"></i>{{child.title}}
</li>
</ul>
</transition>
</li>
</ul>
<transition name="mobileMenu" v-else>
<ul class="mobile" v-show="sideList">
<li class="first" v-for="(menu,id) in menuData" :key="id" @click="menuShow(menu,id)" :class="{fisrtMenu:menu.bigshowclass}">
<i :class="menu.icon"></i>{{menu.title}}
<transition name="mobile_fade">
<ul class="mobileChild" v-show="menu.isSubShow">
<li class="second" v-for="(child,index) in menu.childs" :key="index" @click="pushpath(child,menu,index)" :class="{secondMenu:child.showclass}">
<i :class="child.icon"></i>{{child.title}}
</li>
</ul>
</transition>
</li>
<div class="weChat">
<p>欢迎关注微信公众号:</p>
<img :src="weiImg" alt="weixin">
</div>
</ul>
</transition>
<div class="search" :class="{inputStyle:searchInput}" >
<input type="text" placeholder="请输入关键字进行搜索" v-model="keyWord" v-show="searchInput" >
<i class="iconfont icon-search" @click="search"></i>
</div>
</div>
</div>
</template>
CSS样式编写
上次写分页的时候发现样式还是挺多的,这里就不全部附上了,把关键的提取过来吧,毕竟样式对于前端来说都是so easy的,这里主要是侧边菜单的过渡效果和自适应的调整,部分写在样式里面了。实现原理在dom中定义了transition过渡,定义一个name属性,设置的过渡样式fade-enter-active和fade-leave-active分别指定了载入和离开时的动作。
.mobileMenu-enter-active {
transition: all .8s ease;
}
.mobileMenu-leave-active {
transition: all .5s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.mobileMenu-enter, .mobileMenu-leave-to{
transform: translateX(-20px);
opacity: 0;
}
.mobile_fade-enter-active{
transition: all 1s ease;
}
.mobile_fade-leave-active {
transition: all 0s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.mobile_fade-enter, .mobile_fade-leave-to{
transform: translateY();
opacity: 0;
}
@media screen and (min-width: 769px){
.logo .menuIcon i{
display: none;
}
}
@media screen and (max-width: 769px){
.logo{
flex:1;
}
}
@media screen and (max-width: 850px){
.menuList .first{
padding:0 5px;
}
}
@media screen and (max-width: 1200px){
.menuList .first i{
display: none;
}
}
功能逻辑实现
功能一:自适应屏幕菜单切换,这就要我们时刻来监听浏览器宽度了,这里我们可以使用window.addEventListener来实现监听,获取document.documentElement.clientWidth或document.body.clientWidth即为屏幕显示的宽度,拿到这个宽度我们做些判断,改变listShow、searchInput、logopPic的属性来实现自动适应屏幕样式。
mounted(){
window.addEventListener('resize', this.Switching)
},
methods: {
Switching(){
let that = this
let w = document.documentElement.clientWidth || document.body.clientWidth
if(w >= 769){
that.listShow = true; //显示pc菜单
}
if(w <769){
that.listShow = false; //显示mobile菜单
}
if(w<450){
that.searchInput=false //隐藏input框
that.logopPic=true //显示logo
that.sideList=false //隐藏侧边栏
}else{
that.sideList=true //显示侧边栏
}
if(w>1000){
that.searchInput=true //显示input框
}else{
that.searchInput=false //隐藏input框
}
},
}
功能二:搜索功能,当点击搜索图标时,判断input是否显示切有无值,如果有的话就执行搜索操作,这里我们忽略了路由请求操作,需要的自行添加,如果没有值则用来切换input状态,隐藏和现实input框,当屏幕小于450时因为宽度不够,显示时我将logo给隐藏了,隐藏时再次显示logo即可。结合前面的屏幕宽度判断,这时input框就可以很好的自适应了。
search(){
if(this.searchInput&&this.keyWord!==''){
let val=this.keyWord
}else{
let that = this
let w = document.documentElement.clientWidth || document.body.clientWidth
if(w < 450){
if(this.logopPic){
this.logopPic=false
this.searchInput=!this.searchInput
}else{
this.logopPic=true
this.searchInput=!this.searchInput
}
}else{
this.logopPic=true
this.searchInput=!this.searchInput
}
}
},
功能三:侧边菜单除了可以根据屏幕宽度来自动隐藏显示,当我们还需要可以手动展开/收起菜单,这里我在左上角留了一个图标,用来提醒用户这里是菜单按钮(小屏幕时自动收起了,pad则自动展开),在图标上添加点击事件@click="sideMenu"。
sideMenu(){
this.sideList=!this.sideList
},
功能四:最主要的菜单点击功能,点击一级菜单时改变样式,如果有二级菜单则显示二级菜单,再次点击则收起子菜单。选择二级菜单时同样改变对应样式,这样用户就可以很清楚的知道自己当前正处于哪个菜单项中了。
menuShow(menu,id){
if(menu.isSubShow){ //重置子菜单样式
if(menu.childs){
for(var i = 0;i<menu.childs.length;i++){
menu.childs[i].showclass = false;
}
}
}
for(var i = 0;i<this.menuData.length;i++){ //循环改变菜单样式,只保留当前点击的菜单样式
if(i != id){
this.menuData[i].isSubShow = false;
}
this.menuData[i].bigshowclass = false;
this.menuData[id].bigshowclass = true;
}
menu.isSubShow = !menu.isSubShow //切换子菜单显示效果
},
子菜单同理, 如果没有二级菜单的那么就需要添加路由跳转来实现页面的切换,如子菜单中的this.$router.push(child.path)即可,这里也是把其他子菜单样式清除,保留当前的菜单样式即可。
pushpath(child,menu,index){
this.$router.push(child.path) //路由跳转
let len=menu.childs
menu.isSubShow = false;
for(var i = 0;i<len.length;i++){ //循环子菜单,关闭其他样式,保留当前菜单样式
len[i].showclass = false;
len[index].showclass = true;
}
}
}
菜单数据结构
到这里基本都功能实现了,下面我们看看菜单列表是什么结构吧,这里我们把当前点击的样式状态写在了列表中,当对应的bigshowclass和showclass为真时样式生效,我们就是通过控制这些属性来实现样式的清除和添加的。
menuData: [
{
title:"一级菜单",
icon: "el-icon-message",
// path:'',
classShow: true,
bigshowclass:false,
isSubShow:false,
},
{
icon: "el-icon-message",
title: "两级菜单",
bigshowclass:false,
isSubShow:false,
childs:[
{
icon: "el-icon-loading",
title: "权限管理",
path:'',
showclass:false,
},
{
icon: "el-icon-bell",
title: "角色管理",
path:'',
showclass:false,
}
]
},
{
icon: "el-icon-news",
title: "三级菜单",
bigshowclass:false,
isSubShow:false,
},
{
icon: "el-icon-news",
title: "四级菜单",
isSubShow:false,
bigshowclass:false,
childs: [
{
icon: "el-icon-phone-outline\r\n",
title: "帐号管理",
path:'',
showclass:false,
},
{
icon: "el-icon-picture",
title: "积分管理",
path:'',
showclass:false,
}
]
},
{
icon: "el-icon-news",
title: "五级菜单",
bigshowclass:false,
isSubShow:false,
},
{
icon: "el-icon-news",
title: "六级菜单",
bigshowclass:false,
isSubShow:false,
},
],
这就是今天分享的内容了,下面是mobile端的效果。有不懂的可以留言,也欢迎指正!