最近我又开始思考分类页面如何更加实用,此前我一直都尝试使用 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 对于最后一行的卡片处理方式是自动拉伸宽度,导致卡片及其膨胀,很大程度地影响了浏览实用性。如下图所示:

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 下的设定是一样。现在,风记星辰就有了新的自适应分类页面。而且一行代码不需要媒体查询就可以实现,电脑和手机完全自适应的状态,如下:

1080P 下的样式
2K 下的样式
手机下的样式

结语

CSS 的发展让前端变得越来越简单,不得不说时代在进步。不仅仅上述的卡片布局,grid 布局实际上强大到可以实现很多其他布局。代码就摆在那里,就看每个人的创意了。