#1 自定义栅格系统&响应式布局

原文连接

一.如何构建自己的栅格系统

  1. grid design
  2. how your grid behaves at different viewports
  3. whether to use HTML OR CSS grid systems

1.building your grid system

8步建立自己的栅格系统:

  1. 选择一个栅格系统规范: css grid, Flextbox, 还是floats
  2. 设置 box-sizingborder-box
  3. 创建 grid container
  4. 计算 column-width(列宽)
  5. 决定 gutter(间隔) 位置
  6. 创建debug grid
  7. 制作布局差异(make layout variations)
  8. 使布局变得响应式

二.具体步骤

对应上面的8条

1.选择规范

CSS Grid最好用,但是浏览器的支持太差,选择使用Flexbox或者Floats。 flexbox和floats之间其实差异很小,下面为了通常情况,选择floats来布局。

2.设置box-sizing

box-sizing有3种: content-box, padding-box, border-box, 设置为border-box是最常用的, 设置方法如下

html {
    box-sizing: border-box
}

*,
*:before,
*:after {
    box-sizing: border-box
}

3. 设置栅格容器

每个栅格都有一个容器来决定栅格的最大宽度,一般可以命名为 .l-wrap, 使用 .l 表示 layout, 这种命名规范称之为 SMACSS.

设置如下:

.l-wrap {
    max-width: 1140px;
    margin-right: auto;
    margin-left: auto;
}

强烈推荐使用 em | rem 这样的相对单位,对于响应式布局可以增强可访问性和响应能力, 下面例子为了简洁,还是会使用pixels作为单位。

4.计算列宽

我们上面说过使用 floats 来布局,创建列(columns)列间距(gutter)我们由使用下面5个属性(使用flexbox则属性更多一点):

  • width
  • margin-right
  • margin-left
  • padding-right
  • padding-left

通过上面的介绍,可以大致写出如下html(使用Jade):

.l-wrap
  .three-col-grid
    .grid-item Grid item 1
    .grid-item Grid item 2
    .grid-item Grid item 3

从这个html,我们可以知道一行总共有3列,并且没有额外的div用来充当gutters,这意味着:

  • 我们列通过 width 属性
  • 我们通过 margin 或者 padding 属性来创建列间距

如果我们同时考虑columns 和 gutters 会使情况变得复杂,我们先假设没有列间距的情况。

我们知道整个栅格最大宽度为1140px,这也意味着3个小栅格每个宽度为 1140px/3=380px

.three-col-grid .grid-item {
    width: 380px;
    float: left;
}

当我们在浏览器中改变浏览器大小时,发现视窗小于1140px时,栅格会重新占据下一行,这不是我们想要的,所以我们不能使用 px 当作单位,所以我们改用百分比来当作单位:

.three-col-grid .grid-item {
    width: 33.33333%;
    float: left;
}

有一点要注意的是: 当一个容器中的所有子元素都是浮动的时候,容器的高度会坍塌(height collapses), 这个现象称之为 float collapse。就好像这个容器没有包含元素一样。所以我们要清除浮动

.three-col-grid:after {
    display: table;
    clear: both;
    content: '';
}

如果使用sass的话:

@mixin clearfix {
    &:after {
        display: table
        clear: both;
        content: '';
    }
}
// 使用
.three-col-grid {@include clearfix}

// sass表示
@mixin clearfix
  &:after
    display: table
    clear: both
    content: ''

.three-col-grid
  +clearfix

5.决定列间距的位置

上面我们可以知道,可以使用 margin 或者 padding 来创建列间距,有4种情况:

  • 使用margins,间距放在同一边
  • 使用padding, 间距放在同一边
  • 使用margins, 间距等分的放在2边
  • 使用paddings, 间距等分的放在2边

从这开始事情将变得复杂,你需要计算列宽依据你选择上面的哪一种方式。

方法1:one-sided gutters(using margin)

这个方法使用margin属性,这个间距放在右边或者左边,由你决定。我们假设放在右边:

.grid-item {
    /* 需要重新计算width 属性 */ 
    margin-right: 20px;
    float: left;
}

这里有个问题,就是列宽我们使用的是百分比, 而margin使用的是pixels,单位不一致,我们不能同时进行计算。

这在以前是不能计算的,现在我们可以使用CSS 提供的 calc 函数(注意不支持IE8-和opera mini) 来混合计算不同的单位,浏览器会自动帮助我们计算最后渲染出来的宽度值。

