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

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

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

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设置,使瀑布流具图片有懒加载的功能。

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

(0)

相关推荐

  • 微信小程序通过js实现瀑布流布局详解

    前言 瀑布流 - 小程序中数据列表或者图片展示难免高度不一,而我们一般固定宽度,让其高度自适应,并无缝对接.如下图: 瀑布流的两种做法: css: 在父元素上使用column-count: 2也可以做到两列排版.但column-count: 2是左右两列排版,如果碰巧出现左边累计高度过多大于右边累计高度的话,右边会出现大片空白区域.点击前往 js(推荐): 判断左右两边的累计高度,那边的高度小,便往那边添加(需要动态获取节点元素的高度进行计算).下面用的就是此种方法: 准备工作 我们需要用到 a

  • 微信小程序实现瀑布流布局与无限加载的方法详解

    前言 瀑布流布局是一种比较流行的页面布局方式,最典型的就是Pinterest.com,每个卡片的高度不都一样,形成一种参差不齐的美感. 在HTML5中,我们可以找到很多基于jQuery之类实现的瀑布流布局插件,轻松做出这样的布局形式.在微信小程序中,我们也可以做出这样的效果,不过由于小程序框架的一些特性,在实现思路上还是有一些差别的. 今天我们就来看一下如何在小程序中去实现这种瀑布流布局: 小程序瀑布流布局 我们要实现的是一个固定2列的布局,然后将图片数据动态加载进这两列中(而加载进来的图片,会

  • 小程序简单两栏瀑布流效果的实现

    瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式.视觉表现为参差不齐的多栏布局,即多行等宽元素排列,后面的元素依次添加到其后,等宽不等高,根据图片原比例缩放直至宽度达到我们的要求,依次放入到高度最低的那一栏. 先上代码:https://developers.weixin.qq.com/s/Fgm5s1mz7Wdm 所谓简单,是指只考虑图片,图片之外的其他元素高度固定,不在考虑范围内. 说一下基本的实现思路: 1.加载列表数据 2.在一个隐藏的view中加载图片,通过image组件的bin

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

    电商小程序中,用到瀑布流的地方非常多,每次都写一个瀑布流,重复一次逻辑,作为程序员,肯定是非常不愿意的. 瀑布流的形式都是大同小异,不同的是瀑布流中每个模块的内容,随业务而变化. 所以,我们把瀑布流框架抽象成组件,瀑布流的内容由业务确定.这样即可实现组件化和自定义的最大平衡,微信小程序组件源码. 首先,我们来看一下瀑布流组件在实际项目中的实际效果. 1 实际效果 瀑布流组件实际效果如下图所示,左侧为用户交互效果,右侧为图片懒加载实际效果. 2 什么是瀑布流? 瀑布流,又称瀑布流式布局.是比较流行

  • javascript瀑布流式图片懒加载实例解析与优化

    之前写过一版图片"懒加载"的文章,刚好周末在整理文件的时候,大概又看了一遍之前写的代码发现有很多可以优化的地方. 这篇文章主要就是结合上篇<javascript瀑布流式图片懒加载实例>再来看看图片"懒加载"的一些知识. 图片"懒加载"的主旨: 按照需要加载图片,也就是说需要显示的时候再加载图片显示,减少一次性加载的网络带宽开销. 先来看一段代码: var conf = { 'loadfirst': true, 'loadimg': t

  • 微信小程序实现图片懒加载的示例代码

    本文主要介绍微信小程序的模拟图片懒加载,实现的原理是通过页面预加载图片(默认图),加载完成后再显示出来原图,而非真正意义上的懒加载(跟web的懒加载还有很大的差距),只是借此提高用户体验度. 多图片懒加载 1.xml页面 <block wx:for="{{list}}" wx:key=""> <image class='relative width-100 mgb-20 fade_in' src='{{item.cover_url}}' mode=

  • MUI 解决动态列表页图片懒加载再次加载不成功的bug问题

    在项目开发中遇到这样的功能,要求实现列表页动态加载功能,在实现过程中遇到一些小小插曲,下面小编给大家详细说明下解决方法: 首次加载时图片可以获取成功,再次加载失败,通过chrome调试发现img 的 data-lazyload 属性没改变 调试的时候发现了bug $.fn.imageLazyload = function(options) { var lazyloadApis = []; this.each(function() { var self = this; var lazyloadAp

  • 详解性能更优越的小程序图片懒加载方式

    意义 懒加载或者可以说是延迟加载,针对非首屏或者用户"看不到"的地方延迟加载,有利于页面首屏加载速度快.节约了流量,用户体验好 实现方式 传统H5的懒加载方式都是通过监听页面的scroll事件来实现的,结合viewport的高度来判断. 小程序也类似,通过监听页面onPageScroll事件获取当前滚动的数据,结合getSystemInfo获取设备信息来判断.由于scroll事件密集发生,计算量很大,经常会造成FPS降低.页面卡顿等问题. 这里说的是通过另外一种方式来实现 create

  • jQuery 瀑布流 绝对定位布局(二)(延迟AJAX加载图片)

    瀑布流-绝对定位布局,与浮动布局的区别在于 1.布局不一样: 绝对定位:一个UL里面放置所有的绝对定位的LI: 浮动布局:多个(一般三四个)UL分布放置LI: 2.AJAX不一样 绝对定位:只需要将请求来的JSON数据(当然可以是别的格式的数据),插入到UL就可以了.然后再对这个新插入的LI进行TOP和LEFT设置: 浮动布置:是将请求来的JSON数据(当然可以是别的格式的数据),分别插入到对应的UL当中,因为有绝对定位,所以不用对LI设置位置.会自动向下排列: 一.功能分析: 1.定位每一个L

  • javascript瀑布流式图片懒加载实例

    最近项目使用到了"懒加载",现在更新一般,因为平时主要使移动端的开发所以库文件使用的是zepto.js .当然也可以和jQuery 通用. 代码如下: /** * Created by zhiqiang on 2015/10/14. * hpuhouzhiqiang@gmail.com * 图片的懒加载 **/ function loadImgLazy(node) { var lazyNode = $('[node-type=imglazy]', node), mobileHeight

  • jQuery 瀑布流 浮动布局(一)(延迟AJAX加载图片)

    浮动布局:即HTML结构的列,是用浮动方式. 一.功能分析: 1.判断图片是否进入可视区域: 2.用AJAX请求服务器数据: 3.将数据播入到相应的列队: 二.实现方法: 给window的scroll事件l绑定一个处理函数:做如下工作: 1.如何判断最后一行的图片,是否进入了可视区域? 如果:最后一行的某个图片距离浏览器可视区域顶部的距离值 小于 (可视区域的高度+滚动条滑动的距离值): 那么:就可以判定这个图片进入了浏览器的可视区域: 2.如何用AJAX请求服务器数据; $.getJSON()

  • vue组件代码分块和懒加载讲解

    目录 懒加载组件 问题与解决方案 加载中组件 错误处理组件 preload 和 prefetch 总结 前端开发中,随着业务和页面增加,以组件为基本单位的结构下,组件数量会增长极快,为了优化我们会很显然地想要进行一些工作: 代码分块 懒加载非必要资源文件 非必要资源,指的首次渲染出某页面所不必要的资源,如因为用户操作才出现的图片.弹窗等. 代码分块和懒加载在页面层面具有极大的优化作用,用户很可能只是浏览dashboard页面,可能根本就不会去看详情等页面,那我们就不必将详情页面的代码和dashb

  • 基于angular6.0实现的一个组件懒加载功能示例

    我们常常会遇到这样一个问题,当我们使用一个第三方控件库的时候,我们只用到了其中 1 个或某几个组件,会连带一大堆无用的东西,造成体积臃肿不堪.又或者首页用到的组件较多,首页加载速度缓慢,这个时候,我们或许需要加载用户可视范围内用到的组件,随着用户的浏览下拉,我们再去加载这些组件,渐进式加载,渐进式体验,这个时候你或许就用到了本工具所实现的功能.或者一个页面的某些不重要区域,比如第三方广告又或者不重要的元素,可以采用懒加载懒渲染,降低用户首屏等待时间.一切都在用户不知不觉中进行.大大增加用户体验,

随机推荐