介绍
相信大家对瀑布流并不陌生,瀑布流又称瀑布流式布局。是直播软件搭建时比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。
他具有以下特点:
1、直播软件搭建展示页面以图片为主
2、图片尺寸不一交错排布
3、空间使用更加充分,节省了空间
既然瀑布流适用直播软件搭建场景那么多,我们本期就是以瀑布流在前端的三种实现方案,以及他们使用的优缺来探索。
正文
A. column方案
column可以指定容器下元素列的宽度和数量
columns: column-width column-count;
但我们瀑布流用到的css属性是:
- column-count:指定列数
- column-gap:列之间的差距
实现
<ul class="img-wrapper">
<li><img src="./image/1.jpg" alt=""></li>
<!--
...
...
...
-->
<li><img src="./image/20.jpg" alt=""></li>
</ul>
我们在html中先放入二十张图片作为本次演示。
.img-wrapper{
column-count: 4;
column-gap: 10px;
counter-reset: count;
width: 960px;
margin: 0 auto;
}
.img-wrapper>li{
position: relative;
margin-bottom: 10px;
}
这里我们用column-count设定为了4列,column-gap间距为10像素。就这么简单的两句我们就实现了直播软件搭建中的一个瀑布流。
对了,为了我们更直观的观察排列规律,我们用伪类再做个计数器(后面的方案都会出现该伪类和html结构)。
.img-wrapper>li>img{
width: 100%;
height: auto;
vertical-align: middle;
}
.img-wrapper>li::after{
counter-increment: count;
content: counter(count);
width: 2em;
height: 2em;
background-color: rgba(0,0,0,0.9);
color: #ffffff;
line-height: 2em;
text-align: center;
position: absolute;
font-size: 1em;
z-index: 2;
left: 0;
top: 0;
}
优点
1、在直播软件搭建中实现简单,仅需两行核心代码。
2、图片自动填充不用考虑图片加载状态。
缺点
1、兼容性仍有些不尽人意。
2、直播软件搭建排列规律永远都是先上下再左右,无法控制,动态加载会出现严重问题。
B. flex方案
用到的html结构与伪类计数与A方案相同,这里不再过多占用篇幅。
这个方案用到了弹性盒子,你没听错,用弹性布局也可以实现一个瀑布流,虽然很多局限性,但是也可以,在一定需求场景内使用。
实现
.img-wrapper{
display: flex;
flex-wrap: wrap;
flex-direction: column;
height: 1300px;
}
.img-wrapper>li{
position: relative;
width: calc(100% / 4);
padding: 5px;
box-sizing: border-box;
}
我们对父容器设置弹性盒后,因为直播软件搭建瀑布流是多行的所以还要flex-wrap设置wrap,并且flex-direction还要设置为column。最关键的是一定要设置一个高度。当然在其子元素用百分比设定要显示几列。
就这样,flex也可以实现一个瀑布流了。
当然,我们如果想在直播软件搭建时改变一定程度的序列优先级,可以改变css的order属性。
.img-wrapper>li:nth-child(4n+1){
order: 1;
}
.img-wrapper>li:nth-child(4n+2){
order: 2;
}
.img-wrapper>li:nth-child(4n+3){
order: 3;
}
.img-wrapper>li:nth-child(4n){
order: 4;
}
优点
1、在直播软件搭建中实现相对简单。
2、图片自动填充不用考虑图片加载状态。
3、顺序在一定程度上可以改变。
缺点
1、高度是固定的,很难做活。
2、顺序虽然可以改变,但是仍然不灵活,不尽人意。
C. js+absolute方案
js实现瀑布流的话,我们可以考虑把子元素全部设置成绝对定位。然后监听图片加载,如果加载完就把子元素设置其对应的位置,逐个塞到父容器中。
实现
import Waterfall from "./js/Waterfall"
window.onload = new Waterfall({
$el: document.querySelector(".img-wrapper"),
count: 4,
gap: 10
})
我们先在配置阶段把父容器和列数,间距设置好。
然后再去写Waterfall类:
export default class Waterfall {
constructor(options) {
this.$el = null; // 父容器
this.count = 4; // 列数
this.gap = 10; // 间距
Object.assign(this, options);
this.width = 0; // 列的宽度
this.items = []; // 子元素集合
this.H = []; // 存储每列的高度方便计算
this.flag = null; // 虚拟节点集合
this.init();
}
init() {
this.items = Array.from(this.$el.children);
this.reset();
this.render();
}
reset() {
this.flag = document.createDocumentFragment();
this.width = this.$el.clientWidth / this.count;
this.H = new Array(this.count).fill(0);
this.$el.innerHTML = "";
}
render() {
const { width, items,flag,H,gap } = this;
items.forEach(item => {
item.style.width = width + "px";
item.style.position = "absolute";
let img = item.querySelector("img");
if(img.complete){
let tag = H.indexOf(Math.min(...H));
item.style.left = tag * (width + gap) + "px";
item.style.top = H[tag] + "px";
H[tag] += img.height*width/ img.width + gap;
flag.appendChild(item);
}
else{
img.addEventListener("load", () => {
let tag = H.indexOf(Math.min(...H));
item.style.left = tag * (width + gap) + "px";
item.style.top = H[tag] + "px";
H[tag] += img.height*width/ img.width + gap;
flag.appendChild(item);
this.$el.append(flag);
})
}
})
this.$el.append(flag);
}
}
我们这里就简单实现了一下:
- 初始化,计算出列宽来,将H作为列高存储器,4列那么就是[0,0,0,0]。然后收集子元素后,清除父容器内容。
- 遍历其子元素,设置其都为绝对定位,设置其列宽。后监听其下的图片加载是否完毕。
- 如果加载成功,那么计算应该在的位置,瀑布流的常规原则是哪一列数值最小就在那一列上设置新图片。当然他的相对高度和间距也要计算出来,同时在H当前列上要把高度存起来。
- 每次图片加载完就更新虚拟节点到父容器中。
优点
1、在直播软件搭建中控制灵活,随意扩展。
2、也可以无限加载,不用过多考虑兼容问题。
3、同时可以添加诸多动画来增强用户体验。
缺点
1、在直播软件搭建中实现相对复杂。
2、图片填充需要考虑图片加载状态。
3、性能逊色于纯css实现。
思考
以上便是在直播软件搭建中瀑布流实现的三种方式,其实绝大部分的商业级都是js实现的瀑布流,本质实现起来并不麻烦,我们有时间可以自己封装一套。