指定列数为3,自适应最小列数为2,最小列宽为300px;更多效果可查看博客mdx组件文章
- 实现一个组件,通过指定列数,用于分栏展示
- 在窄屏下,自动减少列数
- 如果有子项横跨多列,则提供minCol用于指定布局最小列数
简单的分栏,可直接根据grid布局实现,代码如下:
/* n栏布局 */
.grid {
display: grid;
grid-template-columns: repeat(n, 1fr);
grid-gap: 10px;
}
样例:
通过指定n,即可实现n栏布局。这样基本效果就实现了,但是对于博客内容而言,在窄屏下,如果栏数过多,就会导致内容过于拥挤。 于是我们可以传入一个minWidth值,让每一列的最小宽度为minWidth,即可通过自适应屏幕宽度来分配列数,达到窄屏下自动减少列数的目的,代码也很简单:
/* 设定最小列宽为200px,如此当n列 列宽之和超过父容器长度时就会自动分配 */
.grid {
--minWidth: 200px;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(var(--minWidth), 1fr));
grid-gap: 10px;
}
显而易见,上面的代码虽然实现了自适应,但是并没有提到列数n,即在屏幕很宽的时候会自适应出来很多列(现在假设我们需要的是3列) 样例:
现在,需求就成为了在保持自适应的同时,限制列数为n,即在屏幕很宽的时候,自适应出来的列数也不会超过n
在这里,我们可以思考上述代码中的列数是怎么计算出来的:
- 已知条件中,我们传入了一个最小宽度minWidth
- 而repeat函数以及minmax根据传入的最小宽度,会先按照最小宽度计算出可容纳的最多列数,即
(父容器宽度100% / minWidth)取整
;然后再去根据列数排列 - 那么按照上面的计算过程,我们能够控制的也就是这个最小宽度minWidth,即我们可以通过更改传入的minWidth来控制列数
- 当然,这里更改的是传入的minWidth,而不是用户传给组件的minWidth;所以之后,将传入repeat的minWidth都记为newMinWidth
因为要满足两个要求
- 屏幕宽度足够时,列数为n
- 屏幕宽度不足时,列数根据最小宽度自适应
那么根据第一个情况,我们可以通过计算得出每一列的宽度,即(父容器宽度100% - (n-1)*gap) / n
,记为minWidth1,然后将这个宽度作为newMinWidth传入repeat函数,即可实现列数为n的效果
按照第二个情况,直接传入minWidth即可实现满足最小宽度的自适应
在第二种情况下,按照第一种情况计算出来的minWidth1是肯定会比minWidth小的(因为计算出来之后不满足最小宽度才进入第二种情况)。所以通过取两者的最大值即可实现两种情况的兼容
.grid {
--n: 3;
--gap: 10px;
--minWidth: 300px;
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(
max(
calc(100% / var(--n) - var(--gap) * (var(--n) - 1) / var(--n)),
var(--minWidth)
),
1fr
)
);
grid-gap: var(--gap);
}
样例
在上述代码中,我们已经实现了n栏布局以及最小宽度的自适应。但是,如果子元素有一个横跨多列,那么就会出现一种情况:在窄屏下,通过自适应,列数到了1,但子元素需要跨m列,就会出现溢出的情况(会导致多余的gap出现,并且溢出的列宽度都为0) 此时,就需要手动指定自适应后的最小列数minCol,即在窄屏下,至少要有m列,这样就能保证子元素不会溢出(实际开发肯定不会出现溢出问题,因为代码都是手写的可以随意更改(如窄屏可通过媒体查询指定其他样式等等),但这里是由组件形式提供使用,所以需要考虑溢出的情况)
样例(试着将屏幕拉窄):
同样按照上面实现最大列数为n,来实现最小列数为m的效果,上面的情形可分为:
- 屏幕宽度足够时,列数为n(同上计算)1
- 屏幕宽度不足时,列数根据最小宽度自适应
- 自适应后的列数小于m,此时需要手动指定列数为m(新增情况)2
- 自适应后的列数大于等于m,此时不需要手动指定列数(同上计算)3
对于这个新增情况,我们可以通过计算得出每一列的宽度,即(父容器宽度100% - (m-1)*gap) / m
,记为minWidth2然后将这个宽度作为newMinWidth传入repeat函数,即可实现列数为m的效果
可以想到,所有情况中minWidth2>minWidth1,在2情况下,minWidth2小于minWidth;在3情况下,minWidth2大于等于minWidth1
- 情况1:取
min(minWidth2, max(minWidth, minWidth1))
得到minWidth1;(此时关系为minWidth2 > minWidth1 > minWidth) - 情况2:按照之前的操作,可取
min(minWidth2, max(minWidth, minWidth1))
得到minWidth2;(此时关系为minWidth2 < minWidth, minWidth1 <= minWidth) - 情况3:取
min(minWidth2, max(minWidth, minWidth1))
得到minWidth;(此时关系为minWidth2 >= minWidth > minWidth1)
所以三种情况都可以通过取min(minWidth2, max(minWidth, minWidth1))
得到最终的newMinWidth
.grid {
--n: 3;
--gap: 10px;
--minWidth: 300px;
--minCol: 2;
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(
min(
calc((100% - (var(--minCol) - 1) * var(--gap)) / var(--minCol)),
max(
calc(100% / var(--n) - var(--gap) * (var(--n) - 1) / var(--n)),
var(--minWidth)
)
),
1fr
)
);
grid-gap: var(--gap);
}
样例(试着将屏幕拉窄):
上面代码中用的是auto-fit,实际也可以用到auto-fill,两者在宽屏上的效果略微不同:参考 auto-fill与auto-fit