移动端友好的响应式导航栏教程

jopen 9年前

源自codrops团队优良的设计理念,搬运且翻译成中文。 原文地址:http://tympanus.net/codrops/2013/05/08/responsive-retina-ready-menu/

以下是译文:

今天我来教大家设计一个色彩绚丽且移动端友好的响应式导航栏。这个导航栏的灵感源自一款叫做“无主之地(Borderlands)”游戏中的一个叫做Maliwan武器生产商商标所采用的颜色集。导航栏会自动根据浏览器窗口的大小调整布局格式:在PC宽度下呈现为一行按钮,在平板宽度下呈现为三行按钮,而在移动端则变成了一个菜单栏按钮连接,点击可以显示和隐藏整个导航栏。为了使这个导航栏做到真正地移动端友好,我们将采用图标字体来作为导航栏图表,这样的话,当界面放大缩小的时候,图标也会自动调整分辨率,避免锯齿(像素过高)和模糊(像素过低)。

移动端友好的响应式导航栏教程

准备工作:选择图标字体

图标字体的制作不是一项简单的任务,不过我们完全没有必要给自己找这个麻烦。像IcoMoon,或者FontAwesome这样的插件已经为我们准备好了现成的图标以供选择,我们也可以使用这些工具快速创造自己需要的图标字体。使用图标文字的好处是这些图标像Web字体一样,我们可以轻松地改变它的颜色和大小,而不会出现类似图片放大之后模糊的情形。因此我们就省得为移动端再准备不同分辨率大小的同个图片了。

首先我们需要制作自己所需要的图标按钮(当然你也可以选择插件已经制作好的图标文字)。我主要使用Adobe Illustrator来制作矢量图,当然其他类似的矢量图制作工具也可以完成这个任务。我们首先需要在矢量图制作工具中做好所需的按钮,然后将它们导出为SVG格式的文件。为了确保在所有浏览器中显示正常,我们必须把设计元素时的不同线条组合成一个完整的对象,然后将每一个图标导入到IcoMoon App Tool中:

移动端友好的响应式导航栏教程

导入好这些元素之后,点击右下角下载旁边的设置按钮,我们可以对生成的字体进行详细的配置,包括字体编码格式,给每个字体分配对应的字符等。当然大多数情况下默认的配置是能够满足需要的,这时候我们只要直接点击下载按钮,就可以获得整个字体包和相关文件了。

移动端友好的响应式导航栏教程

获得图标字体之后,第一件事当然是放到我们的项目中查看效果。IcoMoon下载包中提供了必要的4类字体文件资源,对应不同的浏览器,同时还有响应的CSS样式。更方便的是它还提供了一个demo.html文件,我们可以直接在其中看到自定义图标的效果。

按钮的HTML实现

<nav  id="menu" class="nav">       <ul>          <li>              <a  href="#" title="">                  <span  class="icon"> <i aria-hidden="true"  class="icon-home"></i></span><span>Home</span>              </a>          </li>          <li>                <a href="#" title=""><span class="icon"> <i aria-hidden="true" class="icon-services"></i></span><span>Services</span></a>           </li>           <li>       <a  href="#" title=""><span  class="icon"><i  aria-hidden="true" class="icon-portfolio"></i></span><span>Portfolio</span></a>   </li>   <li>       <a  href="#" title=""><span  class="icon"><i  aria-hidden="true" class="icon-blog"></i></span><span>Blog</span></a>    </li>   <li>       <a  href="#" title=""><span  class="icon"><i  aria-hidden="true" class="icon-team"></i></span><span>The  team</span></a>    </li>   <li>       <a  href="#" title=""><span  class="icon"><i  aria-hidden="true" class="icon-contact"></i></span><span>Contact</span></a>          </li>      </ul>  </nav>

在网页中使用图标字体的方法非常简单,只需要在一个i标签或者span标签元素中增加名叫“icon-iconname”的class属性就可以了。这里要提一下,我们一般会在body标签中添加no-js这个class属性,当使用modernizr时,如果浏览器没有禁用javascript,那么modernizr会自动把no-js改成js。这样做的好处是,假如浏览器禁用了脚本,我们可以通过这个class属性让导航栏默认处于展开的状态。Modernizr的功能十分强大,我们还可以用它来检测移动设备对触屏的支持如何,并做出应对策略。

CSS和Javascript的实现

/* 全局CSS配置,将在所有设备下生效 */    .nav ul {   max-width: 1240px;   margin: 0;   padding: 0;   list-style: none;   font-size: 1.5em;   font-weight: 300;  }    .nav li span {   display: block;  }    .nav a {   display: block;   color: rgba(249, 249, 249, .9);   text-decoration: none;   transition: color .5s, background .5s, height .5s;  }    .nav i{   /* 确保图标文字在谷歌浏览器下显示清晰 */   transform: translate3d(0, 0, 0);  }    /* 去掉webkit内核下点击按钮出现的蓝色背景 */    a, button {   -webkit-tap-highlight-color: rgba(0,0,0,0);  }

