最近我又开始思考分类页面如何更加实用,此前我一直都尝试使用 flex 布局来实现卡片页面,现在我有了新的见解。
flex 布局
flex 示例
一个简单的卡片 CSS 如下所示:
容器
display: flex;
flex-wrap: wrap;
gap: 1rem; // 内距
卡片(方式 1)
flex-grow: 1;
flex-shrink: 1;
flex-basis: 15rem; // 每个卡片的宽度
可缩写:flex: 1 1 15rem;
进一步缩写:flex: 15rem;
提示:在 flex 布局下,设置 gap 后,会默认占用容器。为了保证卡片在每一行都会占满容器的宽度,我们在 flex 设置了 flex-grow: 1 与 flex-shrink: 1 使其根据容器的宽度自动拉伸卡片的宽度。
卡片(方式 2)
width: calc(15rem - 1rem);
提示:在 flex 布局下,设置 gap 后,会默认占用容器。所以需要对卡片减去内距后,就不会超出容器。
然后卡片就会根据定义的属性自动罗列出来。
flex 弊端
所以风记星辰之前的分类页面,都是使用这个样式。后来我发现 flex 对于这种布局出现了一些弊端,在使用子元素自动伸缩属性 flex-grow: 1 与 flex-shrink: 1
后,flex 对于最后一行的卡片处理方式是自动拉伸宽度,导致卡片及其膨胀,很大程度地影响了浏览实用性。如下图所示:
因此我们不得不再对每个卡片设置最大高度,也就是 max-height。如此一来我们所有的代码变得复杂起来。于是,我开始尝试选择 grid 布局。
flex 移动端方案
后来数学函数(calc、min、max 等)在 CSS 开始流行起来。我开始寻求为了可以不使用媒体查询,就完全应付 PC 和移动端的样式,通过一行代码解决自适应布局。于是有了以下的代码:
卡片
flex: min(15rem, 100%);
这段代码的含义是,在 15rem 和 100%取最小值,当屏幕宽度超过了 15rem 时(例如电脑),卡片宽度采取 15rem 的值。当屏幕小于 15rem 时,例如在手机端就会铺满整个宽度。这点在制作自适应卡片时,尤其好用。
grid 布局
grid 示例
在 grid 方式下,布局代码变得更加简单:
容器
display: grid;
gap: 1rem;
grid-template-columns: 示例如下;
// 例 1:设定一行 4 个自适应卡片的布局
grid-template-columns: 1fr 1fr 1fr 1fr;
可缩写:grid-template-columns: repeat(4, 1fr);
进一步缩写:grid: auto / repeat(4, 1fr);
// 例 2:设定卡片按固定 15rem 大小自适应卡片的布局
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
进一步缩写:grid: auto / repeat(auto-fill, minmax(15rem, 1fr));
你会发现所有的设定,只需要对容器设置几行代码即可,十分简便。
grid 弊端
但是上述例 1 会将卡片数量固定,无法满足自适应。而例 2 因为最小卡片宽度还是 15rem,但在移动端宽度小于 15rem 时,则会出现卡片超出容器宽度的问题。因此不得不使用媒体查询来实现自适应,可如此就无法实现以最少量代码实现布局了。因此我曾一度不愿使用 grid 布局,后来依然是数学函数的出现,解决了我的问题,于是有了以下的答案。
grid 移动端方案
为了继续使用一行代码适应移动端,我开始将数学函数放置里面。于是不需要媒体查询的新方案来了:
display: grid;
gap: 1rem; // 内距
grid: auto / repeat(auto-fill, minmax(min(15rem, 100%), 1fr)); // 每个卡片宽度
解释和 flex 下的设定是一样。现在,风记星辰就有了新的自适应分类页面。而且一行代码不需要媒体查询就可以实现,电脑和手机完全自适应的状态,如下:
结语
CSS 的发展让前端变得越来越简单,不得不说时代在进步。不仅仅上述的卡片布局,grid 布局实际上强大到可以实现很多其他布局。代码就摆在那里,就看每个人的创意了。