零、介绍

这篇文章主要是介绍网站页面瀑布流布局的实现,主要包括:

  1. 瀑布流是什么

  2. 瀑布流的实现原理

  3. 瀑布流的使用场景

  4. 实现中有哪些问题 & 如何解决

  5. 可扩展的使用场景

一、瀑布流是什么

瀑布流, 又称瀑布流式布局,是比较流行的一种网站页面布局。视觉表现为宽度相等高度不定的元素组成的参差不齐的多栏布局,随着页面向下滚动,新的元素附加到最短的一列而不断向下加载。

二、瀑布流的实现原理

瀑布流本质上就是寻找各列之中高度最小的一列,并将新的元素添加到该列后面,只要有新的元素需要排列,就继续寻找所有列中的高度最小列,把后来的元素添加到高度最小列上。

图解基础瀑布流

我们接下来看下为什么要永远寻找最小列?

先看图 1 的排列顺序,第一排元素的顶部会处于同一个高度,依次排列在顶端,第一排排满之后,第二排从左往右排列。然而这种排列方式很容易出现其中一列过长或其中一列过短的情况。

为了解决图1中列可能过长或者过短的问题,我们按照图 2 的方式将元素放在最短的一列进行排列。


三、瀑布流的使用场景

瀑布流滑动的时候会不停的出现新的东西,吸引你不断向下探索,巧妙的利用视觉层级、视线的任意流动来缓解视觉的疲劳,采用这种方案可以延长用户停留视觉,提高用户粘度,适合那些随意浏览,不带目的性的使用场景,就像逛街一样,边走边看,所以比较适合图片、商品、资讯类的场景,很多电商相关的网站都使用了瀑布流进行承载。

上图的蘑菇街PC瀑布流效果是在基础瀑布流的基础上做了扩展改造, 在瀑布流顶部某一列或某几列插入其他非瀑布流内容。

本文将介绍这种扩展瀑布流的四列实现场景,适用基础场景如下:

四、瀑布流的的实现有哪些问题&如何解决

  1. 非瀑布流内容如何插入?

  2. 如何寻找所有列的高度最小者?

  3. 如何渲染瀑布流?

Vue 实现瀑布流

我们采用 Vue 框架来实现瀑布流,其一些自带属性使我们的瀑布流实现更加简单。

  • 通过 ref 可以很方便的获取每列高度,通过比较算法算出高度最小列。

  • 拿到高度最小列之后,将下个要插入的元素数据放到最小列的数据列表(columnList)中,通过操作数据完成元素渲染。

  • 利用 Vue 的具名插槽在瀑布流顶部插入其他非瀑布内容。

  • 通过 watch 监测元素渲染,判断是否继续进行渲染和请求更多元素数据。

非瀑布流内容如何插入

通过 Vue 的具名插槽(slot),将非瀑布流元素作为父组件的内容传递给瀑布流子组件。

  • 父组件通过 HTML 模板上的槽属性关联具名插槽,非瀑布流内容作为具名插槽的内容提供给子组件。

  • 具名插槽有:first-col、second-col、 third-col、 last-col、 merge-slot,分别代表第一、二、三、四、合并列。

  • 子组件通过插槽名字判断将非瀑布流内容放在哪一列。如果插槽存在,则将其所携带的内容插入到置顶位置。

  • 因为合并列的特殊性,如果包含合并列,则将合并列绝对定位到顶部,合并列占的瀑布流对应的列进行下移。父组件传合并列相关的参数给子组件:merge(判断是否包含合并列), mergeHeight(合并列的高度),mergeColunms(合并的是哪 2 列)。

代码实现

如何寻找所有列的高度最小者

每一列都定义一个 ref,通过 ref 获取当前列的高度,如果该列上方有合并块,则高度要加上合并块的高度,然后比较 4 列高度取到最小高度,再通过最小高度算出其对应的列数。

代码实现

// 通过ref获取每列高度,column1,column2,column3,column4分别代表第一、二、三、四列let columsHeight = [this.$refs.column1.offsetHeight, this.$refs.column2.offsetHeight, this.$refs.column3.offsetHeight, this.$refs.column4.offsetHeight]// 如果包含合并块, 则更新高度,合并块下的列高要增加合并块的高度if(this.merge){    // 如果有合并列,则合并列下的列高度要加合并内容的高度。    columsHeight[0] = this.mergeColumns.indexOf(1) > -1 ? columsHeight[0] + this.mergeHeight : columsHeight[0];    columsHeight[1] = this.mergeColumns.indexOf(2) > -1 ? columsHeight[1] + this.mergeHeight : columsHeight[1];    columsHeight[2] = this.mergeColumns.indexOf(3) > -1 ? columsHeight[2] + this.mergeHeight : columsHeight[2];    columsHeight[3] = this.mergeColumns.indexOf(4) > -1 ? columsHeight[3] + this.mergeHeight : columsHeight[3];}// 获取各列最小高度let minHeight = Math.min.apply(null, columsHeight);// 通过最小高度,得到第几列高度最小this.getMinhIndex(columsHeight, minHeight).then(minIndex => {   // 渲染加载逻辑});// 获取高度最小索引函数getMinhIndex(arr, value){    return new Promise((reslove) => {        let minIndex = 0;        for(let i in arr){            if(arr[i] == value){                minIndex = i;                reslove(minIndex);            }        }    });}

