小程序瀑布流组件实现翻页与图片懒加载


Posted in Javascript onMay 19, 2020

电商小程序中,用到瀑布流的地方非常多,每次都写一个瀑布流,重复一次逻辑,作为程序员,肯定是非常不愿意的。
瀑布流的形式都是大同小异,不同的是瀑布流中每个模块的内容,随业务而变化。
所以,我们把瀑布流框架抽象成组件,瀑布流的内容由业务确定。这样即可实现组件化和自定义的最大平衡,微信小程序组件源码。

首先,我们来看一下瀑布流组件在实际项目中的实际效果。

1 实际效果

瀑布流组件实际效果如下图所示,左侧为用户交互效果,右侧为图片懒加载实际效果。

小程序瀑布流组件实现翻页与图片懒加载

2 什么是瀑布流?

瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,waterfall-item宽度固定,高度不定,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。如下图所示:

小程序瀑布流组件实现翻页与图片懒加载

3 实现功能

该瀑布流组件实现了以下几个功能:

  • 支持图片懒加载
  • 支持上拉数据翻页
  • 支持自定义样式
  • 支持瀑布流Item间隔底层自动计算
  • 原生组件模式:即类swiper和swiper-item 组件用法
  • 组件与数据完全解耦

4 实现原理

4.1 waterfall 和waterfall-item实现原理

第一步:在 waterfall-layout 目录下创建 waterfallwaterfall-item 组件,目录结构如下:

.
├── query-node.js
├── waterfall-item.js
├── waterfall-item.json
├── waterfall-item.wxml
├── waterfall-item.wxss
├── waterfall.js
├── waterfall.json
├── waterfall.wxml
└── waterfall.wxss

第二步:分别在waterfall.jswaterfall-item.jsrelations选项中指定组件父、子级关系:

// waterfall.js
Component({
 // ... other code
 relations: {
 './waterfall-item': {
  type: 'child',
 },
 // ... other code
 }
})
// waterfall-item.js
Component({
 // ... other code
 relations: {
 '././waterfall': {
  type: 'parent',
 },
 // ... other code
 }
})

指定彼此的父、子组件的关系后,即可通过 this.getRelationNodes 原生 API,就能访问彼此实例对象及其属性和方法。

第三步:实现waterfall.wxmlwaterfall-item.wxml代码:
waterfall.wxml代码实现非常简单,只有5行代码:

<view class="waterfall custom-class">
 <view class="waterfall-inner">
 <slot ></slot>
 </view>
</view>

同样,waterfall-item.wxml代码实现也非常简单,只有5行代码:

<view
 class="waterfall-item custom-class"
 style="{{position}}:0;top:{{(top >= 0 ? top + 'px' : 0 + 'rpx')}};"
>
 <slot ></slot>
</view>

不知道slot用法的童鞋,请参考微信小程序自定义组件模板和样式文档。

4.2 瀑布流原理

其实,不管是微信小程序、web、还是原生APP,瀑布流的实现原理都是一样的。都可以绝对定位和位置计算来实现。
瀑布流的大体过程如下图所示:

第一步:数据通过this.setData从逻辑层传输到视图层,进行第一渲染,由于每个waterfall-itemtop:0;position:left;,所以都重叠了在一起。

第二步:通过节点查询API获取每个waterfall-item元素信息,并且计算出正确的topposition值。

第三步:setData每个waterfall-itemtopposition,实现重排。

小程序瀑布流组件实现翻页与图片懒加载

具体逻辑实现如下:

首先,我们来实现一个节点查询API querySelector,之后会用到:

// query-node.js
/**
 * 获取当前页面中,选择器为 selector 的第一个node节点
 * @param {String} selector 符合微信小程序规范的选择器
 * @param {Object} context 调用环境,普通页面中为wx,自定义组件中为this;默认值为wx.
 * @return {Array} 返回一个数组,第一个元素为 node 节点
 */
export const querySelector = function (selector, context = wx) {
 return new Promise((resolve, reject) => {
 context.createSelectorQuery()
 .select(selector)
 .boundingClientRect((res) => {
  if (res) {
  resolve(res);
  } else {
  reject(`不存在选择器为 ${selector} 的节点`);
  }
 })
 .exec();
 })
};

接着,看一下组件waterfallwaterfall-item在实际项目中的用法:

<waterfall
  loading="{{loadMorePending}}"
  isAllLoaded="{{isAllLoaded}}"
 >
  <block wx:for="{{data.sections}}" wx:key="id" wx:for-item="product">
  <waterfall-item
   index="{{index}}"
   custom-class="flow-item-wrapper"
  >
   <view class="product-item">
   业务代码
   </view>
  </waterfall-item>
  </block>
 </waterfall>

