我在寻找同样的答案,由于找不到任何有用的解决方案,我设法自己解决了这个问题。以下是我的解决方案,只使用CSS Grid,完美地解决了这个问题。
简而言之:
解决方案A - 推荐(3-5列,每列最小宽度为64像素)
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%/3, max(64px, 100%/5)), 1fr));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%/3, max(64px, 100%/5)), 1fr));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
解决方案B(每列3-5列,每列宽度为64像素)
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3, max(64px, 100%/5))));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3, max(64px, 100%/5))));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
可重复使用的解决方案(Sass mixin)
如果您想要一个可重复使用的函数,以便轻松生成具有不同最大和最小列值的列布局,允许您在列之间添加一些间隙或调整列的最小宽度,那么您应该利用Sass mixins和CSS变量,就像这样:
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
--min-cols: #{$min-cols};
--max-cols: #{$max-cols};
--cols-min-width: #{$cols-min-width};
--grid-row-gap: #{$grid-row-gap};
--grid-column-gap: #{$grid-column-gap};
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min((100%/var(--min-cols) - var(--grid-column-gap)*(var(--min-cols) - 1)/var(--min-cols)), max(var(--cols-min-width), (100%/var(--max-cols) - var(--grid-column-gap)*(var(--max-cols) - 1)/var(--max-cols)))), 1fr));
gap: var(--grid-row-gap) var(--grid-column-gap);
}
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
--min-cols: #{$min-cols};
--max-cols: #{$max-cols};
--cols-min-width: #{$cols-min-width};
--grid-row-gap: #{$grid-row-gap};
--grid-column-gap: #{$grid-column-gap};
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, min((100%/var(--min-cols) - var(--grid-column-gap)*(var(--min-cols) - 1)/var(--min-cols)), max(var(--cols-min-width), (100%/var(--max-cols) - var(--grid-column-gap)*(var(--max-cols) - 1)/var(--max-cols))))));
gap: $grid-row-gap $grid-column-gap;
}
.parent {
@include grid-min-max-cols(3, 5, 64px, 5px, 5px);
}
请查看我的CodePen,以查看可重复使用的解决方案(A和B)的完全功能示例。
https://codepen.io/btous/pen/QWvGNGm
与弹性盒子相似的方法
请查看我的CodePen,以查看一个使用弹性盒子实现类似方法的完全功能示例:
https://codepen.io/btous/pen/OJzwdJw
深入了解!
理解CSS属性grid-template-columns
中的minmax()
函数
CSS语句grid-template-columns: repeat(auto-fit, minmax(min, max))
告诉浏览器根据元素容器的宽度和min
和max
值来计算列数。因此,解决这个问题的关键是理解minmax(min, max)
函数在repeat()
函数中以及在grid-template-columns
CSS属性上的工作原理。
如果我们有一个带有以下CSS属性和值的网格容器,
grid-template-columns: repeat(auto-fit, minmax(min, max)
,它会将容器分割为尽可能多的列,其宽度由
minmax()
函数的
max
参数确定。因此,如果我们将
grid-template-columns
属性设置为
repeat(auto-fit, minmax(50px, 100px)
,在一个500px的容器上,它将被分割为5个100px的列,因为这是
minmax()
函数的
max
参数。
如果容器的宽度增加到550px,它仍然可以容纳最多5列(因为要容纳另外100px的列,它至少应该有600px的宽度),但会有一个50px的剩余空间留下来。
如果
minmax()
函数的第二个值小于第一个值,它将以第一个值作为最终值,所以
minmax(100px, 50px)
将得到100px。
如果
max
值设置为1fr(1份),浏览器将按照前两点的解释进行操作,但是使用
min
值来计算要将容器分割成的列数,并且如果有任何剩余空间,它将平均分配给所有列。因此,如果我们将
grid-template-columns
属性设置为
repeat(auto-fit, minmax(100px, 1fr)
,在一个与第二点定义的容器宽度相同的容器上(550px),它仍然可以容纳最多5列,并且会有一个50px的剩余空间,但是这个剩余空间不会被留空,而是平均分配给所有5列(每列20px),结果是一个每列120px的5列布局。
请参阅CSS-TRICKS的帖子以获取更多详细信息:
https://css-tricks.com/auto-sizing-columns-css-grid-auto-fill-vs-auto-fit
既然我们知道它的工作原理,我们可以逐步面对问题并解决它,以便为minmax()
函数设置min
和max
参数。
我们有两种不同的方法让grid-template-columns
属性计算列数:
第一种方法,如第四点所解释的那样,是将
min
值设置为我们想要的每列宽度,并将
max
值设置为1fr。
第二种方法,如前两点所解释的那样,是将
max
值设置为我们想要的每列宽度,并将
min
值设置为0px,因为我们希望确保
min
参数永远不会高于
max
参数,以避免它被视为最终值,正如第三点所解释的那样。
这里的要点是,如果为
?符号设置的值是静态的,当容器可以容纳超过所需的最大列数时,它将继续将它们添加到布局中;而当容器没有足够的空间来容纳所需的最小列数时,它将从布局中删除它们。
好了,现在我们知道我们需要让浏览器动态计算这个值,以确保不会破坏我们定义的最大和最小列布局,所以让我们来做吧!
动态计算
minmax()
函数的列宽
这是最棘手的部分。首先,我们需要确保列数永远不超过我们所期望的最大列数(在您的情况下为5,但可以是任何值)。
为了实现这一点,我们必须确保列宽永远不超过容器宽度除以最大列数(对于您来说是5),即100%/5,所以现在我们有以下内容:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(calc(100%/5), 1fr));
}
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, calc(100%/5)));
}
酷,现在布局不超过最大的5列了,但它总是返回一个5列的布局,因为容器总是可以适应其同等宽度除以5的5列。请参见下面的片段:
解决方案A:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(calc(100%/5), 1fr));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
解决方案B:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, calc(100%/5)));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
为了在布局缩小宽度时减少列数,我们需要告诉它你想要的最小列宽,比如64像素,这样它可以在容器宽度不适合时开始移除列。
我们将通过将
minmax()
函数的参数包装在一个
max()
函数中来实现这一点,该函数将取我们两个参数的最大值(期望的最小列宽,例如64像素,和我们已经有的值,即calc(100%/5)),得出以下结果:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(max(64px, 100%/5), 1fr));
}
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, max(64px, 100%/5)));
}
请注意,我们不需要在
max()
函数内部使用
calc()
函数。
通过上述代码,每当100%/5的结果小于64px时,它将将
max()
函数的值设置为64px,并且
minmax()
函数将被转换为
minmax(64px, 1fr)
(对于解决方案A)或
minmax(0, 64px)
(对于解决方案B),并且会将容器宽度除以64px来计算可以容纳多少列。请参见下面的片段:
解决方案A:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(max(64px, 100%/5), 1fr));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
解决方案 B:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, max(64px, 100%/5)));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
现在我们已经正确设置了最大列数,但仍需要设置最小列数(在您的情况下为3)。为此,我们需要确保
minmax()
函数的参数永远不会超过容器的三分之一,即100%/3。
我们将通过再次包裹
minmax()
函数的参数来实现这一点,但这次是在一个
min()
函数内部,并且除了我们已经有的值
max(64px, 100%/5)
之外,还要加上前面段落中找到的值,结果如下:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%/3, max(64px, 100%/5)), 1fr));
}
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3, max(64px, 100%/5))));
}
您可以在下面的片段中看到他们的行为:
解决方案 A:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%/3, max(64px, 100%/5)), 1fr));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
解决方案 B:
.parent {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3, max(64px, 100%/5))));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
允许CSS列间隙
通过这样做,我们已经设置好了布局,但是如果我们能够在网格中设置间隙而不破坏它将会很好,所以让我们来做吧!
如果我们想要设置一个column-gap
属性(因为行间隙不会影响我们的布局),比如说,5像素,当我们计算列宽以设置布局时,我们必须考虑这个属性。如果不考虑,它就会打乱。
为了做到这一点,我们必须在计算minmax()
函数的max
参数时,从每一列减去间隙。
我们可以通过将column-gap
值乘以列数减一来得到每一列的比例间隙,因为间隙总是比列数少一个。这将导致:
.parent {
display: grid;
row-gap: 5px;
column-gap: 5px;
grid-template-columns: repeat(auto-fit, minmax(min(100%/3 - 5px*(3-1)/3, max(64px, 100%/5 - 5px*(5-1)/5)), 1fr));
}
.parent {
display: grid;
row-gap: 5px;
column-gap: 5px;
grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3 - 5px*(3-1)/3, max(64px, 100%/5 - 5px*(5-1)/5))));
}
在下面的片段中查看他们的行为:
解决方案 A:
.parent {
display: grid;
row-gap: 5px;
column-gap: 5px;
grid-template-columns: repeat(auto-fit, minmax(min(100%/3 - 5px*(3-1)/3, max(64px, 100%/5 - 5px*(5-1)/5)), 1fr));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
解决方案B:
.parent {
display: grid;
row-gap: 5px;
column-gap: 5px;
grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3 - 5px*(3-1)/3, max(64px, 100%/5 - 5px*(5-1)/5))));
}
.child {
width: 64px;
height: 64px;
max-width: 100%;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin-top: 0;
text-align: center;
}
.parent {
justify-content: space-evenly;
margin: 0 auto;
padding: 5px;
border: 5px solid dodgerblue;
background: wheat;
overflow: hidden;
resize: both;
}
.child {
margin: 0 auto;
border: 2px solid brown;
}
<p>You can resize the blue container</p>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
制作一个可重用的函数,适用于任意数量的最小和最大列数,任意列的最小宽度和任意的column-gap值。
我们已经实现了我们的目标,即创建一个响应式布局,最多有5列,最少有3列,每列的最小宽度为64px,列之间的间隔为5px。这看起来非常好,但是如果出于某种原因我们想要改变其中一个属性的值或者其中一些属性的值,我们将不得不重新审查grid-template-columns属性,并在记住和理解所有的函数和数字后,将其更改为我们需要的值。
为什么不利用Sass mixins和CSS变量,使得我们只需更改变量的值,让代码为我们完成其余工作呢?
我们可以创建一个mixin,将min-cols、max-cols、cols-min-width和grid-column-gap作为参数,并将grid-template-columns属性中的显式值替换为这些参数。然后,在CSS文件中的容器选择器中,我们可以包含这个mixin,并为每个值设置一个变量,以便在需要更改任何值时更清晰明了。
在SCSS中,它会看起来像这样:
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
--min-cols: #{$min-cols};
--max-cols: #{$max-cols};
--cols-min-width: #{$cols-min-width};
--grid-row-gap: #{$grid-row-gap};
--grid-column-gap: #{$grid-column-gap};
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min((100%/var(--min-cols) - var(--grid-column-gap)*(var(--min-cols) - 1)/var(--min-cols)), max(var(--cols-min-width), (100%/var(--max-cols) - var(--grid-column-gap)*(var(--max-cols) - 1)/var(--max-cols)))), 1fr));
gap: $grid-row-gap $grid-column-gap;
}
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
--min-cols: #{$min-cols};
--max-cols: #{$max-cols};
--cols-min-width: #{$cols-min-width};
--grid-row-gap: #{$grid-row-gap};
--grid-column-gap: #{$grid-column-gap};
display: grid;
grid-template-columns: repeat(auto-fit, minmax(0, min((100%/var(--min-cols) - var(--grid-column-gap)*(var(--min-cols) - 1)/var(--min-cols)), max(var(--cols-min-width), (100%/var(--max-cols) - var(--grid-column-gap)*(var(--max-cols) - 1)/var(--max-cols))))));
gap: var(--grid-row-gap) var(--grid-column-gap);
}
.parent {
@include grid-min-max-cols(3, 5, 64px, 5px, 5px);
}
有了这些,我们就可以生成一个具有最大和最小列数的漂亮的列布局了。