如何渲染瀑布流

瀑布流常用在无限下拉加载或者加载数据量很大、且包含很多图片元素的情景,所以通常不会一次性拿到所有数据,也不会一次性将拿到的数据全部渲染到页面上,否则容易造成页面卡顿影响用户体验,所以何时进行渲染、何时继续请求数据就很关键。

何时渲染

选择渲染的区域为滚动高度 + 可视区域高度的 1.5 倍,既可以防止用户滚动到底部的时候白屏,也可以防止渲染过多影响用户体验。如果最小列的高度 - 滚动高度 < 可视区域高 * 1.5 ,则继续渲染元素,否则不再继续渲染。

何时请求数据

当已渲染的元素+可视区域可以展示的预估元素个数 > 已请求到的个数 的时候才去继续请求更多数据,防止请求浪费。如果已加载的元素个数 + 一屏可以展示的元素预估个数 > 所有请求拿到的元素个数 ,则触发下一次请求去获取更多数据。

瀑布流渲染核心思路

  • 监测滚动,判断是否符合渲染条件,如果符合条件则开始渲染。

  • 定义一个渲染索引 renderIndex,每渲染一个元素后 renderIndex + 1, 实时监测 renderIndex 的变化, 判断是否符合渲染和数据请求条件。

  • 拿到最小高度列索引后,将下一个元素插入到该列中,并触发 renderIndex + 1 进行下一轮渲染判断。

代码实现