当第一个waterfall-item组件,在视图层布局完成后会执行ready生命周期钩子。
ready 生命周期钩子中,我们需要做两件事:

  • 获取父组件waterfall的实例对象,并挂载在waterfall-item组件的 this实例对象上。因为之后我们需要在waterfall-item组件中修改waterfall上的数据。
  • 获取waterfall-item组件的高度,计算waterfall-item组件的位置信息topposition
// waterfall-item.js
import { querySelector } from './query-node';
Component({
 // ... other code
 lifetimes: {
 ready() {
  const [waterfall] = this.getRelationNodes('./waterfall');
  this.parent = waterfall;
  this.setWaterfallItemPosition();
 },
 }
 methods:{
 async setWaterfallItemPosition() {
  querySelector('.waterfall-item', this)
  .then(async (node) => {
   const { top, position } = await this.parent.getWaterfallItemPostionInfo(node);
   this.setData({
   top,
   position
   })
  })
 }, 
 }
 // ... other code
})

setWaterfallItemPosition方法中,我们调用了父组件上的方法this.parent.getWaterfallItemPostionInfo,获取当前waterfall-item组件的topposition信息。并把已经渲染好的waterfall-item组件的累计高度缓存在waterfallleftHeightsrightHeights属性上,用于计算下一个waterfall-item组件位置,主要逻辑如下:

// waterfall.js
const POSITION_LEFT = 'left';
const POSITION_RIGHT = 'right';

Component({
 // ... other code
 /**
 * 组件的方法列表
 */
 methods: {
 lifetimes: {
  ready() {
  this.initParams();
  }
  },
 initParams() {
  this.leftHeights = 0;
  this.rightHeights = 0;
 },
 /**
  * 设置 waterfall-item 的高度值
  * @param {Object} node waterfall-item 组件位置尺寸数据
  */
 async getWaterfallItemPostionInfo(node) {
  let top = 0;
  let position = POSITION_LEFT;
  const { height } = node;
  const { itemGap } = this;
  if (this.leftHeights <= this.rightHeights) {
  top = this.leftHeights;
  if(this.leftHeights === 0) {
   this.leftHeights += height;
  } else {
   top += itemGap;
   this.leftHeights += (height + itemGap);
  }
  } else {
  position = POSITION_RIGHT;
  top = this.rightHeights;
  if(this.rightHeights === 0) {
   this.rightHeights += height;
  } else {
   top += itemGap;
   this.rightHeights += (height + itemGap);
  }
  }
  return {
  top,
  position,
  }
 }
 // ... other code
 }
})

当所有的waterfall-item重排结束后,瀑布流渲染完成。

4.3 图片懒加载原理

微信小程序中,<image>标签本身是支持懒加载的,当lazy-load={{true}},且在即将进入一定范围(上下三屏)时才开始加载。

也就是说,当lazy-load={{true}}<image>标签初次渲染在视口上下三屏之外时,是不会请求图片资源的,当<image>即将进入三屏之内时,才会加载。

在4.2小节的图3中,<waterfall-item>的初始化位置设置成了top:0;position:left;,所以,都在视口中。如果将top的值成三屏之外的数值,例如,400vh或者更大,则<waterfall-item>重排之后,任然在三屏之外的图片即会自动懒加载。

<view
 class="waterfall-item custom-class"
 style="{{position}}:0;top:{{(top >= 0 ? top + 'px' : itemCount * 100 + 'vh')}};"
>
 <slot ></slot>
</view>
Component({
 // waterfall-item.js
 // ... other code
 lifetimes: {
 ready() {
  const { itemCount } = this.data;
  const [waterfall] = this.getRelationNodes('./waterfall');
  waterfall.childCount += 1;
  this.parent = waterfall;
  this.setData({
  itemCount: itemCount + waterfall.childCount,
  })
 },
 },
 // ... other code
})

4.4 数据翻页

因为实现了wx:for <waterfall-item>功能,和<swiper-item>组件一样,因此翻页逻辑完全由用户自己定制,<waterfall><waterfall-item>只给你提供翻页的功能,组件就可以和瀑布流数据结构完全解耦。

4.5 瀑布流Item间隔底层自动计算

将列和行中,两个<waterfall-item>组件之间的距离定义为itemGap,则:

itemGap = waterfall宽度 - (waterfall-item宽度 * 2)

<waterfall>ready钩子中,可以获取到<waterfall>组件的宽度;同理,在<waterfall-item>ready钩子中,可以获取到<waterfall-item>组件的宽度。
在调用getWaterfallItemPostionInfo之前,获取到itemGap的值即可。这样,在计算<waterfall-item>top值时,除了第一行的<waterfall-item>top值等于0之外,其他所有<waterfall-item>top值等于:

// this.leftHeights += height + itemGap;
// or 
// this.rightHeights += height + itemGap;

