1,问题描述
1,问题表现
需求如下:
- 在 flex 布局中,默认1行1个 item,可切换为1行2个 item。
- item 的数量不固定,当1行1个 item 时最多展示4个(1行2个则8个),超出滚动。
代码实现:
<script setup>
import { ref } from "vue";
const isMore = ref(false);
</script>
<template>
<ul class="box" :class="{ more: isMore }">
<li v-for="n in 4" class="item">{{ n }}</li>
</ul>
<button @click="isMore = !isMore">切换</button>
</template>
<style lang="scss">
.box {
box-sizing: border-box;
height: 500px; // 算上间距
padding: 20px;
overflow: auto;
background-color: #fff;
.item {
height: 100px;
margin-bottom: 20px;
background-color: salmon;
&:last-child {
margin-bottom: 0;
}
}
&.more {
display: flex;
flex-wrap: wrap;
.item {
width: calc((100% - 20px) / 2);
&:nth-child(2n + 1) {
margin-right: 20px;
}
}
}
}
</style>
效果展示,一行2个时不符合预期:
2,正常预期
当一行2个时,排列方式如下:
2,原因分析
猜测:纵轴(交叉轴)的对齐方式有问题。但给 box 设置 align-items: flex-start;
,没有任何变化。
查阅资料后发现了一个属性 aligin-content。简单介绍:
- 只对多行 item 有效,也就是说当 item 不换行时,此属性无效。
- 效果和
justify-content
类似,但针对的是纵轴。 - 默认值
aligin-content: stretch
:每一行 item 都等比例拉伸,占满整个纵轴。
默认值的效果:以上面的需求为例,如果没有设置 item height :100px
,则效果如下:
所以,问题的原因就是 aligin-content: stretch
默认值的原因。
3,解决方案
方案1:设置多行 item 与纵轴的起点对齐
.box {
align-content: flex-start;
}
方案2:flex 布局,和容器固定高度滚动的需求分开。
可以看到,如果 box 没有给定固定高度,即便 aligin-content: stretch
默认值也没有关系,因为纵轴没有多余的空间来让它搞事。
所以,可以在 box 外套一层来设置容器高度相关 css 即可。
<template>
<div class="wrap">
<ul class="box" :class="{ more: isMore }">
<li v-for="n in 4" class="item">{{ n }}</li>
</ul>
</div>
<button @click="isMore = !isMore">切换</button>
</template>
<style lang="scss">
.wrap {
height: 500px;
overflow: auto;
background-color: #fff;
}
.box {
padding: 20px;
/* ... */
}
</style>