背景
瀑布流布局在我们现在的前端页面中经常会用的到,有很多优点
- 节省页面空间
- 方便下拉刷新、到底加载等操作
- 让用户有向下浏览的冲动
- 布局美观,错落有致
- 移动端友好
介绍当前各大网站常见的图片瀑布流形式
各种瀑布流及实现
1. 元素宽高均相同 & 列数固定
最简单的图片布局方案,每张图片等宽等高展示,图片按比例进行裁切,列数固定。常用于社交网站。
参考网站:Instagram
优点:
- 图片不变形
- 各个图片展示面积相同
- 行高相同
- 可按照原始顺序输出
- 整体布局整齐
缺点:
- 不能保证完全展示图片内容
- 非响应式
实现方式:
- 由于不能保证完全展示图片内容,为避免图片拉伸,使用
object-fit: cover
裁切图片 - 给每张图片设定 固定宽度 之后使用 flex 布局即可
- 解决最后一行展示问题
.container {
width: 1000px;
margin: 0 auto;
overflow: hidden;
border: 1px solid darkgray;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
div {
width: 247px; // 固定宽高
height: 247px;
margin-bottom: 4px;
// 最后一行三个元素的情况
&:last-child:nth-child(4n - 1) {
margin-right: 251px
}
// 最后一行两个元素的情况
&:last-child:nth-child(4n - 2) {
margin-right: 502px;
}
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
2. 元素宽高均相同 & 列数自适应
上一种布局的增强版,在上一种布局的基础上使得列数不在固定,因此可以适应各种大小屏幕。容器放大缩小的时候图片大小也会跟随变化,图片有最小的显示宽度,如果当前列数下,图片一行展示不下,则自动减少列数。常用于图站索引页。
参考网站:google photos 探索页、500px
优点:
- 图片不变形
- 各个图片展示面积相同
- 行高相同
- 可按照原始顺序输出
- 整体布局整齐
缺点:
- 不能保证完全展示图片内容
实现方式:
- 由于不能保证完全展示图片内容,为避免图片拉伸,使用
object-fit: cover
裁切图片 - 先考虑宽度问题:使用 flex 布局使容器宽度自适应,并设置最小宽度,当前的显示列数就等于 整体宽度/最小值 ,剩余部分会自动撑大
- 再考虑高度问题:由于需要让宽高比例固定,而宽度又是不确定的,故使用
padding-bottom
固定容器比例,因为padding
的百分比是基于父容器的宽度决定的。可参考这个 demo - 解决最后一行展示问题,尾部添加额外标签占满最后一行
.container {
width: 100%;
margin: 0 auto;
overflow: hidden;
border: 1px solid darkgray;
display: flex;
flex-wrap: wrap;
div {
flex: 1 0 250px;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
margin: 2px;
position: relative;
overflow: hidden;
&:after {
padding-bottom: 150%;
content: '';
display: block;
}
}
i {
margin: 2px;
flex: 1 0 250px;
height: 0;
}
}
3. 元素仅宽相同 & 高度自适应 & 列数固定
纵向布局,每列的宽度相同,元素的高度由图片的宽高比例决定,容器总宽度和列数固定。常用于移动端固定两列瀑布流。
参考网站:淘宝手机版
优点:
- 图片不变形
- 保证完全展示图片内容
- 错落有致
缺点:
- 不能按照原始顺序输出,为竖向顺序
- 非响应式
实现方式:
- 使用 多列布局 实现,设置固定的列数
- 使用
break-inside: avoid
防止换列时截断 - 元素高度使用图片比例自动撑开即可
.container {
width: 1000px;
margin: 0 auto;
column-count: 4;
column-gap: 4px;
overflow: hidden;
border: 1px solid darkgray;
div {
break-inside: avoid; // 底部参差不齐而不是换到下一列去
margin-bottom: 4px;
img {
width: 100%;
}
}
}
4. 元素仅宽相同 & 高度自适应 & 列数自适应
上一种布局的增强版,使得列数不再固定。因此可以适应各种大小屏幕。容器放大缩小的时候图片大小也会跟随变化,图片有最小的显示宽度,如果当前列数下,图片一行展示不下,则自动减少列数
参考网站:花瓣
优点:
- 图片不变形
- 错落有致
缺点:
- 不能保证完全展示图片内容
- 不能按照原始顺序输出,为竖向顺序
实现方式:在上一种方式的基础上添加媒体查询即可
.container {
width: 100%;
margin: 0 auto;
column-count: 2;
column-gap: 4px;
overflow: hidden;
border: 1px solid darkgray;
div {
break-inside: avoid;
margin-bottom: 4px;
img {
width: 100%;
}
}
}
@media (min-width: 768px) {
.container {
column-count: 3;
}
}
@media (min-width: 992px) {
.container {
column-count: 4;
}
}
@media (min-width: 1200px) {
.container {
column-count: 5;
}
}
5. 元素仅高相同 & 宽度自适应
横线版本的瀑布流,每一行的高度都是相同的,图片元素宽度由图片本身的比例决定。但由于每行的右侧需要对齐占满,但每行图片的宽度之和并不相等,因此图片是无法展示完全的。
参考网站:百度图片
优点:
- 图片不变形
- 错落有致
- 可按照原始顺序输出
缺点:
- 不能保证完全展示图片内容
实现方式
- 由于不能保证完全展示图片内容,为避免图片拉伸,使用
object-fit: cover
裁切图片 - 给每张图片设定 固定高度 和 100% 的宽度 之后使用 flex 布局即可
- 解决最后一行展示问题
.container {
width: 100%;
border: 1px solid darkgray;
display: flex;
flex-wrap: wrap;
&:after {
content: '';
display: block;
flex-grow: 99999;
}
div {
margin-right: 4px;
margin-bottom: 4px;
flex-grow: 1;
img {
object-fit: cover;
height: 300px;
width: 100%;
}
}
}
6. 每行元素高相同 & 行高自适应 & 宽度自适应
各个行的行高不同,图片的宽度也不同,分别自适应以将图片展示完全。
参考网站:500px
优点:
- 图片不变形
- 错落有致
- 可按照原始顺序输出
- 可显示图片完整内容
实现方式:
- 首先参考 demo 通过修改 padding 的百分比我们可以实现一个等比例的容器。
- 我们可以事先从后台获取到图片的宽高,因此我们就可以针对每一张图片设置和其等比例的容器。demo 。实现的方式就是我们先设定每行的高度为 200px ,因此每个图片容器的宽度就等于 200px * 图片宽/图片高,最后设置
padding-bottom
的比例为元素的高宽比以撑开父元素,使其维持宽高比。 - 然后让图片的宽度为 100% 以占满整个容器 demo
-
然后考虑如何让每行的元素占满整个页面,通过 flex grow 我们可以实现这一点,但是每个元素的放大比例是多少呢。
在前面不 grow 的情况下,每张图片的容器的宽度已经是按比例分配了,而想要实现前一行描述的分配方式,每行的剩余空间,我们希望它仍然按照目前容器宽度所占的比例来分配。因此值显然不是均为1,如果均为1那么每张图片的放大比例均相同,导致的结果就是宽的图片和窄的图片获得的放大量是一样的,比如之前一个100px 和 300px 的图片,均放大 100px 就变为了 200 和 400,由1:3的比例变味了1:2的比例了。由例子我们可以看出,放大比例值其实就是每个容器当前的宽度,即放大前两张图宽度比为1:3,放大后我们希望的放大比例也为1:3。因此将 flex-grow 的值设置为容器宽度就可以了。
- 最后处理最后一行的问题
<div class="container-6">
<div v-for="(item,i) in list" :key="i"
:style="{flexGrow: item.width / item.height,flexBasis: item.width / item.height * 200 + 'px'}">
<img :src="item.url" alt=""/>
<i :style="{paddingBottom:item.height / item.width * 100 + '%'}"></i>
</div>
</div>
.container {
width: 100%;
margin: 0 auto;
border: 1px solid darkgray;
display: flex;
flex-wrap: wrap;
&:after {
content: '';
flex-grow: 999999999;
}
div {
flex: 1 0 200px;
position: relative;
overflow: hidden;
background: red;
margin: 2px;
&:after {
content: '';
display: block;
flex-grow: 99999;
}
i {
display: block;
}
img {
position: absolute;
top: 0;
width: 100%;
}
}
}