收藏本站腾讯微博新浪微博

经典论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

蓝色理想 最新研发动态 网站开通淘帖功能 - 蓝色理想插件 论坛内容导读一页看论坛 - 给官方提建议

论坛活动及任务 地图和邮件任务 请多用悬赏提问 热夏来袭,选一款蓝色理想的个性T恤吧!

手机上论坛,使用APP获得更好体验 急需前端攻城狮,获得内部推荐机会 论坛开通淘帖功能,收藏终于可以分类了!

搜索
查看: 6173|回复: 24

[教程] 【教程】弧形排列的可折叠二级导航 - 制作思路(布局篇)

[复制链接]
发表于 2009-3-20 14:32:32 | 显示全部楼层 |阅读模式
前段时间做了一个“弧形排列的导航”,响应版主的建议,现整理一个详细的教程。本文以解决问题的思路和方法为线索进行讨论,基础的 CSS 知识就不赘述了。另外本文仅涉及基本布局,关于 JS 特效的实现有机会再另行撰文。

[[i] 本帖最后由 birdstudio 于 2009-7-17 11:00 编辑 ]
 楼主| 发表于 2009-3-20 14:33:19 | 显示全部楼层
Step 0: 分析设计稿



接到项目后的第一件事,不是切图,也不是写代码,而是分析设计稿。

首先我们会发现,设计稿中最特别的地方在于各菜单项贴合图片的曲线,呈现不规则的排列。那么似乎为各菜单项分别定义外边距(或文本缩进)就可以实现了。然而事情并没有这么简单,UI 设计师提醒我们,这个菜单需要实现动态的“手风琴效果”,二级菜单不论展开与否,所有菜单项都必须自然地贴合图片轮廓。因此,各菜单项的位置不能定死。看来我们要换一种思路。

当菜单展开与折叠时,用 JS 来计算各菜单项应该出现的位置并重新定位,这是我想到的第一个方法。很显然,这是可行的。但此时我想给自己一个小挑战,不依赖 JS,完全用 CSS 来实现这个布局要求。表现层的事,JS 越少插手越好——这是我给自己定下的一个基本原则。而且这也不是很紧迫的任务,就让我再多思考一下吧。

文字在纵向自然流动,在水平方向自动避开某个区域,这不得不让我们联想到浮动的某些特性。事实上这个问题很像是 CSS 教材中常常会提到的“异形排版”——把文字灌入到一个不规则的容器当中。它的实现原理就是用一些无语义的空元素,通过浮动来占据某些区域,文字流动时会自动避开这些区域,从而呈现不规则排版。简单分析一下,我们眼前的这个项目似乎也可以用到这个方法。那么,开始干吧!

