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

源自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