📔引言
瀑布流作为一种常见的网页图片呈现方式,其交互效果广受欢迎,可参考花瓣网。本文将详细解析图片瀑布流的实现原理,包括懒加载,并演示如何与服务端进行数据交互。
📔准备工作
首先,让我们先来审视一下项目的目录结构。在该结构中,app.js是服务端启动文件,主要负责提供接口,返回所需的图片数据,而index.html则是瀑布流页面。
服务端的app.js利用Express框架搭建本地服务器。通过访问http://127.0.0.1:3000,默认返回瀑布流页面。获取图片接口通常采用分页模式,通过`pageNo`和`pageSize`参数进行控制。由于我们只提供简单的数据服务,可以根据请求参数返回相应的图片列表。值得注意的是,假设图片数量大于300,则不再返回数据,仅返回空数组。
在index.html页面中,为了支持IE9及以上浏览器,我们引入了第三方CDN提供的Promise和使用axios库进行页面的ajax请求。所有函数均为普通函数,变量声明使用var,以确保兼容性。
CSS方面,我们通过将瀑布流块水平居中,并对内部的item元素添加阴影效果,以提升页面视觉体验。
🚀实用工具函数
JavaScript中包含许多实用的工具类函数,下面我们将逐一详细介绍其中的几个。
🍚getRandomInt函数
getRandomInt函数用于生成指定范围内的随机整数,包括范围两端的边界值。
🍚getRandomHeight函数
getRandomHeight函数用于获取介于200到500之间的随机高度。在实际项目中,由于收集几百张高度不一致的图片可能会比较困难,我们可以利用随机数来模拟这一过程。
🍚getRandomColor函数
getRandomColor函数用于生成随机背景色,包括透明度,透明度的取值范围为0.1到1。
🍚createItem函数
createItem函数用于创建div元素项。由于图片地址不可用,相关代码已经注释。元素项的高度和背景色通过之前介绍的工具函数生成。
🍚request函数
request函数用于向服务器请求获取图片,其中params包含pageNo和pageSize信息。
🍚debounce函数
debounce函数是一个防抖函数,用于限制函数的触发频率。为了兼容IE,函数参数列表需要处理成数组的形式。
📝瀑布流布局原理
在瀑布流中,为了形成交错的样式,元素的排列必须通过定位来实现。因此,外层的waterfall容器需要设置为相对定位,而内部的元素则需要使用绝对定位。
🍚getCols函数
确定页面显示几列的关键在于计算每列的宽度。其中,width表示元素项的宽度,gap表示项与项之间的间隙。表达式n * width + (n - 1) * gap代表多列元素项所占的总宽度,应当小于body的宽度。同时,考虑到body左右需要留一部分间隙,我们假设总宽度小于bodyWidth - margin * 2。通过调整等式,并通过~~运算符(类似于parseInt)取整,我们可以得到列数。
📝瀑布流布局基本原理
瀑布流的核心原理在于,首行元素铺满后,后续元素将定位在当前高度最小的列的后面,依次往后定位以填充整个布局。因此,全局需要维护一个heights数组,用于存放每一列的当前高度。这样,在元素定位时,我们可以选择高度最小的列,并更新该列的高度,以确保整体布局的平衡。
这种布局方式使得瀑布流在展示不同高度的元素时,呈现出美观的交错效果。通过维护列的高度信息,我们能够动态适应不同尺寸的屏幕,并保持瀑布流布局的灵活性。
📝列索引和外层元素设置
🍚getMinIndex函数
getMinIndex函数用于获取heights数组中值最小的列的索引。
🍚setWaterFallRect函数
由于外层waterfall块和内层元素定位的原因,内层元素脱离文档流,导致外层高度塌陷。因此,需要根据列数和heights数组共同设置外层元素的宽高。
🍚waterfall函数
waterfall函数实现了上述功能。首先,首行元素铺满同时填充高度值到heights数组中。后续的元素需要判断heights数组中值最小的索引,计算出left和top定位值并应用于当前元素。在for循环结束时,所有元素项的布局定位完成,此时再更新外层waterfall块的宽高。
注意,for循环中变量i的初始值为loaded,而loaded用于对已完成布局定位的元素计数。这是因为配合懒加载,每次懒加载新增元素时,只对新增的元素进行布局定位,而之前的元素则不再布局,以此来优化性能。
通过这些函数,瀑布流的布局和定位得以实现,而懒加载的结合进一步提高了页面性能。
👉实现初始化功能
已经完成了基础的工具函数和功能函数,现在需要初始化整个瀑布流界面。isReq用作节流阀,后面接入懒加载时,当滚动条触发过于频繁且接口处于请求过程中,则不再发起请求。
total用于记录请求的图片总数,每次请求成功后分页码加1,下次请求则获取下一页的数据。
createDocumentFragment函数用于将创建的DOM元素加入到文档片中。待所有的DOM创建完成并加入到文档片中时,再将文档片一次性插入到waterfall块中。
通常的方式是创建完元素就直接添加到waterfall中,但是每次插入都会导致页面重排。而由于createDocumentFragment存在于内存中,不在DOM树中,因此将文档片插入到waterfall块中时,页面仅仅进行一次重排。
👉懒加载实现
我们通过将窗口滚动条事件与lazyLoad函数绑定,实现了懒加载的效果。每次滚动都会触发lazyLoad,在这个函数中,我们注意到文档未显示的内容高度为documentHeight - scrollTop - clientHeight。通常情况下,如果这部分高度小于窗口高度的一半,我们就加载新的数据。
如果满足这个条件,并且完成布局的元素数量loaded大于或等于请求的图片数量total,表示服务端返回的数据已经全部加载完成,不再需要请求数据。因此,我们注销了滚动条事件。
👉响应式设计
在这个基础上,我们添加了响应式功能。即在浏览器窗口宽度改变时,动态切换列数。窗口宽度改变后,整个页面的元素项需要重新布局,因此我们需要重置loaded和heights。同时,如果窗口宽度低于body的最小宽度,无需重新布局,即无论窗口如何改变,至少显示两列。
🔢完整的代码
axios 和 promise-polyfill 下载本地或CDN引入皆可
🐠效果图
🌈懒加载
🔄响应式