.grid-item {
    width: calu((100% - 20px * 2) / 3);
    /* 其他属性 */
}

最后我们需要移除最右边的grid item 的 margin-right

.grid-item:last-child {
    margin-right: 0;
}

大多时候,移除最右边盒子的外边距的同时,一般需要将 float 设置为 right,这样可以阻止浏览器给盒子添加的子像素,导致盒子移动到下一行的情况,即:

.grid-item:last-child {
    margin-right: 0;
    float: right;
}

这样对一行的情况已经OK了,对于多行的情况,我们需要移除每一行最右边的盒子的外边距,可以使用 nth-child():

.grid-item:nth-child(3n+3) {
    margin-right: 0;
    float: right;
}

方法2:one-sided gutters(using paddings)

和方法1一样,这个方法将列间距放在列的一边。假设放在右边:


.grid-item {
    /* 宽度属性 */
    padding-right: 20px;
    float: left;
}

发现宽度和方法1中的不同了吗?因为我们将box-sizing设置为了 'border-box', 现在宽度计算包括padding值。

这种情况,3列中的2列快读要大于最后一列的宽度,这样会导致怪异的CSS计算情况出现,建议永远不要尝试这种方法,可能出现很丑的界面

方法3:split gutters(using margin)

这个方法将列间距放在列的两边,这样代码看起来像这样:

.grid-item {
    /* 宽度属性 */
    margin-right: 10px;
    margin-left: 10px;
    float: left;
}

利用 calc() 计算列宽:

.grid-item {
    /* 注意这里列宽要比第一种情况小 */
    width: calc((100% - 20px * 3) / 3);
    margin-right: 10px;
    margin-left: 10px;
    float: left;
}

这样就可以了,没有其余的步骤需要去做了,多行也是一样,也不用特意设置很行最后一个盒子的样式了

方法4: split gutters(using padding)

这个方法和方法3差不多,但是宽度计算更简单了,因为padding值直接包含在盒子里面:

.grid-item {
    width: 33.3333%;
    padding-right: 10px;
    padding-left: 10px;
    float: left;
}

注意使用这个方法,需要在.grid-item 里面在添加一层div,使得2盒子之间的界限更加的清晰,即:

.l-wrap
  .three-col-grid
    // 需要多添加一层div.grid-inner
    // 使得各个grid-item之间的间隙更加的明显
    .grid-item: .grid-inner Grid item
    .grid-item: .grid-inner Grid item
    .grid-item: .grid-inner Grid item
    .grid-item: .grid-inner Grid item
    .grid-item: .grid-inner Grid item
    .grid-item: .grid-inner Grid item

查看codepen代码

选择哪一种方法?

如果要选的话,我会选择列间距放两边而不是把列间距放一边的方法, 因为这样CSS更简单。另外更偏向于使用margin作为列间距,因为代码会更加的干净, 即方法3最为理想。但是方法4使用padding计算更加简单,下面会使用padding来作介绍。

6.创建调试栅格

当开始创建栅格时,创建一个控制栅格用于调试对于布局帮助很大,这样可以帮助你正确的创建栅格。

目前只有一个蹩脚的调试栅格,就是创建HTML元素,添加一些CSS。codepen

HTML:

.l-wrap
  .fixed-gutter-grid
    .column
    .column
    .column
    .column
    .column
    .column
    .column
    .column
    .column
    .column
    .column
    .column

CSS(使用margins分开式列间距,即上面的方法3):

.l-wrap {
  max-width: 1140px;
  margin-right: auto;
  margin-left: atuo;
}

.column {
  width: calc((100% - 20px * 12) / 12);
  height: 80px;
  margin-left: 10px;
  margin-right: 10px;
  background: rgba(0, 0, 255, 0.25);
  float: left;
}

7.创建布局变形

下一步就是根据内容创建布局变形,这也正是栅格系统最耀眼的地方。可以创建给布局一个合理的名字,而不必要写多个栅格class。

例如,加入你有个栅格系统只用于guest articles,这个布局分布为 2-7-3,这个guest-article 布局为:

.l-guest-article
  .l-guest.grid-item: .grid-inner item 1
  .l-main.grid-item: .grid-inner item 2
  .l-sidebar.grid-item: .grid-inner item 3

每列宽度为 100% / 12 = 8.333%, 因此.l-guest为 8.333% * 2, 其余栅格依次求出宽度, 可以使用预处理语言(不如sass) 的percentage函数来代替手动计算

