美文网首页微信小程序开发微信小程序开发微信小程序
微信小程序瀑布流最好最简单的解决方案

微信小程序瀑布流最好最简单的解决方案

作者: wan7451 | 来源:发表于2018-06-08 11:06 被阅读73次

    网上能搜到的小程序瀑布流解决方案,要么代码复杂、逻辑混乱,要么实现不了业务功能,所以把我在项目中的实现方案给大家分享下。

    最简单的实现方案,不适用有分页的场景。

    这个方案简单的原因是因为仅仅使用了css的属性。
    使用column-count 属性可以指定页面显示的列数,一般瀑布流都是2列,所以可以定义class

    .list-masonry {
      column-count: 2;           //2列
      column-gap: 20rpx;       //列间距
    }
    

    界面定义也很简单

    <view class='list-masonry'>
      <block wx:for="{{goodsList}}" wx:key="{{item.id}}">
        <template is='goodsCard' data="{{data:item}}" />
      </block>
    </view>
    

    其中,goodsList为页面展示的数据,goodsCard为瀑布流的卡片,这个很容易理解。

    注意,瀑布流的卡片需要css属性 display: inline-block; 将卡片设置为 内联元素。image 组件设置缩放模式 mode="widthFix" 来保持图片宽高比。

    column-count 属性默认是以列的形式来填充数据的。比如我们有20条数据,1 ~ 10 条数据会展示在左边第一列,11 ~ 20 条数据会展示在第二列。
    如果有分页,再往数组中增加20条数据后,就会变成 1 ~ 20 条数据会在左边,21 ~ 40 条数据会展示在右边。用户体验非常差。
    由于 column-fill: balance; 填充属性无效,无法指定填充顺序为行的形式。
    所以这种实现方案只能一下加载完所有数据,不适用于分页。

    Component实现瀑布流,功能强大,滑动流畅

    通过自定义组件,用自己的思路实现瀑布流。然后在需要瀑布流的地方直接调用,方便复用。

    没有Demo!! 跟着我的步骤一步一步来,就能轻松实现。

    1. 首先创建瀑布流自定义组件文件。

    建议在项目根目录创建文件夹component,然后在该目录下创建文件夹WaterFallView,最后在WaterFallView下创建component。(鼠标右键->新建->Component)。

    微信截图_20180607103451.png
    2. 设计瀑布流的wxml。

    瀑布流的结构简单,只有左右2列。所以在设计UI的时候,布局很简单。

    <view class='fall-container'>
      <!-- 左边一列 -->
      <view class='fall-left'>
        <block wx:for="{{leftList}}" wx:key="{{item.id}}">
          <!--瀑布流内容卡片-->
          <template is='goodsCard' data="{{data:item}}" />
        </block>
      </view>
      <!--右边一列 -->
      <view class='fall-right'>
        <block wx:for="{{rightList}}" wx:key="{{item.id}}">
          <!--瀑布流内容卡片-->
          <template is='goodsCard' data="{{data:item}}" />
        </block>
      </view>
    
    </view>
    

    左右两边,一边一个View。通过这两个View 来展示瀑布流的两列。每个View对应一个数据源,由此可见,这套思路的重点是这个两个数据源的处理。每个View中的template 为瀑布流中的卡片,就不介绍了。
    超过两列的瀑布流比较少见,本篇不考虑,但可用本篇的思路来实现。

    3. css样式
    .fall-container {
      width: 100%;
      display: flex;
    }
    
    .fall-left {
      display: flex;
      flex-direction: column;
    }
    
    .fall-right {
      display: flex;
      flex-direction: column;
      margin-left: 20rpx;
    }
    
    4. 具体实现逻辑

    根据上面的 wxml 结构,这个组件的核心逻辑就是如何把要展示的数据item 放入leftList、rightList这两个数组中。

    如何分配数据item?这个简单,我们可以定义2个变量 leftHight、rightHight,来分别记录leftList、rightList数组中图片的高度(可以理解为左边View、右边View的高度,其实只是图片的高度,但已满足瀑布流的的需求)。当leftHight 大于 rightHight时,把数据放入rightList,并让rightHight叠加数据中图片的高度。当rightHight大于 leftHight 时,把数据放入leftList,并让leftHight 叠加数据中图片的高度。

    if (leftHight == rightHight) {  //第1个item放左边
      leftList.push(tmp);
      leftHight = leftHight + tmp.itemHeight;
    } else if (leftHight < rightHight) {
      leftList.push(tmp);
      leftHight = leftHight + tmp.itemHeight;
    } else {
      rightList.push(tmp);
      rightHight = rightHight + tmp.itemHeight;
    }
    

    瀑布流展示图片的时候,需要知道图片的宽高,然后根据图片的宽高比来设置 image组件的宽高。所以如果你们的数据没有宽高或宽高比,很难实现瀑布流。虽然可以通过代码获得图片宽高,但会对性能以及用户体验有很大影响,不推荐这么做。可以和后台同学商量下,看如何加上宽高数据。

    Component有自己生命周期方法,甚至可以象Page一样,当做一个单独的页面使用。可以在他的生命周期方法中获得到瀑布流的宽度,以及图片的最大高度。

    attached: function () {  //第一个生命周期方法
        wx.getSystemInfo({
          success: (res) => {
            let percentage = 750 / res.windowWidth;  //750rpx/屏幕宽度
            let margin = 20 / percentage;                    //计算瀑布流间距
            itemWidth = (res.windowWidth - margin) / 2;  //计算 瀑布流展示的宽度
            maxHeight = itemWidth / 0.8                   //计算瀑布流的最大高度,防止长图霸屏
          }
        });
      },
    

    拿到瀑布流的宽度后,就可以根据图片的宽高比,计算出 image 组件的宽高。

    let tmp = listData[i];    //单条数据
    tmp.width = parseInt(tmp.width);  //图片宽度
    tmp.height = parseInt(tmp.height); //图片高度
    tmp.itemWidth = itemWidth    //image 宽度
    let per = tmp.width / tmp.itemWidth;  //图片宽高比
    tmp.itemHeight = tmp.height / per;  //image 高度
    if (tmp.itemHeight > maxHeight) {
        tmp.itemHeight = maxHeight;   //image 高度,不超过最大高度
    }
    

    在template中,image的宽高需要声明下。单位是px,不是rpx

     <image 
      class='card-img' 
      mode='aspectFill' 
      style='width:{{data.itemWidth}}px;height:{{data.itemHeight}}px;' 
      src='{{data.img}}' 
      lazy-load>
    </image>
    
    5. 所有JS代码
    /**
     * 瀑布流组件
     */
    
    var leftList = new Array();//左侧集合
    var rightList = new Array();//右侧集合
    var leftHight = 0, rightHight = 0, itemWidth = 0, maxHeight = 0;
    
    Component({
      properties: {},
      data: {
        leftList: [],//左侧集合
        rightList: [],//右侧集合
      },
    
      attached: function () {
        wx.getSystemInfo({
          success: (res) => {
            let percentage = 750 / res.windowWidth;
            let margin = 20 / percentage;
            itemWidth = (res.windowWidth - margin) / 2;
            maxHeight = itemWidth / 0.8
          }
        });
      },
    
      methods: {
        /**
         * 填充数据
         */
        fillData: function (isPull, listData) {
          if (isPull) { //是否下拉刷新,是的话清除之前的数据
            leftList.length = 0;
            rightList.length = 0;
            leftHight = 0;
            rightHight = 0;
          }
          for (let i = 0, len = listData.length; i < len; i++) {
            let tmp = listData[i];
            tmp.width = parseInt(tmp.width);
            tmp.height = parseInt(tmp.height);
            tmp.itemWidth = itemWidth
            let per = tmp.width / tmp.itemWidth;
            tmp.itemHeight = tmp.height / per;
            if (tmp.itemHeight > maxHeight) {
              tmp.itemHeight = maxHeight;
            }
            
            if (leftHight == rightHight) {
              leftList.push(tmp);
              leftHight = leftHight + tmp.itemHeight;
            } else if (leftHight < rightHight) {
              leftList.push(tmp);
              leftHight = leftHight + tmp.itemHeight;
            } else {
              rightList.push(tmp);
              rightHight = rightHight + tmp.itemHeight;
            }
          }
    
          this.setData({
            leftList: leftList,
            rightList: rightList,
          });
        },
      }
    })
    
    
    6. 使用瀑布流

    a. 注册自定义组件
    在使用自定义组件的Page的json文件中声明要使用的组件

    {
        ....
        "usingComponents": {
            "waterFallView": "../../component/WaterFallView/WaterFallView"
         }
    }
    

    b. 在 wxml 中添加组件,并加上 id

    <waterFallView id='waterFallView'>
    </waterFallView>
    

    c. 在JS中找到组件,并调用fillData() 方法。下拉刷新时 isFull 传 true。

      fillData: function (isFull,goods){
        let view = this.selectComponent('#waterFallView');
        view.fillData(isFull, goods);
      },
    

    相关文章

      网友评论

      本文标题:微信小程序瀑布流最好最简单的解决方案

      本文链接:https://www.haomeiwen.com/subject/aazvsftx.html