CSS实现居中+换行+左对齐
发布时间:
最后更新:
我的博客的首页有两个卡片栏(文章和友链),为了适配不同宽度的屏幕需要实现居中和自动换行。为了显得整齐,新的一行需要从左起(即左对齐),每张卡片的大小都是一样的。
不同宽度的屏幕下效果如图:
最终效果用动画演示大概就是这个样子,虚线框#container
是卡片容器元素,里面的卡片.card
居中排列,放不下的从新行左边开始。
动画演示
传统布局方式 #
一开始我觉得这需求很简单,毕竟无论是居中还是换行都只需要一行CSS即可搞定,于是先写一个Demo:
<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="utf-8">
<title>测试</title>
<style>
:root {
--card-size: 160px;
--gap: 10px;
}
#container {
margin: 20px auto;
padding: 20px;
width: 560px;
border: dashed #777;
/* 演示 */
animation: vpx 3s linear infinite;
}
.card {
text-align: center;
line-height: 2.5em;
font-size: 4em;
margin: var(--gap) calc(var(--gap) / 2);
width: var(--card-size);
height: var(--card-size);
color: white;
background: rgb(9, 157, 243);
}
@keyframes vpx {
0% {
width: 1000px;
}
100% {
width: 450px;
}
}
</style>
</head>
<body>
<div id="container">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
<div class="card">4</div>
<div class="card">5</div>
</div>
</body>
</html>
首先尝试比较灵活的flex布局:
#container {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
确实是居中+换行了,但是没有满的一行也居中了,与想要的左对齐不一样。如果去掉justify-content
属性就会变成这样:
左对齐+换行,但是没有居中。后来我又试了给外层加上居中属性,但没有任何效果,因为图中可以明显看到右边的空白是在#container
内的,把#container
居中没有意义。
再查查flex的其它用法,也没有找到任何方案,于是flex布局宣告失败。
其它方案呢?我又尝试了绝对定位、float、transform,但都以失败告终,这让我反思这个需求是否真的有我想象的那么简单?
思考一下 #
为何这些尝试全都失败,我简单地思考了下,text-align: center
和justify-content: center
会把每一行都居中肯定不行。传统布局里也没有把内容整体居中这种属性,而且换行都需要先确定容器的宽度,没法根据一行可容纳多少来自动计算。
这就导致了无法用两层容器,在外层决定宽度的容器里加上居中内部元素的属性(例如text-align: center
),然后内层容器仅换行来实现,因为内层宽度无法自动计算会,自动设为100%。
再回头看看需求,发现左对齐+固定大小的卡片其实就是一个网格系统,网格布局不是有display: grid
么,我瞎折腾半天干什么。
经一番查资料,发现grid布局天生就是用来解决这种问题的。
Grid布局的威力 #
#container {
display: grid;
grid-template-columns: repeat(auto-fit, var(--card-size));
justify-content: center;
grid-gap: var(--gap);
}
.card {
/* 由于使用了 grid-gap 故去掉卡片的间距 */
margin: 0 !important;
}
仅需在容器上使用3行就搞定,最关键的是grid-template-columns
里的repeat
函数,这个函数轻松解决了根据一行可容纳的元素数量计算布局宽度的问题。
repeat
函数接受两个参数,第一个是数量,第二个是尺寸。其中第一个参数有一些特殊值可以自动计算数量,比如auto-fit
能够根据第二个参数、容器大小、网格间距等来计算最多放几个。在grid-template-columns
上使用repeat(auto-fit, ...)
,将第二个参数设为卡片宽,即可自动确定每行的格子数,再配合justify-content: center
把格子区域居中,就达到了想要的结果。
效果非常好,完全达到了想要的结果。
另外auto-fit
有一个兄弟叫auto-fill
,这俩的详细区别可以去看CSS-Tricks,从效果上来说,在子元素不够布满一行时,auto-fit
仍会把它们居中,而auto-fill
则是左起。
最后 #
技术一直都在进步,传统难以解决的问题用新的东西有奇效,以前碰到这问题只能用MediaQuery来为不同宽度的屏幕设置不同的列数,或者用JS来做。而使用新的display: grid
,这问题就迎刃而解,不得不说网格布局真是卡片类问题的大杀器。
据我所见,现在用了网格布局的网站并不多,大多遇到这类问题还是用固定的列数,比如手机下单列,宽屏下两三列,2K、4K高清屏就没法自动扩展列数还是两三列,导致左右空白很多,我想用户买高清屏可不是为了看空白的。
早用新功能,早日解脱(什么要兼容IE?)