[[i] 本帖最后由 birdstudio 于 2010-1-4 20:17 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:34:05 | 显示全部楼层
Step 1: 勾勒曲线

首先我们需要确定这些浮动占位元素的尺寸。打开 Photoshop,用路径工具勾出曲线的大致轮廓,限定文字流动区域的右边缘。

示意图(已缩小至50%):


[[i] 本帖最后由 birdstudio 于 2010-1-4 20:16 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:34:43 | 显示全部楼层
Step 2: 量化曲线

利用 Photoshop 的网格和参考线工具将曲线进行栅格化,即量化为多个长方形。因为 CSS 目前还只能处理矩形元素,我们只能用矩形来模拟曲线的形状。我采用的量化精度是 5px。我觉得这是一个适中的数字,精度更高也没有意义了,反而增加无谓的资源消耗。

示意图(原比例局部图):


[[i] 本帖最后由 birdstudio 于 2010-1-4 20:16 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:35:29 | 显示全部楼层
Step 3: 收集量化数据

度量并记录一下曲线栅格化后得到的数据(也就是这些长方形的高宽尺寸)。我们稍后将利用这些数据在页面中建立占位元素。

在下面的示意图中,我用半透明的黄色矩形来表示这些占位元素的尺寸和位置。这些矩形之间的间隙是实际上是不存在的,仅作示意。

示意图(已缩小至50%):


[[i] 本帖最后由 birdstudio 于 2010-1-4 20:16 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:36:14 | 显示全部楼层
Step 4: 写结构

图片编辑工作到此结束,下面打开文本编辑器,开始根据导航的结构编写 HTML 代码。

平时我们在制作导航时,都是采用无序列表,这几乎已经成为公认的法则了,那么今天也不例外。这里的二级导航,与下拉菜单有相似之处,我决定通过无序列表的嵌套来表达菜单的层级关系。那么结构代码大致应该是这个样子的:


  1. <div id="dv_nav">
  2.         <h2>导航</h2>
  3.         <ul id="ul_nav">
  4.                 <li>
  5.                         <h3><span>洗护系列</span></h3>
  6.                         <ul>
  7.                                 <li><a href="###">赋活水养</a></li>
  8.                                 <li><a href="###">动感卷曲</a></li>
  9.                                 <li><a href="###">……</a></li>
  10.                         </ul>
  11.                 </li>
  12.                 <li>
  13.                         <h3><span>造型系列</span></h3>
  14.                         <ul>
  15.                                 <li><a href="###">珠光亮泽</a></li>
  16.                                 <li><a href="###">酷炫凌乱</a></li>
  17.                                 <li><a href="###">……</a></li>
  18.                         </ul>
  19.                 </li>
  20.                 <li>
  21.                         <h3><a href="###">促销信息</a></h3>
  22.                 </li>
  23.                 <li>
  24.                         <h3><span>秀发护理小TIPS</span></h3>
  25.                         <ul>
  26.                                 <li><a href="###">洗发知多少</a></li>
  27.                                 <li><a href="###">护发怎么办</a></li>
  28.                                 <li><a href="###">型发知多少</a></li>
  29.                         </ul>
  30.                 </li>
  31.                 <li>
  32.                         <h3><a href="###">VIDEO</a></h3>
  33.                 </li>
  34.         </ul>
  35. </div>
复制代码


简单解释一下为什么会写成这样。

首先导航部分是一个完整的逻辑区块,于是将它放进一个 <div> 标签之中,并命名(ID)。除了命名之外,我觉得还需要一个更明显的区块标识,于是我用了一个 <h2>,毕竟它不可能大过整个页面的标题。可能有同学会说,这个标题在设计稿中是不存在的,没关系,我们可以用 CSS 把它隐掉。它存在的意义主要还是在于标记结构,此外裸奔的时候你也会看到它。

写完标题,接下来就是导航的正文了,前面已经说过,采用无序列表。每个一级菜单都对应一个 <li>,同时一级菜单项采用 <h3> 进行标记。二级菜单项同样也是采用无序列表来组织,它们都紧随对应的一级菜单项 <h3> 之后,包含在一级菜单的 <li> 之中。不论一级二级菜单,导航中的每个链接自然需要都采用 <a> 标签来标记。(比照上面的代码来阅读本段,应该会更容易理解。)

值得注意的是一级菜单项 <h3> 的内部文字,有些用了 <a> 标签,而有些是 <span>。这里需要强调一点,在写结构的时候,它是什么,就让它是什么。如果某一段文字确实就是链接,那么就用 <a>;如果这段文字只是看起来有点像链接(比如可以响应点击动作)但却并没有链接到某个 URL 时,就不应该用 <a>,我们完全可以使用 CSS 让它具有可点击的视觉表现。所以在这里,部分一级菜单项只是一个标题,虽然最终我们要通过点击它来展开和收缩二级菜单,但它本身并不是一个链接,我就没有让它成为一个链接。当然为了便于应用样式和绑定事件,我在 <h3> 内部嵌套了一层 <span> 标签作为钩子备用。

好了,现在我们来看一下这个结构在无样式的情况下是什么样子的。嗯,看起来很有条理,功能也很完整。

示意图(为节省空间,启用了浏览器的缩放功能):


[[i] 本帖最后由 birdstudio 于 2010-1-4 20:16 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:37:00 | 显示全部楼层
Step 5: 开始装扮页面

接下来就是重头戏了,我们开始为页面添加样式。

添加样式的第一步当然是必要的 CSS Reset。在这个小演示里我就不导入外部文件了,直接写两句简单点的达到目就行。当然,如果是正式项目,还是需要引入一套专业的适合自己的 CSS Reset。

  1. * {margin: 0; padding: 0;}
  2. li {list-style: none;}
复制代码


接着定义一下页面的基本样式,比如页面背景色、文字字体与颜色、链接下划线等等。

  1. body {
  2.         font: 12px/1.2 'Microsoft YaHei', Verdana, Arial, Helvetica, sans-serif;
  3.         color: #ddd;
  4.         background: black;
  5. }
  6. a:link, a:visited {text-decoration: none;}
  7. a:hover, a:active {text-decoration: underline;}
复制代码


然后,开始为 #dv_nav 写一些基本样式,比如基本尺寸、背景图片等等,为后续的布局搭建一个范围。

  1. #dv_nav {
  2.         padding-top: 30px;
  3.         width: 500px;
  4.         height: 540px;
  5.         background: url(img/bg-1.jpg) no-repeat;
  6. }
复制代码


刚刚上面提到,我用了一个 <h2> 来标记结构,但它不需要在页面中出现,于是我们让它消失。

  1. #dv_nav h2 {display: none;}
复制代码


接下来开始定义菜单项的样式。刚刚做了 CSS Reset,所有菜单项之间的间隔都消失了,它们会紧密地挤成一堆。所以,我们首先设置一级菜单区段之间的间隔。(这里用内边距或外边距都可以,不过我自己习惯优先使用内边距,姑且视为个人积累下来的布局思维模式吧。)

  1. #ul_nav li {padding-bottom: 24px;}
复制代码

由于 IE6 不支持子元素选择符,我们在这里采用了后代选择符。不妙的是,这条规则也会对二级菜单项产生影响,所以我们要消除这个副作用。同时,我们还要定义二级菜单项之间的间隔。(我在这里使用的是外边距,当然你也可以试试换成内边距,只要效果达到并且布局方式健壮就行。)

  1. #ul_nav li li {
  2.         padding-bottom: 0px;  /** 清除上一条规则对二级菜单的影响 **/
  3.         margin-top: 10px;
  4. }
复制代码


我们再继续给一、二级菜单项应用一些文字样式,让它们看起来更加层次分明,同时更加接近设计稿的要求。

  1. #ul_nav h3 a, #ul_nav h3 span {
  2.         font-size: 16px;
  3.         font-weight: 700;
  4.         color: #aaa;
  5. }
  6. #ul_nav li li a {color: #999;}
复制代码


好了,基本样式定义完成了。不过为了让大家看清楚各元素的相对关系,我们再增加一些额外的样式。毕竟不是每位同学都是在 Firefox + Firebug 环境下进行设计和调试,对吧?

  1. #ul_nav h3 {background: #555;}
  2. #ul_nav li li {background: #222;}
复制代码


打开浏览器预览一下,页面应该是这样的。

示意图:


[[i] 本帖最后由 birdstudio 于 2010-1-4 20:15 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:38:14 | 显示全部楼层
Step 6: 加入占位元素

做完了基本的导航布局之后,我们要实现导航的异形排版。在 Step 3 中,我们收集了占位元素的尺寸数据,现在我们要把它们添加到页面之中。

根据浮动的原理,在文档流中,浮动元素必须在文本之前,文本才会环绕浮动元素流动。于是,在结构代码中,占位元素必须添加在导航之前。这样代码的结构就变成了这样:

  1. <div id="dv_nav">
  2.         <h2>导航</h2>
  3.         <!--=======================================================-->
  4.         <div id="dv_placeholder">
  5.                 <div class="n1"></div>
  6.                 <div class="n2"></div>
  7.                 <div class="n3"></div>
  8.                 ...
  9.         </div>
  10.         <!--=======================================================-->
  11.         <ul id="ul_nav">
  12.                 ...
  13.         </ul>
  14. </div>
复制代码


照例解释一下结构代码。(为了让大家看得更清楚,我在新增代码的头尾使用了注释进行标注。)

根据 Step 3 格栅化的结果,我们总共需要 27 个占位元素。如果把这些元素直接插入到区块容器(#dv_nav)之下,则感觉有些凌乱。于是我在它们的外围加了一层 <div> 并命名,让结构代码看起来更有条理。即使它们都是纯粹为了布局而存在的无语义元素,也要让它们有条不紊地存在。——当然如果你没有这种代码洁癖的话,就让它们随地躺着也没关系。

占位元素的标签我选择用 <div>,如果你喜欢用 <p> <span> <b> 等等其它标签也是可以的,对最终效果没有影响。由于各占位元素的尺寸不一,我为它们设置了不同的类名。“n1”、“n2”等等意为序号,是我自己习惯的设计模式,或许也可以算作框架的一部分吧。

结构写好之后,来写样式。首先让这些占位元素一律向右排,并且各自独占一行。

  1. #dv_placeholder div {
  2.         float: right;
  3.         clear: right;
  4. }
复制代码


接着为每个占位符设定尺寸。

  1. #dv_placeholder .n1 {width: 185px; height: 10px;}
  2. #dv_placeholder .n2 {width: 195px; height: 10px;}
  3. #dv_placeholder .n3 {width: 200px; height: 10px;}
  4. ...
复制代码


写到这里,我们终于遭遇到第一个浏览器 bug —— IE6 的块级元素默认高度 bug。这里不多解释,直接用 18 个字节解决它。

  1. #dv_placeholder div {
  2.         font: 1px/1 serif;  /** 清除块默认高度 @ IE6 **/
  3. }
复制代码


同样,为了让大家看清楚各元素的相对关系,我们让占位元素显示为半透明的黄色。

  1. #dv_placeholder div {
  2.         background: yellow;
  3.         opacity: 0.2; filter: Alpha(Opacity=20);
  4. }
复制代码


这样,占位元素就安排好了。我们看一下浏览器中的情况。

示意图:


[[i] 本帖最后由 birdstudio 于 2010-1-4 20:15 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:38:44 | 显示全部楼层
Step 7: 最后来点儿 CSS 魔法

经过上一步的精心设置,占位元素貌似已经占据了合适的位置。(或许你会有一点小疑问:包裹了占位元素的 #dv_placeholder 这个元素在哪里?事实上由于它内部的占位元素均已浮动(脱离文档流),这样对于文档流的布局来说,#dv_placeholder 就是一个空元素,空 div 元素是不占据高度的。所以,虽然在结构上它包含了多个占位元素,但在表现层,它是零高度的。当然这里还有一个技巧就是避免让它在 IE6,7 下触发 hasLayout,否则 IE6,7 会错误地让它闭合浮动的占位元素,从而将导航正文挤到占位元素的下方。)

现在我们距离理想的效果还有一步之遥——导航还没有排列为弧形,那么我们给它来一点儿 CSS 魔法。

  1. #dv_nav {
  2.         text-align: right;  /** 各菜单项右对齐 **/
  3. }
复制代码


大家请看好,接下来,就是见证奇迹的时刻!

示意图:


[[i] 本帖最后由 birdstudio 于 2010-1-4 20:14 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:39:14 | 显示全部楼层
所有各级菜单都乖乖地右对齐,并且紧贴着占位元素,呈现出平滑的弧形排列效果。

不过,慢着,你在 IE6 下可能发现了一点小问题——菜单文字跟占位元素之间好像隔了一点什么,没有真正紧密地贴着。似乎我们再次撞上了 IE6 的 bug ——这一次是著名的“3px 浮动 bug”。如果你不是那么追求完美,完全可以无视这一点偏差。但如果你已经下定决心精益求精,那么一行代码也足以解决它。

  1. #dv_placeholder div {
  2.         _margin-left: -3px;  /** 应付3px bug @ IE6 **/
  3. }
复制代码


好了,大功告成!为了验证这种布局方式是否是可扩展的,你可以自行增减菜单项进行测试。

这里是“精简演示”的完整代码:

 提示:您可以先修改部分代码再运行




最后我们去掉那些表明元素相对关系的附加样式,还它真正的面目。

示意图:


[[i] 本帖最后由 birdstudio 于 2010-1-4 20:14 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-20 14:39:55 | 显示全部楼层
结语

这样就结束了?

是的。

这篇文章的核心是讲述布局思路。喜欢抠下代码直接走人的朋友可能会有点失望,这篇文章所实现的效果跟成品相比还是有很大差距——样式不够美观,更不要说手风琴效果。但是,我更愿意向你展示它洗尽铅华之后的内在之美。在我眼中,那华丽的衣衫就好像天边的浮云一样……

相对于布局技巧来说,手风琴 JS 特效可能稍显平淡。打开“完整演示”所调用的 JS 文件,你可能会发现代码极为简洁,仅仅对元素类名的进行操作,完全没有涉及表现层的设置。真正的表现层代码完全存在于 CSS 文件中,这样的低耦合降低了开发复杂度和维护成本。有机会撰文详述。

感谢您看完本文,我期待您的批评和建议!

作者:birdstudio

相关链接:
客户要求
完整演示 (外链)
精简演示 (外链)
原文 (外链)

[[i] 本帖最后由 birdstudio 于 2010-1-6 17:26 编辑 ]
回复 支持 反对

使用道具 举报

发表于 2009-3-20 14:53:09 | 显示全部楼层
比以前的详细多了,引用一句话“经典的右浮动”
回复 支持 反对

使用道具 举报

发表于 2009-3-20 16:16:44 | 显示全部楼层
楼主高啊  仔细看了2边  真是受益匪浅
回复 支持 反对

使用道具 举报

发表于 2009-3-20 20:31:12 | 显示全部楼层
不错,讲得很详细,我觉得我与你的思路和作法有点相同,呵呵!
笔者的文笔优美,行文流畅,向你学习。辛苦了.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-21 00:28:58 | 显示全部楼层
刚刚又添加了一点内容,修改了几处文字错误。

谢谢楼上几位捧场!

[[i] 本帖最后由 birdstudio 于 2009-3-21 14:14 编辑 ]
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-23 14:21:39 | 显示全部楼层
再次修正少数文字。
回复 支持 反对

使用道具 举报

发表于 2009-3-23 14:31:42 | 显示全部楼层
描述浅显易懂,思维开阔.
回复 支持 反对

使用道具 举报

发表于 2009-3-23 20:25:07 | 显示全部楼层
我觉得可以专门写一个对象来控制这样的视觉表现,用js编写一个对象,弧线的数学表达式当作是一个条件~~自动求解每个元素的x,y坐标。这样一处编写到处使用,只要给定数学表达式就OK了,呵呵,我只是在设想,做起来工程量会有点大。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-3-24 15:49:57 | 显示全部楼层

回复 18# rainoxu 的帖子

没错,这确实是一个可行的方法,也是比较正统的程序员思路。本文的方法可算是取巧,呵呵
回复 支持 反对

使用道具 举报

发表于 2009-4-7 13:45:50 | 显示全部楼层
lz 写得真认真~ 学习~

啥时候我也能写出自己的东西啊~
回复 支持 反对

使用道具 举报

发表于 2009-4-7 15:32:09 | 显示全部楼层
如果真接到这么个东西的话,还真不知道能这么干.吼吼.受教了,birdstudio兄...
回复 支持 反对

使用道具 举报

发表于 2009-6-15 16:06:02 | 显示全部楼层
那个....我想问一下那个折叠的菜单是怎么做的呢?我想学那种菜单的制作哦……
回复 支持 反对

使用道具 举报

 楼主| 发表于 2009-6-15 19:13:33 | 显示全部楼层

回复 22# Mistytear 的帖子

你可以参考一下“完整演示”中的 JS 代码。
回复 支持 反对

使用道具 举报

发表于 2009-6-20 17:47:04 | 显示全部楼层
受益匪浅啊
回复 支持 反对

使用道具 举报

发表于 2009-6-22 10:57:39 | 显示全部楼层
写的真不错,呵呵
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|小黑屋|Archiver|手机版|blueidea.com ( 湘ICP备12001430号 )  

GMT+8, 2020-11-27 02:17 , Processed in 0.109098 second(s), 8 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表