我们先给按钮添加一个鼠标悬停效果,当鼠标悬停在按钮上方时,这个按钮的变得半透明。

/* PC端鼠标悬停在按钮上方时的特效 */    .no-touch .nav ul:hover a {   color: rgba(249, 249, 249, .5);  }    .no-touch .nav ul:hover a:hover {   color: rgba(249, 249, 249, 0.99);  }

下面我们介绍一个方法,可以在不用给每个li元素添加不同class属性的情况下,分别给他们赋予不同的显示效果,这就是li:nth-child。我们通过这种方法给列表中的每一个按钮赋予不同的颜色。
.nav li:nth-child(6n+1) {   background: rgb(208, 101, 3);  }    .nav li:nth-child(6n+2) {   background: rgb(233, 147, 26);  }    .nav li:nth-child(6n+3) {   background: rgb(22, 145, 190);  }    .nav li:nth-child(6n+4) {   background: rgb(22, 107, 162);  }    .nav li:nth-child(6n+5) {   background: rgb(27, 54, 71);  }    .nav li:nth-child(6n+6) {   background: rgb(21, 40, 54);  }

接下来介绍一下响应式界面设计基础中的基础,那就是@media的应用。这里顺带简单介绍一下em这个单位,em指的是相对于body全局字体大小,如果body全局字体大小是15px,那么50em就等于800px。这样做的好处是当body字体调整时,整个页面的大小都可以跟着动态变化,有利于移动端的适配。
@media (min-width: 50em) {     /* 当屏幕的宽度小于50em时,将水平的导航栏转换成垂直的列表格式 */   .nav li {    float: left;    width: 16.66666666666667%;    text-align: center;    transition: border .5s;   }     .nav a {    display: block;    width: auto;   }

我们给每个按钮添加选中点击时彩色的底边。加上focus和active这两个选项,这样当移动设备选中和使用键盘选中的时候也可以看到效果。
/* 悬浮、选中和点击的情况下出现彩色的底边 */     .no-touch .nav li:nth-child(6n+1) a:hover,   .no-touch .nav li:nth-child(6n+1) a:active,   .no-touch .nav li:nth-child(6n+1) a:focus {    border-bottom: 4px solid rgb(174, 78, 1);   }     .no-touch .nav li:nth-child(6n+2) a:hover,   .no-touch .nav li:nth-child(6n+2) a:active,   .no-touch .nav li:nth-child(6n+2) a:focus {    border-bottom: 4px solid rgb(191, 117, 20);   }     .no-touch .nav li:nth-child(6n+3) a:hover,   .no-touch .nav li:nth-child(6n+3) a:active,   .no-touch .nav li:nth-child(6n+3) a:focus {    border-bottom: 4px solid rgb(12, 110, 149);   }     .no-touch .nav li:nth-child(6n+4) a:hover,   .no-touch .nav li:nth-child(6n+4) a:active,   .no-touch .nav li:nth-child(6n+4) a:focus {    border-bottom: 4px solid rgb(10, 75, 117);   }     .no-touch .nav li:nth-child(6n+5) a:hover,   .no-touch .nav li:nth-child(6n+5) a:active,   .no-touch .nav li:nth-child(6n+5) a:focus {    border-bottom: 4px solid rgb(16, 34, 44);   }     .no-touch .nav li:nth-child(6n+6) a:hover,   .no-touch .nav li:nth-child(6n+6) a:active,   .no-touch .nav li:nth-child(6n+6) a:focus {    border-bottom: 4px solid rgb(9, 18, 25);   }

调整图标的相对位置和渐变效果:
/* 调整图标的位置 */   .icon {    padding-top: 1.4em;   }     .icon + span {    margin-top: 2.1em;    transition: margin .5s;   }

当鼠标悬停在按钮上方时,改变元素的高度,产生动画效果:
/* 鼠标悬停的时候出现高度变化动画 */   .nav a {    height: 9em;   }     .no-touch .nav a:hover ,   .no-touch .nav a:active ,   .no-touch .nav a:focus {    height: 10em;   }      /* 这里让文字跟着整个元素的高度变化而变化 */   .no-touch .nav a:hover .icon + span {    margin-top: 3.2em;    transition: margin .5s;   }

调整按钮的位置,并设计CSS渐变效果:
/* 调整按钮的位置,并设计CSS渐变效果 */   .nav i {    position: relative;    display: inline-block;    margin: 0 auto;    padding: 0.4em;    border-radius: 50%;    font-size: 1.8em;    box-shadow: 0 0 0 0.8em transparent;    background: rgba(255,255,255,0.1);    transform: translate3d(0, 0, 0);    transition: box-shadow .6s ease-in-out;   }

为了获得我们想要的效果,我们改变盒模型的阴影,并将它的大小从0.8em渐变到0,将它的颜色从透明转变到一个相对不透明的状态。
/* 盒模型阴影动画效果 */   .no-touch .nav a:hover i,   .no-touch .nav a:active i,   .no-touch .nav a:focus i {      box-shadow: 0 0 0px 0px rgba(255,255,255,0.2);    transition: box-shadow .4s ease-in-out;   }      }