data() {    return {        columnList1: [], // 第一列元素列表        columnList2: [],        columnList3: [],        columnList4: [],        renderIndex: -1, // 渲染第几个item        isRendering: false, // 是否正在渲染        itemList: [], // 所有元素列表        isEnd: false    };}watch: {    renderIndex(value) {        // 当前滚动条高度        const scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;        // 最小列高度 - 滚动高度 < 可视区域高的的1.5倍        if (renderMinTop - scrollTop < winHeight * 1.5) {            this.renderWaterfall();        }        // 已加载的元素个数 + 一屏可以展示元素预估个数 > 所有请求拿到的元素个数        if (loadedItemNum + canShowItemNum > this.itemList.length && !this._requesting && !this.isEnd) {            // 请求瀑布流数据            this.getData();        }    }}scroll() {    // 当前滚动条高度    const scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;    // 底部检测高度    const bottomDetectionTop = this.$refs.bottomDetection.offsetTop;    const tempLastScrollTop = lastScrollTop; // lastScrollTop:上次一滚动高度    lastScrollTop = scrollTop;    if (tempLastScrollTop === -1) {        this.renderWaterfall();    }    // 如果是向下滚动则判断是否需要继续渲染    if (scrollTop > tempLastScrollTop) {        if (bottomDetectionTop - tempLastScrollTop < winHeight * 1.5 && !this.isRendering) {            this.renderWaterfall();        }    }}renderWaterfall() {    // 如果还没有数据、所有数据已经渲染完成、正在渲染则不进行渲染计算操作    if (this.itemList.length === 0 || this.renderIndex >= this.itemList.length - 1 || this.isRendering) {        if (this.renderIndex === this.feedList.length - 1 && !this._requesting && !this.isEnd) {            this.getData();        }        return;    }    this.isRendering = true;    /***    *** 获取最小高度代码    ***/    this.getMinhIndex(columnsHeight, minHeight).then(minIndex => {        const key = `columnList${minIndex + 1}`;        let itemData = this.itemList[this.renderIndex + 1];        this[key] = this[key].concat(itemData);        this.$nextTick(() => {            this.renderIndex = this.renderIndex + 1;            this.isRendering = false;        });    });}

五、可扩展的使用场景

为了灵活使用瀑布流,在设计的时候就做好了扩展准备,通过 HTML 模板代码可以看出来,具名插槽的内容可以放在任意列,并没有限制死,所以可以扩展使用到以下各个场景。


源自:https://juejin.im/post/5e05acf0f265da33d158a1b1

声明:文章著作权归作者所有,如有侵权,请联系小编删除。

感谢 · 转发欢迎大家留言

点1个 “” 少n个bug

swift瀑布流实现_蘑菇街PC首页瀑布流实践相关推荐

  1. java中字符流 字节流_理解Java中字符流与字节流的区别

    1. 什么是流 Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列.和水流一样,Java中的流也具有一个"流动的方向",通常可 ...

  2. posix自己搭建消息队列_蘑菇街消息系统上云实践

    小编又来啦-本周要推荐给大家的是一篇跟中间件上云相关的技术文章,这里面详细的记录了,蘑菇街自研消息系统上云的全过程,也是市面上开放出来为数不多的企业自研组件上云实践.有相关需求的同学可以好好学习下. ...

  3. java zip 流压缩_关于zip:检测流是否用Java压缩的最佳方法

    找出i java.io.InputStream包含压缩数据的最佳方法是什么? 这是HTTP请求/响应的一部分吗? 介绍 由于所有答案都存在5年之久,因此我有责任写下来,这是怎么回事.我严重怀疑应该读取 ...

  4. tinyproxy王卡免流配置_大王卡tiny免流模式

    #能不能过40G自测,我还没有用到40G,手里有钉钉卡,大王卡很少用 mode=3gwap; listen_port=65080; daemon=on; worker_proc=; uid=3004; ...

  5. 京东 PC 首页 2019 改版前端总结

    距离上次首页改版,已有2年3个月零五天.相比上次改版对首页整体框架.开发流程的大刀阔斧(前两次改版总结传送门:2016版,2017版),这次的改版看起来显得有点像跳水--没什么水花.在站在巨人肩膀上的 ...

  6. 京东 PC 首页 2019 改版前端操作总结

    距离上次首页改版,已有2年3个月零五天.相比上次改版对首页整体框架.开发流程的大刀阔斧(前两次改版总结传送门:2016版,2017版),这次的改版看起来显得有点像跳水--没什么水花.在站在巨人肩膀上的 ...

  7. 「实战」蘑菇街 PC 端首页,瀑布流布局的实现原理与细节技巧

    作者:蘑菇街前端团队 链接:https://juejin.im/post/5e05acf0f265da33d158a1b1 零.介绍 这篇文章主要是介绍网站页面瀑布流布局的实现,主要包括: 瀑布流是什 ...

  8. vue瀑布流demo_面试加分企业级Vue瀑布流

    介绍 前端加分项目来了!!! 这篇文章主要介绍,企业级网站页面瀑布流布局的实现,主要包括: 瀑布流是什么 瀑布流的实现原理 瀑布流的使用场景 瀑布流的的实现有哪些问题&如何解决 可扩展使用场景 ...

  9. [19/03/30-星期六] IO技术_四大抽象类_ 字节流( 字节输入流 InputStream 、字符输出流 OutputStream )_(含字节文件缓冲流)...

    一.概念及分类 InputStream(输入流)/OutputStream(输出流)是所有字节输入输出流的父类 [注]输入流和输出流的是按程序运行所在的内存的角度划分的 字节流操作的数据单元是8的字节 ...

最新文章

  1. KafkaOffsetMonitor 安装
  2. 洛谷P1083 [NOIP2012提高组Day2T2]借教室
  3. CSDN:借助工具对【本博客访问来源】进行数据图表可视化(网友主要来自美国、新加坡、日本、英德加澳等)——记录数据来源截止日期20200718晚上22点
  4. 【小白学习C++ 教程】九、C++中字符型、字符串和转义字符
  5. mp4文件时长 c++源码_【C语言】如何使用头文件 .h 编译 C 源码!so easy!
  6. 数据集成之主数据管理(转载整理)
  7. python 判断字符串时是否是json格式方法
  8. c语言奇葩错误,6个奇葩的(hello,world)C语言版(转)
  9. SQL技巧(多行合并一列)
  10. 临界区,互斥量,信号量,事件的区别
  11. java 标识符_java标识符是什么
  12. Visual Studio Code 1.49 发布
  13. 超强的用linux命令勾搭美女
  14. apple quicktime怎么在ppt中用_只添加一个色块,你也能做出高大上的PPT排版
  15. 胡晓博:3月21日阿里云北京峰会ECS大神
  16. 揭开阿里P2P面纱:大数据是泡泡
  17. c# 傅里叶变换 频域_如何学会傅里叶变换?
  18. 《统计会犯错——如何避免数据分析中的统计陷阱》导读
  19. SpringBoot+Vue项目外卖点餐系统
  20. 【ICCV2019论文阅读】PU-GAN:点云上采样对抗网络

热门文章

  1. 前端Js框架汇总【转】
  2. SQLConnect
  3. 2016HUAS暑假集训训练题 F - 简单计算器
  4. leetcode 之Rotate List(18)
  5. RHEL7 -- 使用Chrony设置时间与时钟服务器同步
  6. MyEclipse取消验证Js的两种方法
  7. 阅读作业二-----读Lost in CatB有感 by 李栋
  8. poj题目分类(转)--方便分类做题
  9. SQL语句备查(引用)
  10. 源码部署Apache和shell脚本安装