基于Flex的简单响应式 Grid 布局
为了庆祝一下 HBO 终于推出了《冰与火之歌:权力的游戏4》,也同时帮助我一个一年级的学生重新设计一下他那相当复杂的设计,
我决定今天教程的主题是 Grid Layout (栅格布局)。
其实以上的设计可以用很多方法实现,包括 display: table-cell,inline-block, 或甚至是用 float,
但是我觉得最好的或是最简洁的实现方法就是用 flexbox (当然是在高级浏览器下)。
HTML 代码
这套方案有一个很高大上的地方,就是所用到的标签很简洁,而且没有那些适配的 hacks。
因为插图的缘故,我只在容器 DIV 中展示头两个 figure 元素,所用的插图来自于《冰与火之歌:权力的游戏》,作者:Gavin Bond。
<div id="got-gridbox">
<figure>
<img src="daenerys-targaryen.png" alt="Black and white photograph of Emilia Clarke as Daenerys Targaryen">
<figcaption>Daenerys Targaryen</figcaption>
</figure>
<figure>
<img src="tyrion-lannister.png" alt="Black and white photograph of Peter Dinklage as Tyrion Lannister">
<figcaption>Tyrion Lannister</figcaption>
</figure>
…
</div>
主体 CSS
首先,我们将 DIV 容器设置成 display: flexbox, 用 justify-content 将里面的元素分隔开来。
行式布局依靠 flex-flow:row-wrap 属性.
(同理的,在我最近的一篇文章中用flex实现的磁贴式布局是一种列式布局,用到flex-box: column-wrap属性)
全局盒模型 box-sizing 的覆写是为了避免在我给 figure 元素添加边框后导致整个布局的错位。
* {
box-sizing: border-box;
}
#got-gridbox {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
(浏览器前缀没有加是为了让展示的代码看起来更加简洁,比较新版的浏览器对于 flexbox 的支持已经不再需要特定前缀了,除了Safari。)
下一步,我们来设置 figure 元素。
#got-gridbox figure {
border: 1px solid #333;
position: relative;
font-size: 0;
margin: 2% 0;
flex: 0 0 48%;
}
对每个 figure 加上 position: relative 属性,这样可以给里面的标题文本进行绝对定位。
设定 font-size: 0 的目的是为了“吸光 figure 元素里的空气”---这样不会让其出现莫名的空隙。
样式的关键当然是 flexbox 属性了,以后会对其有更详细的说明。现在,加之之前几篇文章的讨论,我们已经对它有了足够多的了解。
首先确保 figure 的宽度是容器的48%,在元素间留出4%的空隙。
空隙通过 margin 来实现,上下各有2%的外补白,这样可以在上下两行间产生4%的行距。
另一种可以的方法是 flex: 0 0 50%,不用设置 margin ,这样也可以作出无空白的两列式栅栏。
接着,将 figure 中的图片改成响应式的。
#got-gridbox figure img {
width: 60%;
height: auto;
margin-top: -2rem;
}
用到的每一张图片都是同样大小与透明度的 PNG 图,填充到 figure 的方式也完全一致。
我在 margin-top 上用了负值,这样可以让人物“走出” figure 元素,让每个 PNG 图片穿过容器顶部。
唯一不同的地方就是在右边框的图片需要向右对齐:
#got-gridbox figure:nth-child(even) img {
float: right;
}
在左边这一栏中,<figcaption>元素应该在<figure>的右边,我们(先)将这种情况定高默认值。
#got-gridbox figure figcaption {
position: absolute;
right: 4%;
top: 5%;
font-family: Deseret, Trajan Pro, Trajan, Requiem, serif;
text-transform: uppercase;
font-size: 1.6rem;
text-align: right;
}
Deseret 是一种只针对大写字幕的字体,而且不是每台机器里都有,所以我们得在这里作降级处理,给它设定几个后备字体。
在右栏的<figcaption> 元素是在对应的方向上。
#got-gridbox figure:nth-child(even) figcaption {
left: 4%;
right: auto;
text-align: left;
}
这里呈现的是一些主要用到的属性。在这充分运用了 flexbox 天生的响应式能力,
除此之处,我唯一添加的也就是在屏幕变小时适当的改变一下字体的大小。
最后整理一下
在Cersei Lannister的黑白图上面附一个带有透明度的蒙板,这样桌子看起来是在图片的底边上
最后的两张图片我给自己加了一点有趣的点缀:两张图里都有一张桌子,把桌子给抠掉或是裁掉不是一个很好的选择。
不管的话真实图片又不会充满父级元素,期待着用 Photoshop 把图再修一篇也不是个好选择。
结果呢,单独处理这个系统的最后两张图片就显得有点棘手了:绝大多数的开发者应该都会在这两个标签上加一个类,我不太想这么干。
换个思路,我决定用 CSS linear gradient 来给它
画一个虚拟的桌子。用伪类选择符 nth-last-child 来选取最后两个 figure 元素。
#got-gridbox figure:nth-last-child(-n+2) {
background-image: linear-gradient(transparent, transparent 80%, #111 81%);
}
最后,在适当的视窗宽度范围内调整一下标题字体的大小。
@media screen and (max-width: 900px) {
#got-gridbox figure figcaption {
font-size: 1.4rem;
}
}
@media screen and (max-width: 800px) {
#got-gridbox figure figcaption {
font-size: 1.25rem;
}
}
当然也可以用 vm 作为单位来对字体大小进行设定,同时用 @media 对其上限和下限进行限定。
@media screen and (max-width: 750px) {
#got-gridbox figure {
flex: 0 0 100%;
margin-bottom: 7%;
}
#got-gridbox figure figcaption {
font-size: 1.6rem;
}
}
@media screen and (max-width: 450px) {
#got-gridbox figure figcaption {
font-size: 1.2rem;
width: 50%;
}
}
就是这样!如你所见,flexbox 让页面中的栅栏式布局变得更简单,再不用找那些非主流方法甚至是 hacker 方式了。