移动端友好的响应式导航栏教程
源自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