Flexbox 已经成为了最流行的建站布局工具之一。Susy 是另一个布局工具,它和 Sass 社区一起在近几年中得到了普及。
许多和我聊过的开发者不确定哪一个工具对于为网站创建布局更好一些。他们中的一些感觉 flexbox 足够强大去处理所有的布局问题。然而,他们不确定是否要去学习 flexbox,因为它混乱的语法。其他人则认为 Susy 更简单,与 flexbox 相比他们更喜欢 Susy 的简单。
一些人甚至会问把 Susy 和 flexbox 结合是不是可能的,这样他们就可以更加容易地布局。所以,这里有两个本文要去探索的问题:
1. 哪一个更强大,flexbox 还是 Susy?
2. 同时使用 flexbox 和 Susy 是否可能?
让我们开始吧,看看我们是如何比较 flexbox 和 Susy 的。
严格地说,直接比较 flexbox 和 Susy 是不可能的,因为它们是两个完全不同的工具。拿 flexbox 与一个基于浮动的布局工具来比较可能更合适。
然而,因为 Susy 和 flexbox 都习惯构造布局,我们可以试着去构造不同的布局去作为比较的代表。在这篇文章中,我们会比较以下五个布局:
1. 内容和侧边栏布局
2. 内容和侧边栏,在比例网格中
3. 内容和侧边栏,在固定间隙网格中
4. 画廊布局,在比例网格中
5. 画廊布局,在固定间隙网格中
不用担心你不确定什么是比例和固定间隙网格。当我们说到第二个布局的时候我们会讲解它。
现在,让我们开始比较第一个布局吧。
我们想要在这第一个布局中保持简单,那样就可以让你开始习惯于使用 flexbox 和 Susy 来创建网格。
我们在这里会创建一个类似的 Smashing Magazine 的博客类型的网站,在这样的网站页面上,内容占据总宽度的 75%,侧边栏占据总宽度的 25%:
博客类型的内容和侧边栏布局。 ( 查看大图 )
下面是我们在使用 flexbox 和 Susy 时都会为这个布局编写的 html 代码。一个容器元素加上为每个工具准备的两个 div。
<!-- Flexbox markup --> <div class="flexbox"> <div class="content"> <h4> Content </h4> </div> <div class="sidebar"> <h4> Sidebar </h4> </div> </div> <!-- Susy markup --> <div class="susy"> <div class="content"> <h4> Content </h4> </div> <div class="sidebar"> <h4> Sidebar </h4> </div> </div>
如你所见,通过使用相同的标记,我们将内容保持一致。
现在让我们开始加样式。
使用 flexbox, 首先我们需要给父容器(.flexbox) 设定 display 属性为 flex。
注意:为了简化代码,我们先不管特定于浏览器的带有前缀的属性。
.flexbox { display: flex; }
然后,我们将 flex-basis 添加到 .content 和 .sidebar, 它类似于 width 属性。
.flexbox { display: flex; .content { flex-basis: 75%; } .sidebar { flex-basis: 25%; } }
Susy 链接
Susy 附带会在网格上有一个默认的中缝(gutter)设置。首先我们需要通过在 $susy 映射中将 gutters 设置为 0 来禁用它:
// Importing Susy @import "susy"; // Removing default gutters $susy: ( gutters: 0 );
Susy 目前最常规的使用方式就是 span() 混入。这里,我们可以在 .content 和 .sidebar 的 span() 混入中使用百分比来达到类似的效果:
.susy { .content { @include span(75%); } .sidebar { @include span(25%); } }
因为 Susy 默认是基于浮动的布局,我们需要强制让父元素(.susy)将子元素进行 clear 处理,来防止 父元素压缩 :
@mixin clearfix { &:after { content: ' '; display: table; clear: both; } } .susy { // Adds clearfix @include clearfix; .content { @include span(75%); } .sidebar { @include span(25%); } }
如你所见,在本阶段 Susy 和 flexbox 的代码都相当的类似。我们可以立马就指出的唯一的不同之处就在于 flexbox 单纯使用 CSS 就能起作用,而 Susy 需要用到 Sass 预处理器。
让我们继续对下一个布局进行比较。它会更加的有趣一些。
对于设计师和开发者而言,网格都是网页上基础的构建块,所以我们会用网格来对布局进行比较。
当我们考虑为网页构建网格时,立即会想到要让网格响应不同的屏幕宽度。
当网格拉伸时,列和间隔应该成比例的扩张(这就是成比例的网格)吗?
成比例的网格。 ( 查看大图 >
或者,列在扩张的同时,间隔仍然保持固定的尺寸(间隔固定的网格)?
固定间隔的网格。 ( 查看大图 )
考虑这个问题很重要,因为这将会影响到布局如何响应像网页这样的流动媒介。它也会影响到布局的代码是如何编写的。
首先让我们来研究成比例网格。
在继续之前,让我们使用Susy来帮助我们给网格创建一个背景,那样我们就能很容易的看到自己是否做对了。对于这里的这个示例,我们会使用12列网格,每一列的宽度将会是每一个间隔宽度的4倍。
$susy: ( columns: 12, gutters: 0.25, debug: (image: show-columns) );
然后我们会使用Susy提供的show-grid()在 .flexbox 和 .susy 上都将网格展示出来。这个 show-grid() 混入只会在选择器上创建一个背景图片,并不会对代码产生任何影响。
.flexbox, .susy { @include show-grid(8);}
好了,让我们继续。对于此布局,我们尝试让.content占据网格中的9列,而.sidebar则占据剩下的3列。
因为我们是现在用的是成比例的网格,所以列和间隔都需要使用百分比宽度。首先我们用flexbox来创建此布局。
Flexbox 链接
因为我们使用的是百分比,所以需要做一些数学计算。
对于此布局,假设每个间隔是20像素宽。列是间隔宽度的4倍 — 因而就是 80 个像素宽。
那么12列布局的总宽度就会是 (12 * 80px) + (11 * 20px) = 1180px.
12列的宽度。 ( 查看大图 )
因为 .content 占了9行,它的宽度就会是 (9 * 80px) + (8 * 20px) = 880px.
9列的宽度。 ( 查看大图 )
最后,我们可以通过用内容的宽度除以网格的总宽度来计算出百分比宽度。 也就是 880px ÷ 1180px * 100% = 74.58%.
9列的百分比宽度。( 查看大图 )
然后,我们可以同样的做一些令人感到不爽的数学计算来得到 .sidebar 的宽度:
.flexbox { display: flex; .content { flex-basis: 74.5762712%; } .sidebar { flex-basis: 23.7288136%; }}
谢天谢地,因为我们可以在flex容器中将justify-content设置成space-between,所以不必再为间隔做数学计算了。这是因为剩余的空间,其宽度就是一个间隔,它将会给 .content 和 .sidebar 之间来分。
.flexbox { display: flex; justify-content: space-between; .content { flex-basis: 74.5762712%; } .sidebar { flex-basis: 23.7288136%; }}
现在让我们来看看如何用Susy来创建相同的布局。
Susy 链接
用 Susy 的话, 我们会直接将列的数量插入到 span() 混入中,而它会为我们来计算出百分比宽度。这里,.content 是 span(9) ,因为内容占了9列嘛。
Susy 也会自动计算出一个间隔的宽度,并将其作为margin-right添加到span()混入中。因而,在向侧边栏添加span()的时候,我们的额外加上加上一个last关键词,来告诉Susy恰当的去掉最后一个margin-right属性。
简单点看,创建相同布局的Susy代码如下:
.susy { @include clearfix; .content { @include span(9); } .sidebar { @include span(3 last); }}
就是在这里,你可能会想到这样的代码优雅了许多,没有令人感到不爽的数学计算。那这是否就意味着Susy比flexbox更好呢?
好吧,并不。我们之前提到过,因为他们是不同的工具,所以不能直接拿来做比较。用基于浮动的布局来同flexbox进行比较应该更恰当,这在过去我们也得进行烦人的数学计算。
这也是人们对Susy最感到困惑的地方。它不是一个框架。它是一个强大的网格计算器,构建出来是用来处理grid的数学计算的。
因为它只是一个计算器,所以我们也可以在使用flexbox时用上Susy来去掉烦人的数学计算。这块是Susy中一个少有人知晓的特性。
这一特性就是 span() 函数。. 它是span()混入在flexbox中的用法,用来创建宽度属性。
因此,我们也可以说 .content 的flex-basis是 span(9),而 .sidebar 的flex-basis是 span(3) ,得到的效果是一样的。
.flexbox { display: flex; justify-content: space-between; // Susy span function with flexbox .content { flex-basis: span(9); } .sidebar { flex-basis: span(3); }}
如你现在可以看到的, flexbox 和 Susy 并不能直接拿来作比较,因为他们是不同的工具。不过Susy可被用来在一些领域 对flexbox进行补充:网格的数学计算。
话虽如此,我们还是继续就flexbox和Susy如何构建布局进行“比较”,这样我们就可以看到许多人错过了的一些具体细节。
让我们来看看第三种布局,面对的是一个固定间隔的网格。
在继续之前,我们先创建一个网格遮罩,那样我们就能知道自己是否把网格放对了地方。这里我们只能通过滚动我们自己的网格来进行对比,因为Susy提供的网格并不能固定间隔。
这种网格的标记语言代码如下:
<div class="fixed-gutter-grid"> <!-- 12 columns in total --> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> <div class="column"></div> </div>
接下来,让我们想办法为这个网格的遮罩创建CSS。
首先,间隔必须用固定的单位,因为只有列会扩展其尺寸。根据之前的示例,我们使用20像素作为此网格间隔的宽度。
总共有11个间隔,意味着剩余的列必须占总宽度的 100% - (11 * 20px)。每列的宽度因而就是 (100% - (11 * 20px)) ÷ 12。
正常情况下我们不能混合使用百分比和像素,因此需要使用 calc() 函数来帮助我们做这个。
下面是这个网格的完整的CSS:
.fixed-gutter-grid { @include clearfix; .column { width: calc((100% - (11 * 20px)) / 12); margin-right: 20px; float: left; height: 80px; background: rgba(blue, 0.25); &:last-child { margin-right: 0; } }}
因为我们把网格设置好了,所以就需继续尝试 .content 和 .sidebar 的样式。我们要得到下面这样的效果:
间隔固定网格中的内容&侧边栏布局。 ( 查看大图 )
Flexbox 链接
咋一看,用flexbox创建这样的网格应该简单。你可能会想我们可以在.content和.sidebar之间加上20像素的外边距,而flexbox会魔法般的平分 .content 和 .sidebar 之间的空间。
.flexbox { display: flex; justify-content: space-between; .content { flex-basis: 75%; margin-right: 20px; } .sidebar { flex-basis: 25%; }}
不幸的是,并不会那么容易。像上面那样做你得到的效果会是下面这个样子:
<br/使用固定间隔网格的Flexbox。 ( 查看大图 )
你把外边距加到.content或者.sidebar,或者在两者之间平分外边距,都没关系。结果都是一样的。
这是因为当我们考虑到间隔时, .content 和 .sidebar 就不在是精确的3:1比例关系了。要计算它们之间精确的比率关系是不可能的,因为一直在变。
不过还是有一个方法可以规避这个 flex-basis 问题。三个步骤:
给flexbox容器加上等于间隔宽度一半的负数外边距(左和右)。
给所有flex单项都加上等于间隔宽度一半的内边距(左和右)。
给所有的flex单项都设置 border-box 属性。
.flexbox { /* Other properties */ margin-left: -10px; margin-right: -10px; > div { box-sizing: border-box; padding: 0 10px; } .content { flex-basis: 75%; } .sidebar { flex-basis: 25%; }
因为我们使用了是内边距,而 box-sizing 属性被设置成了 border-box, 现在的CSS还不能让我们看到任何视觉上的变化。
我们需要给每个flex单项的内部内容另外的一个背景颜色,来看清它是如何对齐到网格的:
h4 { background: turquoise;}
这儿有一个问题。如果我们想要给flex单项添加背景颜色会怎么样呢?
解决这个问题的一个办法就是添加额外的标记,但那不是处理这个问题的最佳方法。
一个替代办法就是给每个flex单项用上外边距,而不是内边距。当我们使用外边距时,必须用calc()增加对flex-basis的计算,如此它就是原来的flex-basis减去整个间隔宽度的结果了。
因此,内容的flex-basis应该是 calc(75% - 20px)。其余的属性如下:
.flexbox { /* Other flexbox properties */ margin-left: -10px; margin-right: -10px; > div { /* Switching to margins */ margin: 0 10px; } /* flex-basis = percentage - gutters on each side */ .content { flex-basis: calc(75% - 20px); } .sidebar { flex-basis: calc(25% - 20px); }}
这里有一个额外的建议:如果你不喜欢想75%或者25%这样的数学计算,可以在Sass中使用percentage函数。
.content { flex-basis: calc(#{percentage(3/4)} - 20px); }.sidebar { flex-basis: calc(#{percentage(1/4)} - 20px); }
还有一件事儿。当你试着将上面的Codepen滚动到右边,你会看到10像素的间隔。这个间隔是我们在向.flexbox加上 margin-right: -10px 时创建的。
这是在响应式网站上面使用flexbox的一个细微差别。如果你在flex容器上不使用负数外边距,就要确保移除这个通过在父元素上添加 overflow-x:hidden 而创建出来的额外空间。
在这里,我们的父元素是body。
body { overflow-x: hidden;}
让我们来看看Susy是如何处理这样的情况的。
Susy 链接
Susy 对于这样的网格并没有个答案,因为它是用来计算百分比宽度的。我们能做的就是将其同基于浮动的布局进行比较,在其中我们会使用calc()进行计算。
目前,我们能了解到下面这两件事儿:
间隔的宽度是20像素。
列的宽度是 calc(100% -(11 * 20px) ÷ 12).
从上述的计算 (布局2中), 我们了解到9列的总宽度等于 (9 x 列宽) 加上 (8 x 间隔宽), 它等于 calc((100% - (11 * 20px)) * 9 ÷ 12 + 8 * 20px)
9列。( 查看大图 )
我们可以遵循同样的计算方法得到 .sidebar 的宽度:
.self-made-grid { @include clearfix; .content { float: left; width: calc((100% - (11 * 20px)) * 9 / 12 + 8 * 20px); margin-right: 20px; } .sidebar { float: left; width: calc((100% - (11 * 20px)) * 3 / 12 + 2 * 20px); }}
如你所见,如果我们的一遍一遍做这样的计算,事情就会很快变得复杂。我们可以做的一件事儿就是简化这个流程,创建出一个网格计算器混入:
$columns: 12;$gutter: 20px;@mixin custom-span($span) { float: left; width: calc((100% - (#{($columns - 1)} * 20px)) * #{$span} / 12 + #{($span - 1)} * 20px); margin-right: $gutter;}.self-made-grid { @include clearfix; .content { @include custom-span(9); } .sidebar { @include custom-span(3); margin-right: 0; }}
要得到我们的数学计算器并不太难,是不?
好吧,让我们言归正传、
如你所见,我们需要用flexbox耍一些怪异的小手段,以让固定间隔的网格能正常工作。一旦你拿下了这些基础,要创建任何这种类型网格的布局都应该是简单的。
注意,这个方法非常重要 (你会在布局4和5中知道为什么)。
除了一般了内容和侧边栏布局,我们通常发现自己会创建的其它布局就是画廊了。
我们来尝试做一个三乘三的画廊。
三乘三画廊。 ( 查看大图
)
因为我们要创建的是一个画廊,所以标记要做一下轻微的修改。
<div class="flexbox"> <!-- 9 gallery__items --> <div class="gallery__item">item</div> <div class="gallery__item">item</div> <div class="gallery__item">item</div> <div class="gallery__item">item</div> <div class="gallery__item">item</div> <div class="gallery__item">item</div> <div class="gallery__item">item</div> <div class="gallery__item">item</div> <div class="gallery__item">item</div></div>
类似的,我们也得考虑到两种类型的网格。首先让我们研究下怎么在一个成比例的网格上创建一个画廊。
Flexbox 链接
你首先要注意的一件事儿就是现在有了超过一行的项。如果是这种情况发生的话,我们就需要给 .flexbox 的一个 flex-wrap 属性设置 wrap,来让flex单项会流动到下一行。
.flexbox { display: flex; flex-wrap: wrap; justify-content: space-between;}
接下来,我们需要计算出 flex-basis 的值,那样每个画廊单项就会占据四列。我们知道从上面数学计算可以得出其值应该是 ((4 * 80px) + (3 * 20px)) ÷ 1180px * 100% = 32.20339%.
.gallery__item { flex-basis: 32.20339%;}
我们也从上面了解到可以将这里的数学计算结果切换成一个Susy的span()函数:
.gallery__item { flex-basis: span(4);}
现在的效果就如下所示:
不完整的flexbox画廊布局。( 查看大图
)
当我们创建一个画廊时,我们常常会添加一些垂直方向的外边距,如此画廊单项在垂直和水平方向上的外边距就是相等的。
你心里首先会出现的想法 可能是我们可以给每个画廊单项添加一个基于百分比的margin-top属性。这个margin-top属性应该等于一个间隔的宽度 。
这个宽度应该是 20 ÷ 1180 * 100% = 1.694915254%。我们也可以使用Susy的gutter()函数:
.gallery__item { flex-basis: span(4); margin-top: gutter(); }
如果你去掉背景网格,就得有一个间距相等的画廊:
… 也就是说,直到你使用Firefox查看这个布局时才会发现的问题:
Firefox中百分比形式的外边距会失效。 ( 查看大图
)
margin-top 的办法失效是由于flex单项的外边距和内边距的百分比是明确预计好了flex容器的尺寸的,依据就是 flexbox 的规范文档 。
这就意味着margin-top的百分比的值只会在我们制定了flex容器的高度值时才会被计算出来。因为我们没有这样做,margin-top 就会等于0。
这个方法会在Firefox中失效,是因为只有Firefox实现了我们刚刚提到的规范。WebKit 和 Internet Explorer 都还没有实现它。
这就给我们带来了一个不幸的冲突:如果是使用flexbox来创建一个基于百分比的画廊,那 我们想要确保垂直和水平方向上的间隔相等就是不可能的。 不过,我们还是可以做到,如果我们并不是要追求间距的完全相等,那么仍然可以对垂直间隔使用绝对单位(像px和em)。
接下来让我们看看如何使用Susy来实现画廊布局。
Susy 链接
Susy 为我们提供了一个 gallery() 混入,可以被用来创建画廊。我们需要做的就是在gallery()混入中加上每个画廊单项会占据的列的数量。
.susy { @include clearfix; .gallery__item { @include gallery(4); }}
因为Susy使用的是基于浮动的布局,我们仍然可以使用基于百分比的外边距来创建一个间距相等的画廊,没有任何问题:
.susy { @include clearfix; .gallery__item { @include gallery(4); margin-top: gutter(); }}
如你所见,做出一个基于百分比宽度的画廊的唯一方法就是通过一个基于浮动的布局。
不过,如果坚持使用基于浮动的布局,就不能处理高度不相等的画廊单项:
基于浮动布局不能处理高度不相等的画廊。 ( 查看大图
)
我们需要切换到固定宽度网格来创建这样一个画廊,因为我们需要使用 flexbox提供的align-stretch 属性。
因此,让我们来看看布局5,我们会找到如何使用flexbox来创建这样的画廊的方法。
对于最后的这个布局,让我们先掐住话头,直接跳到使用flexbox来创建画廊。
Flexbox 链接
我们可以使用跟布局3中一样的原则 (固定间隔上的内容和侧边栏) 来创建画廊。
我们需要再一次做下面这些事情:
想flexbox容器添加一个负的外边距(左边和右边),其值等于间隔的一半。
想所有的flex单项中添加外边距属性,其值等于间隔的一半。
在所有的flex-basis单项上使用calc()。
在flexbox的父容器上将overflow-x设置成hidden。
body { overflow-x: hidden;}.flexbox { display: flex; flex-wrap: wrap; justify-content: space-between; margin-left: -10px; margin-right: -10px; > div { margin: 0 10px; } .gallery__item { flex-basis: calc(#{percentage(1/3)} - 20px); margin: 10px; }}
这个画廊的好处就是它能处理高度不一的单项,免除你的烦恼:
我们并不会讨论用Susy实现固定间隔的布局,因为我们知道自己只是要去再次实现自己的解决方案。
对于接下来的部分,因为现在的主题是画廊,所以让我们来聊聊如何处理画廊中总是会发生的两种场景:
画廊单项是宽度不一的。
画廊中剩下的单项。
让我们同时用flexbox和Susy来处理这些问题。尽管有所不同的是,我们会用使用了Susy的布局4同使用了flexbox的布局5进行比较,以展示如何来处理这些情况。
假设我们想要让画廊中第一项是其它单项尺寸的两倍。
Flexbox 链接
我们只要给flexbox做一些简单的调整,增加flex-basis百分比,那样它就是其它单项宽度的两倍:
.gallery__item:first-child { flex-basis: calc( #{percentage(2/3)} - 20px);}
Susy 链接
用Susy的话就不容易了,因为它使用的是独立的技术来创建画廊。如果你尝试了使用flexbox时使用的方法,会得到重叠的效果(例如,增加第一项的宽度)。
网格上面的重叠项。 ( 查看大图 )
我们要做的就是通过理解nth-child和独立的技术是如何运作的,来创建出我们自己的画廊。如果你感兴趣的话,可以看看我的 关于CSS技术的博客 来理解独立的技术是如何运作的。
.susy { @include clearfix; .gallery__item { float: left; width: span(4); margin-right: -100%; margin-top: gutter(); } .gallery__item:first-child { width: span(8); } .gallery__item:nth-child(3n-1) { margin-left: span( 8 wide); } .gallery__item:nth-child(3n) { clear: left; } .gallery__item:nth-child(3n+4) { margin-left: span(4 wide); }}
如你所见,使用Susy来改变画廊单项的默认尺寸并不容易。不过,如果你理解了Susy是如何运作的, 你可以将其处理间隔的方式切换到 inside 或者 split, 并且你可以通过使用span()混入来达到同样的简单性:
$susy: ( // Other properties gutter-position: split, );.susy { @include clearfix; .gallery__item { @include span(4); margin-top: gutter() * 2; } .gallery__item:first-child { width: span(8); }}
最后,这些方法运行的效果最后都是一样的。
接下来我们来聊聊如何处理重叠的单项。
我们看过flexbox和Susy是如何通过让重叠的单项悬空来处理其重叠的问题。如果重叠的单项不止一个会如何呢?
Flexbox 链接
用flexbox实现的效果相当不好,因为我们使用了space-between属性。
让flexbox上的单项悬空。 ( 查看大图
)
不过,我们可以通过将每个单项的flex-grow属性设置成1来解决这个问题:
.gallery__item { // Other properties flex-grow: 1;}
但是这样会让第一个单项宽度变成整个容器的宽度,因此你可能会想要注意到会让多少个单项悬空。
重叠的单项占据了整个宽度。 ( 查看大图 )
Susy 链接
Susy 默认会让所有的元素悬空到左边,这样多数基于浮动的布局会做的。
使用基于浮动的布局,重叠的单项会被悬空到网格的左边。 ( 查看大图 )
如果你不想要一堆乱糟糟的代码,那我还是建议你不用这种方法。
不过就你的信息而言,可能创建一个使用Susy画廊的布局跟设置 flex-grow: 1 效果是一样的。
我不准备解释这是如何运作的,因为它相当深入了。不过如果你感兴趣想自己弄清楚的话,我们给你留下一个Codepen:
我们只讨论在flexbox上基于这篇文章管理flex-basis属性。Flexbox提供了附加的属性,诸如:flex-grow 和 flex-shrink,它可以让我们做更有意思的布局,这与之前的网格是不一样的。
这些类型的布局超出了本文的范围,所以我强烈建议你先看看在下一节中提到的flexbox课程。
以上我们已经探讨了很多关于使用 flexbox 和 Susy 这两个工具来进行网站布局的事情。
通过这些探讨,你将会明白,为什么我们不能直接对比flexbox 和 Susy,因为他们本来就是两种不同的工具。
另外,你还会懂得如何使用 Susy 和 flexbox 来构建不同的网站布局,并能解决人们使用这两种工具时面临的各种挑战。
当然,这并不是flexbox 和 Susy 的全部,那还有很多的东西。
这儿我推荐一下相关的资料:
Flexbox 是什么?! , Wes Bos
揭开 Flexbox 的面纱 , Landon Schropp
学习 Susy , Zell Liew
前七章免费.
以上就是文章的全部内容。你读完后有什么想法,欢迎在下面一起讨论,如果你有什么问题,可以 联系我 ,我将很荣幸为您解答!
(rb, ml, al)