稍微调整一下屏幕宽度在800-980px之间时的位置:
@media (min-width: 50em) and (max-width: 61.250em) {     /* Size and font adjustments to make it fit better */   .nav ul {    font-size: 1.2em;   }    }

这样算是完成了PC端的设计了,当然现在很多平板甚至手机的分辨率都已经达到1024px以上了,PC和移动端的界限越来越模糊。
/* 设计800px以下的屏幕布局样式和特效 */    @media (max-width: 49.938em) {        /* 多排按钮不能用改变border的特效,我们换成改变背景透明度的特效 */   .no-touch .nav ul li:nth-child(6n+1) a:hover,   .no-touch .nav ul li:nth-child(6n+1) a:active,   .no-touch .nav ul li:nth-child(6n+1) a:focus {    background: rgb(227, 119, 20);   }     .no-touch .nav li:nth-child(6n+2) a:hover,   .no-touch .nav li:nth-child(6n+2) a:active,   .no-touch .nav li:nth-child(6n+2) a:focus {    background: rgb(245, 160, 41);   }     .no-touch .nav li:nth-child(6n+3) a:hover,   .no-touch .nav li:nth-child(6n+3) a:active,   .no-touch .nav li:nth-child(6n+3) a:focus {    background: rgb(44, 168, 219);   }     .no-touch .nav li:nth-child(6n+4) a:hover,   .no-touch .nav li:nth-child(6n+4) a:active,   .no-touch .nav li:nth-child(6n+4) a:focus {    background: rgb(31, 120, 176);   }     .no-touch .nav li:nth-child(6n+5) a:hover,   .no-touch .nav li:nth-child(6n+5) a:active,   .no-touch .nav li:nth-child(6n+5) a:focus {    background: rgb(39, 70, 90);   }     .no-touch .nav li:nth-child(6n+6) a:hover,   .no-touch .nav li:nth-child(6n+6) a:active,   .no-touch .nav li:nth-child(6n+6) a:focus {    background: rgb(32, 54, 68);   }     .nav ul li {    transition: background 0.5s;   }     }

屏幕大小在520px到799px之间时,让导航栏按照2X3的形式进行布局。
/* 导航栏按照2X3的形式布局 */    @media (min-width: 32.5em) and (max-width: 49.938em) {      /* 用左浮动的方法实现两个按钮并排 */   .nav li {    display: block;    float: left;    width: 50%;   }      /* 加入padding使得元素按钮看上去好看些 */   .nav a {    padding: 0.8em;     }     /* 用inline-block可以让按钮在左边,文字在右边 */   .nav li span,    .nav li span.icon {    display: inline-block;   }     .nav li span.icon {    width: 50%;   }     .nav li .icon + span {    font-size: 1em;   }     .icon + span {    position: relative;    top: -0.2em;   }

PC端的元素大小变化无法在平板布局下实现,因此我们简化动画效果。
/* 在平板布局的情形下,我们以一种更为保守的方式设计动画,只改变一下border的大小 */   .nav li i {    display: inline-block;    padding: 8% 9%;    border: 4px solid transparent;    border-radius: 50%;    font-size: 1.5em;    background: rgba(255,255,255,0.1);    transition: border .5s;   }     /* border颜色渐变效果 */   .no-touch .nav li:hover i,   .no-touch .nav li:active i,   .no-touch .nav li:focus i {    border: 4px solid rgba(255,255,255,0.1);   }    }

平板布局下,调整文字的大小和宽度
/* 平板布局下,调整文字的大小和宽度 */  @media (min-width: 32.5em) and (max-width: 38.688em) {      .nav li span.icon {    width: 50%;   }     .nav li .icon + span {    font-size: 0.9em;   }  }

