原生js一步一步实现瀑布流
瀑布流一般的常见的需求有这三种,一是每列固定宽度,这种比较常见,比如花瓣网;另一种是每行固定高度,这种少见一些,典型的例子是百度图片,bing,谷歌图片,还有一种是宽高都不确定,这种需求就比较奇葩了,我们仅讨论第一种瀑布流。
实现这类瀑布流一般的做法是维护一个包含不同列高的数组,先排列第一行,初始化这个数组以后,然后后面的图片(或者是图片的容器)通过绝对定位来排列到最短的列上,并更新数组,随后下一个元素做相同的操作。当窗口尺寸变化怎么办呢?最偷懒的是初始化时就写死容器宽度,当页面太小就出现横向滚动条,这种没啥难度;或者每次修改尺寸就刷新一下页面,可能有些人会嗤之以鼻,还真有大厂这么干,微软的bing搜索的图库就是这么做的;
另一种方式是像花瓣网一样,对所有图片进行重新定位,这也是我们接下来要使用的方式。
首先,我们要准备一系列尺寸不一的图片。一般来说我们使用瀑布流的方式,后台会返回给我们一组图片,里面会包含尺寸信息,虽然前端可以计算出来,但是出于性能优化考虑,这样显然是不提倡的。
出于简单考虑,我的数据只包括了图片地址和尺寸信息,并在页面写死了数据而不是通过后台请求,以此为基础,当滚动页面获取新数据的时候,我们回从这些数据中随机拿出几个组成新的数组来模仿后台响应。
准备图片数据
这里是我预先准备的数据:
1 | var imgInfoList = [ |
构造假数据模拟后台
这是从随机获取元素组成新数组的函数:
1 | /** |
初始化第一行
页面载入完成后,先计算出第一行可容纳的列数,然后排练第一行数据,并初始化 包含每列高度的数组:
1 | window.onload = function () { |
- toFixed会返回string,使用parseFloat可以重新转化为number
- createDocumentFragment可以创建文档碎片,然后一次性将所有元素添加到容器中,可以提高性能
- 给oImgBox元素添加height和width属性是为了方便后面重排列
- colLeftList数组是为了方便后面的元素排列时使用
- 对imgInfoList进行slice操作是为了只针对后面新加进来的元素进行排列
新创建新元素并排列
fallImages函数用于创建新的图片并排列,具体的做法和上面的类似,主要的操作是获取数组中的最小列高并将新元素排到该列后面,然后更新该列在数组中的高度:
1 | /** |
ps:Math.min和Math.max方法并不接受数组参数,所以我们要求数组的最值需要使用Function.prototype.apply方法将其展开。
滚动页面“刷新”数据
接下来,需要添加滚动到底部获取“新数据”的功能:
1 | var end = 0; // 没有更多数据标识 |
ps:这里的end、time、moreFakeImgInfo都是为了模仿后台响应的假数据
重新排列
到此为止,我们的瀑布流已经实现了,不过还有一些细节需要完善,比如说修改窗口/容器尺寸,如何对所有图片进行重新排列,如果大家看过上面的话,这一步就非常简单了,我们只需要获取页面中所有的img-box,然后对他们进行上面的操作,唯一的不同是,我们不用创建新元素,只需要对定位修改:
1 | /** |
是不是很简单,后面如果有时间我会对另外两种瀑布流的实现方式也进行讲解。