具体代码实现请查看源码

5 总结

通过瀑布流框架抽象,使<waterfall><waterfall-item>接近原生组件使用体验,同时使组件与数据完全解耦。通过巧妙的初始化位置top设置,使瀑布流具图片有懒加载的功能。

到此这篇关于小程序瀑布流组件实现翻页与图片懒加载的文章就介绍到这了,更多相关小程序瀑布流内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
js写一个字符串转成驼峰的实例
Jun 21 Javascript
jquery购物车实时结算特效实现思路
Sep 23 Javascript
简单易用的倒计时js代码
Aug 04 Javascript
javascript实现限制上传文件大小
Feb 06 Javascript
JS的框架Polymer中的dom-if和is属性使用说明
Jul 29 Javascript
防止Node.js中错误导致进程阻塞的办法
Aug 11 Javascript
jquery文字填写自动高度的实现方法
Nov 07 Javascript
微信小程序使用setData修改数组中单个对象的方法分析
Dec 30 Javascript
Weex开发之地图篇的具体使用
Oct 16 Javascript
javascript实现鼠标点击生成文字特效
Dec 24 Javascript
vuex管理状态仓库使用详解
Jul 29 Javascript
通过滑动翻页效果实现和移动端click事件问题
Jan 26 Javascript
jQuery 淡入/淡出效果函数用法分析
May 19 #jQuery
微信小程序 wx:for 与 wx:for-items 与 wx:key的正确用法
May 19 #Javascript
微信小程序使用GoEasy实现websocket实时通讯
May 19 #Javascript
jQuery 动画与停止动画效果实例详解
May 19 #jQuery
jQuery 选择方法及$(this)用法实例分析
May 19 #jQuery
通过js随机函数Math.random实现乱序
May 19 #Javascript
javascript实现获取中文汉字拼音首字母
May 19 #Javascript
You might like
虹吸壶煮咖啡26个注意事项
2021/03/03 冲泡冲煮
PHP中使用hidef扩展代替define提高性能
2015/04/09 PHP
PHP5.2下preg_replace函数的问题
2015/05/08 PHP
PHP+redis实现添加处理投票的方法
2015/11/14 PHP
ThinkPHP3.2框架自定义配置和加载用法示例
2018/06/14 PHP
bootstrap data与jquery .data
2014/07/07 Javascript
jQuery实现鼠标经过事件的延时处理效果
2020/08/20 Javascript
Bootstrap轮播加上css3动画,炫酷到底!
2015/12/22 Javascript
JS和jQuery使用submit方法无法提交表单的原因分析及解决办法
2016/05/17 Javascript
AngularJs 最新验证手机号码的实例,成功测试通过
2017/11/26 Javascript
React组件内事件传参实现tab切换的示例代码
2018/07/04 Javascript
小程序卡片切换效果组件wxCardSwiper的实现
2020/02/13 Javascript
vue实现PC端分辨率适配操作
2020/08/03 Javascript
[01:37]TI4西雅图DOTA2前线报道 VG拿下首胜教练357给出获胜秘诀
2014/07/10 DOTA
[04:27]2014DOTA2国际邀请赛 NAVI战队官方纪录片
2014/07/21 DOTA
Python程序中用csv模块来操作csv文件的基本使用教程
2016/03/03 Python
Python中shapefile转换geojson的示例
2019/01/03 Python
python实现QQ空间自动点赞功能
2019/04/09 Python
pandas对dataFrame中某一个列的数据进行处理的方法
2019/07/08 Python
用Python实现最速下降法求极值的方法
2019/07/10 Python
python 进程间数据共享multiProcess.Manger实现解析
2019/09/23 Python
python爬虫 Pyppeteer使用方法解析
2019/09/28 Python
在Pytorch中使用Mask R-CNN进行实例分割操作
2020/06/24 Python
解决margin 外边距合并问题
2019/07/03 HTML / CSS
红色康乃馨酒店:Red Carnation Hotels
2017/06/22 全球购物
TripAdvisor瑞典:全球领先的旅游网站
2017/12/11 全球购物
Java模拟试题
2014/11/10 面试题
3.15国际消费者权益日主题活动活动总结
2014/03/16 职场文书
求职者怎样写自荐信
2014/04/13 职场文书
爱国演讲稿500字
2014/05/04 职场文书
2015年纪念“卢沟桥事变”78周年活动方案
2015/05/06 职场文书
活动总结模板大全
2015/05/11 职场文书
傲慢与偏见电影观后感
2015/06/10 职场文书
vue中data改变后让视图同步更新的方法
2021/03/29 Vue.js
Python中递归以及递归遍历目录详解
2021/10/24 Python
Python Pandas读取Excel日期数据的异常处理方法
2022/02/28 Python