对于更小的移动设备,我们可以隐藏整个导航栏,然后显示一个导航按钮,这样通过点击导航按钮来显示和隐藏导航栏。这个功能可能需要通过Javascript来实现。(译者:更简单的途径是使用jQuery的toggleClass方法实现)。
//  切换class属性的方法  var changeClass = function (r,className1,className2) {   var regex = new RegExp("(?:^|\\s+)" + className1 + "(?:\\s+|$)");   if( regex.test(r.className) ) {    r.className = r.className.replace(regex,' '+className2+' ');      }      else{    r.className = r.className.replace(new RegExp("(?:^|\\s+)" + className2 + "(?:\\s+|$)"),' '+className1+' ');      }      return r.className;  };     //  写入菜单栏按钮  var menuElements = document.getElementById('menu');  menuElements.insertAdjacentHTML('afterBegin','<button type="button" id="menutoggle" class="navtoogle" aria-hidden="true"><i aria-hidden="true" class="icon-menu"> </i> Menu</button>');    //  点击菜单栏按钮时切换class属性  document.getElementById('menutoggle').onclick = function() {   changeClass(this, 'navtoogle active', 'navtoogle');  }    // 点击文档其他位置时自动隐藏导航栏  document.onclick = function(e) {   var mobileButton = document.getElementById('menutoggle'),    buttonStyle =  mobileButton.currentStyle ? mobileButton.currentStyle.display : getComputedStyle(mobileButton, null).display;     if(buttonStyle === 'block' && e.target !== mobileButton && new RegExp(' ' + 'active' + ' ').test(' ' + mobileButton.className + ' ')) {    changeClass(mobileButton, 'navtoogle active', 'navtoogle');   }  }

这一段采用Javascript原生的方法来进行class属性的切换和点击动作进行DOM文档操作的实现。当按钮点击的时候,首先判断按钮带有什么属性,然后改成另一个属性,这样就实现了class属性的切换。实现了导航栏的隐藏与显示之后,我们接着设置一下样式。
/* 导航栏按钮的样式,默认隐藏 */  .nav .navtoogle{   display: none;    width: 100%;   padding: 0.5em 0.5em 0.8em;   font-family: 'Lato',Calibri,Arial,sans-serif;   font-weight: normal;   text-align: left;   color: rgb(7, 16, 15);   font-size: 1.2em;   background: none;    border: none;   border-bottom: 4px solid rgb(221, 221, 221);   cursor: pointer;  }    .navtoogle i{   z-index:-1;  }    .icon-menu {   position: relative;   top: 3px;   line-height: 0;   font-size: 1.6em;  }

如果检测到屏幕小于519px,则显示这个导航栏按钮
@media (max-width: 32.438em) {     /* 显示导航栏按钮 */   .nav .navtoogle{    margin: 0;    display: block;   }

接下来我们实现导航栏的显示和隐藏动画。我们通过元素的高度控制显示和隐藏。特别要提出,如果检测到页面禁用了脚本,我们应该让导航栏默认是显示的。
/* 如果禁用了脚本,那么导航栏总是显示的。 */   .no-js .nav ul {    max-height: 30em;    overflow: hidden;   }     /* 如果脚本是可生效的,那么我们控制高度为0,隐藏之 */   .js .nav ul {    max-height: 0em;    overflow: hidden;   }      /* class的active属性激活之后,给max-height一个动画效果,就能够实现导航栏向下滚动出现的特效 */   .js .nav .active + ul {      max-height: 30em;    overflow: hidden;    transition: max-height .4s;   }

然后给这个小屏导航栏设置样式
/* 小屏幕导航栏样式设置 */      .nav li span {    display: inline-block;    height: 100%;   }     .nav a {    padding: 0.5em;     }      .icon + span {    margin-left: 1em;    font-size: 0.8em;   }

左侧增加一个border效果(设计师需要多多关注这些细节:))
/* 左侧增加一个border效果 */   .nav li:nth-child(6n+1) {    border-left: 8px solid rgb(174, 78, 1);   }     .nav li:nth-child(6n+2) {    border-left: 8px solid rgb(191, 117, 20);   }     .nav li:nth-child(6n+3) {    border-left: 8px solid rgb(13, 111, 150);   }     .nav li:nth-child(6n+4) {    border-left: 8px solid rgb(10, 75, 117);   }     .nav li:nth-child(6n+5) {    border-left: 8px solid rgb(16, 34, 44);   }     .nav li:nth-child(6n+6) {    border-left: 8px solid rgb(9, 18, 25);   }

接下来很多工作可能都是在调整移动端设备的效果上面了。我们使用Modernizr这个工具可以很方便的帮我们识别移动设备的兼容性,这样我们可以通过class属性来为特性支持或者特性不支持的设备进行调整。比如这个导航栏的按钮在PC下面显示正常,但是同样的样式在手机设备上可能显得太小,不好按。这时候我们这样可以进行样式的调整。
/* 让导航栏在触屏设备下看起来更舒适一些 */   .touch .nav a {    padding: 0.8em;   }  }

这样我们就完成了一个相当绚丽的CSS3导航栏的制作,怎么样,是不是很简单啊?


来自:http://my.oschina.net/jndion/blog/418964