即:

.l-grid-article
  +clearfix

  .l-guest
    width: percentage(2/12)
    
    .grid-inner
      background-color: pink

  .l-main
    width: percentage(7/12)
    
    .grid-inner
      background-color: yellow

  .l-sidebar
    width: percentage(3/12)
    
    .grid-inner
      background-color: blue

// 将共同部分提取出来
  .grid-item
    padding-right: 10px
    padding-left: 10px
    float: left

.grid-inner
  display: flex
  justify-content: center
  align-items: center
  height: 80px
  text-align: center

栅格系统变种codepen

8.使布局变得响应式

最后一步就是让布局变得响应式, 可以根据下图方法:

可以看出l-guest-articlehtml标记不改变,完全由CSS来控制布局, 当使用CSS来做响应式布局时,强烈建议移动优先原则,这样写的代码更加的简洁

1.移动布局:

.l-guest-article
  .l-guest /* 这里什么都不用写*/
  .l-main
    margin-top: 20px
  .l-side-bar
    margin-top: 20px

我们什么也不用做,因为组件默认的会占满父元素宽度,我们可以给上下盒子之间添加一些外边距

2.平板布局:

对于这种布局,假定我们的临界点(breakpoint)为700px.

  • .l-guest占据 4/12
  • .l-main.l-sidebar 占据8/12

我们应当将 .l-main 中的 margin-top 移除, 因为它和 .l-guest在同一行。

另外如果我们设置 .l-sidebar为8列,它会自动在第2行上,因为第1行没有多余的空间了。我们可以使用 margin-right 或者 float: right让它在右边。

如下(别忘记容器清除浮动):

.l-guest-article
  +clearfix

  .l-guest
    @media(min-width: 700px)
      width: percentage(4/12)
      float: left

  .l-main
    margin-top: 20px
    @media(min-width: 700px)
      width: percentage(8/12)
      margin-top: 0
      float: left

  .l-sidebar
    margin-top: 20px
    @media(min-width: 700px)
      width: percentage(8/12)
      float: right

3.桌面布局

假设临界点为1200px,这个和前面介绍的布局方式一致,为 2-7-3

值得注意的是,别忘记移除 .l-sidebarmargin-top

.l-guest-article
  +clearfix

  .l-guest
    @media (min-width: 700px)
      width: percentage(4/12)
      float: left

    @media (min--width: 1200px)
      width: percentage(2/12)

  .l-main
    margin-top: 20px
    
    @media (min-width: 700px)
      width: percentage(8/12)
      margin-top: 0
      float: left

    @media (min-width: 1200px)
      width: percentage(7/12)

 .l-sidebar
    margin-top: 20px
    
    @media (min-width: 700px)
      width: percentage(8/12)
      float: right

    @media (min-width: 1200px)
      width: percentage(3/12)
      margin-top: 0

具体效果 响应式设计

总结

通过这篇文章,我们学习了如何自定义栅格系统,以float作为布局规则,主要有以下几个知识点:

  1. 设置box-sizing,容器宽度使用 max-width
  2. 使用 calc() 函数计算列宽(注意IE8-不能使用)
  3. 常见的4种列间距的使用方法,最常用的就是使用margin平分列间距
  4. 使用sass的辅助函数 percentage 来进行宽度计算
  5. 使用 media query 实现响应式布局
  6. 注意使用浮动时,注意清除浮动
  7. 当然还有捎带提及了SMACSS命名方法
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,709评论 1 92
  • 一:在制作一个Web应用或Web站点的过程中,你是如何考虑他的UI、安全性、高性能、SEO、可维护性以及技术因素的...
    Arno_z阅读 1,132评论 0 1
  • 前言 温馨提示:本文较长,图片较多,本来是想写一篇 CSS 布局方式的,但是奈何 CSS 布局方式种类太多并且实现...
    sunshine小小倩阅读 3,107评论 0 59
  • 选择qi:是表达式 标签选择器 类选择器 属性选择器 继承属性: color,font,text-align,li...
    wzhiq896阅读 1,723评论 0 2
  • 一袋辣鸡爪,一袋麻辣花生米,一条辣鱼,一人饮酒。 我不知道自己为什么非要在今日饮酒,或许真的是为了酒后已熏,回忆不...
    漫漫无忧阅读 313评论 15 10