不同的短视频系统视频的展现效果也是不同的,瀑布流就是当下比较受欢迎的一种视频展示效果,在短视频开发过程中,想要实现瀑布流,既可以使用现有的库,也能自己动手研究,接下来就一起看看在短视频开发视频展现的效果中,瀑布流的原理及实现吧。
原理
在短视频开发移动端的大多数情况下,图片都是等宽不等高。如下图,我们在第一排排下了多个等宽不登高的元素
新元素需要放置第一行元素最短的一个后面
如果还需要在添加元素,需要计算出所有列中最短的列,然后把元素放到最短的列后面
现在来具体分析一下其中的步骤
首先我们需要明确所有的内容其实通过脱离文档流,用定位把元素定位在各自的位置上,所以先调整元素的短视频开发样式
<div className="container">
{
list.map(item => (<img src={item.src} className="item" />))
}
</div>
<style>
.container {
position: relative;
}
.item {
position: absolute
}
</style>
排列第一行
在短视频开发是,先假设一共需要排列column列,然后每列之间的间隔是gap宽度,每一列的宽度为columnWidth。需要渲染的列表是list,每个元素都需要设置他的left值,和top值。因为第一行没有前置元素,所以这里单独处理一下。
const items = docuquery.querySelectorAll('item');
items.forEach((item, index) => {
if (index < column) {
item.style.top = 0;
item.style.left = (columnWidth + gap) * index
}
})
从上面可以看到元素的left的值就是每列的宽度加上边距,在乘上当前是属于第几列。
获取所有列的最小高度
在对第一行进行排版以后,开始最二行进行排版,短视频开发新加入的元素需要在第一行最短的元素的后面,所以我们需要计算所有列的高度进行保存,然后得出高度最小列的长度和最小列所在的索引,所以在第一行元素的排版代码中,我们对每列的高度进行存储
const items = docuquery.querySelectorAll('item');
items.forEach((item, index) => {
if (index < column) {
item.style.top = 0;
item.style.left = (columnWidth + gap) * index
arr.push(item.style.top + top)
}
})
例如上图,元素6的top值就是元素3的高度加上元素3的高度。他的left值就是最小列元素的offsetLeft。代码表示如下:
let minHeight = arr[0];
let index = 0;
for (let j = 0; j < arr.length; j++) {
if (minHeight > arr[j]) {
minHeight = arr[j];
index = j;
}
}
排列剩余元素
我们假设第一行第一个元素是最小的,遍历短视频开发高度的存储数组,找到高度最小的那个,并且找到他的索引列值,然后开始设置下一个元素的位置
items[i].style.top = `${arr[index] + gap}px`;
items[i].style.left = `${items[index].offsetLeft}px`;
设置完新元素的位置后,我们需要更新短视频开发中最小列的高度信息,具体如下:
arr[index] = arr[index] + items[i].offsetHeight + gap;
当所以结果都计算完以后,我们需要更新短视频开发中父元素的高度。因为在文档开头,我们使用了绝对定位,让元素脱离了文档流
const container = document.querySelector('.container')
container.style.height = `${max(arr)}px`;
以上就是短视频开发视频展示效果中瀑布流的实现,也是网上大部分教程的内容
重排列
但是在实际短视频开发中,我们还需要考虑这个图片的加载过程,因为不定高的图片没有加载完成的时候是拿不到他的高度的。所以我们还需要监听这个图片的加载过程,每次当图片加载完成以后我们都需要对其进行重新排版,所以需要在上述代码中加入:
const images = container.querySelectorAll('img');
images.forEach(image => {
image.onload = () => {
refresh();
};
});
获取到容器中会影响布局的图片,然后获取他们,监听他们的onload事件,当短视频开发中某个图片加载完成以后,需要重新排列。
此外考虑到DOM结构的动态修改,所以借助MutationObserver实现对DOM结构变化的监听
const observe = new MutationObserver(() => {
refresh(options);
});
使用flex实现瀑布流
除了使用固定定位外,在短视频开发时我们也可以使用flex来实现瀑布流。例如在前面例子中,一共有column列,列与列之间的宽度为gap。
原理
在flex中,我们可以在容器下面放置column个盒子,然后真正需要排列的元素,就放到每个盒子内部。
下面是DOM的结构
<div className="contianer">
<div className="flex1">
{
list1.map((item) => <img src={item.src}>)
}
</div>
<div classsName="flex2">
{list2.map((item) => <img src={item.src}>)}
</div>
<div classsName="flex3">
{ list3.map((item) => <img src={item.src}>)}
</div>
</div>
<style>
.containter {
display: flex;
}
</style>
在容器下面我们维护了column个列表,在子盒子下面渲染短视频开发对应的列表,例如第一个盒子就渲染第一个列表,以此类推。大多数市面上的文章就介绍到这里就戛然而止,但是事实上我们需要对提供的列表进行重新组装,保证排列的先后顺序
首先还是单独对第一行进行处理,首先生成column长度的数组去存储每列需要渲染的元素
const arr = Arry.from({length: column}).fill([])
list.forEach((item, index) => {
if (index < column) {
arr[0].push(item)
}
})
然后在添加第二行的时候我们仍然需要去获取短视频开发中当前高度最短的列,然后把元素加入对应的列数中。至于最小高度计算和最小高度的列的计算方式和上面绝对定位的方式类似,这里就不一一讲解了
相比较于短视频开发采用的绝对定位,在最后我们不需要设置容器的高度,因为容器的高度能够被容器内部的元素撑起来的。
总结
对比两种实现方式,核心的内容都是一样的,需要明白如何去计算最小的高度,新加入的元素如何加入到已经的排列结果中的。如果在短视频开发时,图片的宽高是未知的,还需要去监听图片的宽高进行重新排版。