bootstrap源码分析系列:二,栅格和响应式布局
bootstrap把response单独分开作为一个独立模块
这样整个栅格系统实际上分为了栅格grid和响应式布局response。非响应式的栅格是940固定宽度的,并且分为固定和流式两种,响应式布局就是通过media query增加了对不同屏幕宽度的适应。
布局上,通过容器的负padding-left和每一个列的margin-left来实现的,如下图所示:
一,定宽栅格的实现
在variables.less文件中定义了不同容器宽度下栅格系统的几个变量:列数(总是12列),列宽度columnWidth, 列间隙宽度gutterWidth和行宽度rowWidth。
代码如下:
- // Default 940px grid
- // -------------------------
- @gridColumns: 12;
- @gridColumnWidth: 60px;
- @gridGutterWidth: 20px;
- @gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
- // 1200px min
- @gridColumnWidth1200: 70px;
- @gridGutterWidth1200: 30px;
- @gridRowWidth1200: (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));
- // 768px-979px
- @gridColumnWidth768: 42px;
- @gridGutterWidth768: 20px;
- @gridRowWidth768: (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));
这段代码定义了三种宽度下的显示方式,默认的940px宽度下,每一列宽度是60,列间隙是20,那么行宽度就是60x12+20x(12-1)=940px
在grid代码中直接调用了mixin中的grid方法:
- // Fixed (940px)
- #grid > .core(@gridColumnWidth, @gridGutterWidth);
- // Fluid (940px)
- #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth);
- // Reset utility classes due to specificity
- [class*="span"].hide,
- .row-fluid [class*="span"].hide {
- display: none;
- }
- [class*="span"].pull-right,
- .row-fluid [class*="span"].pull-right {
- float: right;
- }
注意其中还用了属性选择其来选择spanX,因此是不支持ie8一下的浏览器的。
#grid > .core和#grid - .fluid都是在mixin中定义的方法。
我们先看看core是如何实现的:
- .core (@gridColumnWidth, @gridGutterWidth) {
- //这个函数用递归实现了循环,因为less是没有循环的,因此index=12的时候会输出 .span1 ~ 12,而内部是调用.span函数
- .spanX (@index) when (@index > 0) {
- .span@{index} { .span(@index); }
- .spanX(@index - 1);
- }
- .spanX (0) {} //这个没有任何输出的,不知道写这里干嘛
- //这个函数和.spanX函数很像,都是用递归实现循环操作,内部也是调用了.offset函数实现的
- .offsetX (@index) when (@index > 0) {
- .offset@{index} { .offset(@index); }
- .offsetX(@index - 1);
- }
- .offsetX (0) {}
- //定义了12列offset的margin
- .offset (@columns) {
- margin-left: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns + 1));
- }
- //定义了12列的宽度,注意宽度是如何计算出来的
- .span (@columns) {
- width: (@gridColumnWidth * @columns) + (@gridGutterWidth * (@columns - 1));
- }
- //注意负margin,注意这里自带了clearfix,因此宽度会被里面的列撑开,而不需要指定宽度
- .row {
- margin-left: @gridGutterWidth * -1;
- .clearfix();
- }
- //通过属性选择器统一定义了每一列的通用样式,好处是只需要定义一次,坏处是不支持ie8以下的浏览器(不支持属性选择器)
- [class*="span"] {
- float: left;
- min-height: 1px; // prevent collapsing columns
- margin-left: @gridGutterWidth;
- }
- // Set the container width, and override it for fixed navbars in media queries
- //重载了container的宽度
- .container,
- .navbar-static-top .container,
- .navbar-fixed-top .container,
- .navbar-fixed-bottom .container { .span(@gridColumns); }
- // generate .spanX and .offsetX
- //调用函数递归生成.span1~12和.offset1~12
- .spanX (@gridColumns);
- .offsetX (@gridColumns);
- }
其中最大的亮点就是用递归模拟了循环,通过循环避免了重复写12次代码。
二,流式栅格的实现
流式栅格相比对定宽栅格,最大的区别就是:
1,定宽栅格直接使用了传入的参数,而流式栅格用传入的参数计算出百分比,最终使用百分比来做布局。
2,定宽栅格可以使用负margin row来布局,而流式栅格则不行,因为row的负margin应该和span的margin-left保持一致,而这两个使用百分比的话是无法保持一致宽度的,因为百分比计算的对象不一样。
比如在同样是如下参数:
@gridColumns: 12;
@gridColumnWidth: 60px;
@gridGutterWidth: 20px;
@gridRowWidth: (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));
定宽栅格中,每一列就是60px宽度,列间距就是20px
而在流式栅格中根据上述参数会计算出一个百分比来,实际使用的是这些百分比:
// Fluid grid
// -------------------------
@fluidGridColumnWidth: percentage(@gridColumnWidth/@gridRowWidth);
@fluidGridGutterWidth: percentage(@gridGutterWidth/@gridRowWidth);
流式栅格的代码如下:
- .fluid (@fluidGridColumnWidth, @fluidGridGutterWidth) {
- //和定宽栅格没什么区别
- .spanX (@index) when (@index > 0) {
- .span@{index} { .span(@index); }
- .spanX(@index - 1);
- }
- .spanX (0) {}
- .offsetX (@index) when (@index > 0) {
- .offset@{index} { .offset(@index); }
- .offset@{index}:first-child { .offsetFirstChild(@index); }
- .offsetX(@index - 1);
- }
- .offsetX (0) {}
- .offset (@columns) {
- margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth*2);
- *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + (@fluidGridGutterWidth*2) - (.5 / @gridRowWidth * 100 * 1%);
- }
- .offsetFirstChild (@columns) {
- margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) + (@fluidGridGutterWidth);
- *margin-left: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%) + @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%);
- }
- .span (@columns) {
- width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1));
- *width: (@fluidGridColumnWidth * @columns) + (@fluidGridGutterWidth * (@columns - 1)) - (.5 / @gridRowWidth * 100 * 1%);
- }
- .row-fluid {
- width: 100%;
- .clearfix();
- [class*="span"] {
- .input-block-level();
- float: left;
- margin-left: @fluidGridGutterWidth;
- *margin-left: @fluidGridGutterWidth - (.5 / @gridRowWidth * 100 * 1%);
- }
- [class*="span"]:first-child {
- margin-left: 0;
- }
- // Space grid-sized controls properly if multiple per line
- .controls-row [class*="span"] + [class*="span"] {
- margin-left: @fluidGridGutterWidth;
- }
- // generate .spanX and .offsetX
- .spanX (@gridColumns);
- .offsetX (@gridColumns);
- }
- }
三,响应式布局
有了上面的#grid > .core和 #grid > .fluid函数,实现响应式就非常简单了。
首先variables中已经定义好了不同宽度的样式,前面已经讲过了。
然后在加上media query并调用 #grid就ok了:
比如 1200px宽屏就是这么定义的:
- @media (min-width: 1200px) {
- // Fixed grid
- #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200);
- // Fluid grid
- #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200);
- // Input grid
- #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200);
- // Thumbnails
- .thumbnails {
- margin-left: -@gridGutterWidth1200;
- }
- .thumbnails > li {
- margin-left: @gridGutterWidth1200;
- }
- .row-fluid .thumbnails {
- margin-left: 0;
- }
- }