微信小程序瀑布流和虚构列表

尽管本篇是写的微信小程序的案例,然而也可用于H5,思路是想通的,只是有些api的差别,最初会贴代码片段

虚构列表

  • 个别在做长列表优化时,特地是面试时,虚构列表就是个高频词。这个名词听起来很高级,其实原理很简略
  • 虚构列表就是将须要渲染的数组数据革新成二维数组,而后通过监听DOM节点在适合的中央切换为占位元素,达到长列表有限滚动时缩小DOM渲染的优化
  • JS

    /** * 解决占位元素,就是在获取新的数据后 * 通过SelectQuery获取以后数据的理论高度,而后把这个高度设置到占位元素上 */getCurrentItemHeight() {  const query = this.createSelectorQuery();  const { virtualId } = this.data  query.select(`#${virtualId}`).boundingClientRect()  query.exec((res) => {    this.setData({      height: res[0].height    }, this.observePage())  })}/** * 监听元素与页面的相交 * 能够抉择指定元素为参照区域,也能够抉择页面为参照元素,只是API不同 * @doc https://developers.weixin.qq.com/miniprogram/dev/api/wxml/IntersectionObserver.html */observePage() {  const { virtualId, observeDistance, wrapId } = this.data  let IntersectionObserver = wx.createIntersectionObserver(this);  (wrapId ? IntersectionObserver.relativeTo(`#${wrapId}`) : IntersectionObserver.relativeToViewport({ top: observeDistance, bottom: observeDistance }))  .observe(`#${virtualId}`, ({ intersectionRatio }) => {    this.setData({      isShow: intersectionRatio > 0,    })  })}
  • WXML

    <view id="{{virtualId}}">  <block wx:if="{{isShow}}">    <slot></slot>  </block>  <view wx:else style="height: {{ height }}px"></view></view>

瀑布流

  • 瀑布流,又称瀑布流式布局。视觉体现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会一直加载数据块并附加至以后尾部
  • 瀑布流有多种写法,有通过纯CSS实现的,也有借助JS实现的,办法很多,然而我为了接下来能与虚构列表相结合,就采纳JS的写法,就是通过列数把每一列分为一个独自的子元素,而后会记录每一列的高度,通过判断哪一列高度最小,而后将数据push到高度最小的那一列

    /** * 获取列表数据 * @describe 瀑布流解决,哪列高度小,就往哪列push新数据 */getList() {    let { listQuery: { pageIndex, pageSize }, columns, columnsHeight } = this.data;    for (let i = 0; i < pageSize; i++) {        const height = Math.floor(Math.random() * 100)        const item = {          height: height < 150 ? height + 150 : height,        };        const position = this.computePosition(columnsHeight)        columns[position].push(item)        columnsHeight += item.height    }    // 在html中双重遍历columns,而后通过flex:1均匀分布    this.setData({      columns,    })    this.data.columnsHeight = columnsHeight}/** * 获取高度最小列下标 */computePosition(heights) {const min = Math.min(...heights);return heights.findIndex((item) => item === min)}

瀑布流联合虚构列表

  • 让瀑布流有虚构滚动的能力,思路很简略,就是计算每列的偏移量,因为瀑布流被咱们分成了二维数组,所以每块子元素之间就会因为列高度的不统一产生空白区域,所以须要计算这个空白区域的大小,而后通过margin-top挪动列元素达到视觉上的瀑布流连接成果
getList() {  let { listQuery: { pageIndex }, column, columnsHeights } = this.data;  const columns = [];  // 上一组的高度数据,用于计算偏移值  const lastHeights = [...columnsHeights];  // 获取数据  const list = this.getListData();  // 初始化以后屏数据  for (let i = 0; i < column; i++ ) {    columns.push([]);  }  // 遍历新数据,调配至各列  for (let i = 0; i < list.length; i++) {    const position = this.computePosition(columnsHeights);    columns[position].push(list[i]);    columnsHeights[position] += Number(list[i].height);  }  this.setData({    [`listData[${pageIndex}]`]: {      columns,      columnOffset: this.computeOffset(lastHeights),    }  });  this.data.listQuery.pageIndex = pageIndex + 1;  this.data.columnsHeights = columnsHeights;},/** * 获取列表数据 */getListData() {  const result = []  for (let i = 0; i < this.data.listQuery.pageSize; i++) {    const height = Math.floor(Math.random() * 300);    const item = {      height: height < 150 ? height + 150 : height,      color: this.randomRgbColor(),    };    result.push(item);  }  return result;},/** * 随机生成RGB色彩 */randomRgbColor() {  var r = Math.floor(Math.random() * 256); //随机生成256以内r值  var g = Math.floor(Math.random() * 256); //随机生成256以内g值  var b = Math.floor(Math.random() * 256); //随机生成256以内b值  return `rgb(${r},${g},${b})`; //返回rgb(r,g,b)格局色彩},/** * 获取最小高度列下标 */computePosition(heights) {  const min = Math.min(...heights);  return heights.findIndex((item) => item === min);},/** * 计算偏移量 */computeOffset(heights) {  const max = Math.max(...heights);  return heights.map((item) => max - item);},onScrollLower() {  this.getList();}
  • WXML

    <view><scroll-view class="virtualScrollView" eventhandle scroll-y bindscrolltolower="onScrollLower">  <block wx:for="{{ listData }}" wx:key="screenIndex" wx:for-index="screenIndex" wx:for-item="screenItem">    <VirtualItem virtualId="virtual_{{screenIndex}}">      <view class="virtualScreen">        <block wx:for="{{ screenItem.columns }}" wx:key="columnIndex" wx:for-index="columnIndex" wx:for-item="column" >          <view style="margin-top: -{{screenItem.columnOffset[columnIndex]}}px;" class="virtualColumn">            <view wx:for="{{column}}" style="height: {{ item.height }}px; background-color: {{ item.color }};" wx:key="index" wx:for-item="item" wx:for-index="index">              screen: {{ screenIndex }}, column: {{ columnIndex }}            </view>          </view>        </block>      </view>    </VirtualItem>  </block></scroll-view></view>

总结

  • 代码片段:https://developers.weixin.qq....