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

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

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

这个方案简单的原因是因为仅仅使用了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);
  },
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,969评论 3 119
  • Paypal创始人、Facebook的第一位外部投资者Peter Thiel的这本经典创业书,才读完第一遍,有所收...
    Thinkpolo阅读 505评论 0 3
  • Java关键字final 在设计程序时,出于效率或者设计的原因,有时候希望某些数据是不可改变的。这时候可以使用fi...
    狮_子歌歌阅读 738评论 1 4
  • 上学的时候,对于上班的生活有过无数次的想象,我认为不会有这么难。可是上了班,我才明白,生活是多么的不容易。...
    阿毛6666666阅读 206评论 0 0
  • 不乱于心,不困于情,不畏将来,不念过往,如此,安好。 但是,放下,看空,这是何等境界,我等凡夫俗子该如何修炼才能得...
    Curtis2019阅读 433评论 0 0