以下是译文:
今天我来教大家设计一个色彩绚丽且移动端友好的响应式导航栏。这个导航栏的灵感源自一款叫做“无主之地(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导航栏的制作,怎么样,是不是很简单啊?