小程序瀑布流

212 阅读7分钟

1、直接在单页面应用

这个是我自己写的一个很简单的瀑布流,用到的都是css,js用到的就是把数据分成两个数组,不过数据多的时候问题就会显现出来了
第二种方法,是我在码云上看见的一个项目觉得挺好就转载了一下

效果:

在这里插入图片描述

wxml代码

<view class="waterfall_main_container">
	<view class="waterfall_content">
		<!-- 瀑布流左边 -->
		<view class="left">
			<view wx:for="{{leftArr}}" wx:for-item="item" wx:key="index">
				<view class="waterfall_item">
					<image class="item-img" style="width:100%;border-radius: 12rpx 12rpx 0 0;" src="{{item.url}}"></image>
					<view url="url" class="waterfall_shopName">{{item.name}}</view>
				</view>
			</view>
		</view>
		<!-- 瀑布流右边 -->
		<view class="right">
			<view wx:for="{{rightArr}}" wx:for-item="item" wx:key="index">
				<view class="waterfall_item">
					<image class="item-img_video" src="{{item.url}}"></image>
					<view url="url" class="waterfall_shopName">{{item.name}}</view>
				</view>
			</view>
		</view>
	</view>
</view>


js代码(下面有数组去重的方法,需要用到的可以用)

// pages/test/test.js

Page({
  data: {
    list: [{
        name: '1212121212121212121212',
        heart_num: '1',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      {
        name: '565656565656565',
        heart_num: '2',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      {
        name: '3434343434343434',
        heart_num: '3',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      }, {
        name: '787878787878887878',
        heart_num: '4',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      {
        name: '9090909090909090',
        heart_num: '5',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      {
        name: 'ababababbababababa',
        heart_num: '6',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      {
        name: 'cdcdcdcdcdcdcdc',
        heart_num: '7',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      {
        name: 'efefefefefefef',
        heart_num: '8',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      {
        name: '565656565656565',
        heart_num: '2',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      {
        name: '3434343434343434',
        heart_num: '3',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      }, {
        name: '787878787878887878',
        heart_num: '4',
        title: '你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识你所不知道的红酒知识',
        url: 'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg',
        avatar: 'http://img4.imgtn.bdimg.com/it/u=349345436,3394162868&fm=26&gp=0.jpg'
      },
      
    ],
    leftArr: [],
    rightArr: []
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    const { list } = this.data
    console.log(list.length)
    let leftArr = [], rightArr = []
    list.forEach((ele,index) => {
      if (index % 2 === 0) {
        leftArr.push(ele)
      } else {
        rightArr.push(ele)
      }
    })
    // console.log("左侧数据:",leftArr)
    // console.log("右侧数据:",rightArr)
    console.log(leftArr.pop())
    this.setData({
      leftArr, rightArr
    })
  },
  //数组去重
  removeUniqueArr(arr){
    const res = new Map()
    //根据name属性进行去重
    return arr.filter((arr) => !res.has(arr.name) && res.set(arr.name, 1))
  }
})

wxss代码

/* pages/test/test.wxss */
.activity_waterfall{
  margin-top:10px;
  padding-top:5px;
  margin-left: 25rpx;
  margin-right:25rpx;
  position: relative;
}
.waterfall_content{
  clear:both;
  overflow: hidden;
  width:100%;
  display: flex;
  justify-content: space-around;
  flex-wrap: wrap;
}
.waterfall_item{
  display: inline-block;
  background:#fff;
  margin-bottom: 20rpx;
  border-radius: 12rpx;
  width:336rpx;
  min-height: 390rpx;
  max-height: 590rpx;
  position: relative;
  box-shadow: 0 4px 10px 0 rgba(0,0,0,0.10);
}
.item-img_container{
  width:336rpx;
  border-radius: 12rpx 12rpx 0 0 ;
}
.waterfall_item.item-img{
  height: 190rpx;
  max-height: 380rpx;
  border-radius: 12rpx 12rpx 0 0 ;
}
.item-img_video{
  width:336rpx;
  min-height: 190rpx;
  max-height: 380rpx;
  border-radius: 12rpx 12rpx 0 0 ;
}

2、封装成单独组件(转载)

转载自:https://blog.csdn.net/yys190418/article/details/107437199

gitee地址:https://gitee.com/yangyushu/yys-waterfallFlow

先看一下效果,gif图片不知道怎么回事,太大了,上传不了
注释写的很详细就不解释了

在这里插入图片描述

waterFallFlow组件wxml代码

<!--component/waterfallFlow/waterfallFlow.wxml-->
<view class="waterfallflow-main-container">
  <!-- 左侧部分 -->
  <view class="fallflow-left-content">
    <block wx:for="{{leftList}}" wx:key="index">
      <view bind:tap="clickDom" data-data="{{item}}">
        <image class='page_image class-image class-image-left' style='height:{{item.Height}}px' src='{{item.image}}'></image>
        <view wx:if="{{item.title}}">
          <view class='page_title class-title' style='height:{{item.waterfallGap}}px'>
            <spen class='page_title_spen'>{{item.title}}</spen>
          </view>
        </view>
        <view wx:if="{{!item.title}}" class='slot-title'>
          <slot name="slot-{{ item.id }}"/>
        </view>
      </view>
    </block>
  </view>
  <view style="width:{{middleGap}};" />
  <!-- 右侧部分 -->
  <view class="fallflow-right-content">
    <block wx:for="{{rightList}}" wx:key="index">
      <view bind:tap="clickDom" data-data="{{item}}">
        <image class='page_image class-image class-image-right' style='height:{{item.Height}}px' src='{{item.image}}'></image>
        <view wx:if="{{item.title}}">
          <view class='page_title class-title' style='height:{{item.waterfallGap}}px'>
            <spen class='page_title_spen'>{{item.title}}</spen>
          </view>
        </view>
        <view wx:if="{{!item.title}}" class='slot-title'>
          <slot name="slot-{{item.id}}"/>
        </view>
      </view>
    </block>
  </view>
</view>

waterFallFlow组件js代码

属性列表字段

noramalData:组件需要渲染的列表数据
middleGap:瀑布流中间间距
waterfallGap: 瀑布标题高度
isNoramalDataOrList: 只使用单个集合(默认为false)
cuSlotHeight1: 是否传入自定义节点高度,如果传入则不调用获取节点高度,加快运行(单位px)

自定义样式

class-image: 图片
class-image-left: 左瀑布图片
class-image-right: 右瀑布图片
class-title: 标题整体样式
class-title (spen): 改变标题文本样式

bind:clickDom=‘onClickAngleData’ // 将组件内点击方法暴露到onClickAngleData方法

Component({
  /**
   * 在组件定义时的选项中启用多slot支持
   */
  options: {
    multipleSlots: true
  },

  /**
   * 引入自定义样式
   */
  externalClasses: [
    // 图片自定义样式
    'class-image',
    'class-image-left',
    'class-image-right',
    // 标题自定义样式
    'class-title',
  ],

  /**
   * 组件的属性列表
   */
  properties: {
    // 标准数据是否为总集合
    isNoramalDataOrList:{
      type: Boolean,
      value: false
    },
    // 标准数据
    noramalData:{
      type: Array,
    },
    // 瀑布中间距 默认0
    middleGap:{
      type: String,
      value: '0'
    },
    // 自定义组件高度 单位px
    // 传入后组件不调用获取自定义组件高度方法,加载速度加快
    cuSlotHeight1:{
      type: Number,
      value: 0
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    waterfallWidth: 0, // 瀑布宽度
    leftList: [],   // 左瀑布
    rightList: [],  // 右瀑布
    leftHight: 0,   // 左瀑布高度
    rightHight: 0,  // 右瀑布高度
    isInitialize: false,  // 是否初始化
    waterfallGap: 40, // 标题高度
    isSlotHeight: false, // 是否获取自定义组件高度
    cuSlotHeight2: 0,  // 自定义组件高度
  },

  /**
   * 监听数据
   */
  observers:{
    noramalData(val){
      if(!this.data.isInitialize){ return };  // 未初始化拦截
      if(this.properties.isNoramalDataOrList){  // 判断是否仅遍历新增数据
        var arr = val.removeDuplication(this.data.leftList,this.data.rightList);
        this.getNoramalData(arr);  // 【获取瀑布数据】
      }else{
        this.getNoramalData(val);  // 【获取瀑布数据】
      }
    }
  },

  /**
   * 生命周期--组件挂载前
   */
  attached(){
    const that = this;
    that.createSelectorQuery().select('.fallflow-left-content').boundingClientRect(res => {
      that.setData({waterfallWidth: res.width});  // 获取瀑布宽度,并赋值
      that.getNoramalData(that.properties.noramalData,true); // 【获取瀑布数据】并初始化监听数据
    }).exec();
  },

  /**
   * 组件的方法列表
   */
  methods: {

    /**
     * 方法--【获取瀑布数据】 
     * @param {Boolean} type => 是否是初始化 
     */
    getNoramalData(noramalData,type = false){
      if(type){ this.data.isInitialize = true }; // 获取宽度后初始化
      console.log('【新增数据】',noramalData);
      var that = this;
      var waterfallWidth = that.data.waterfallWidth;
      noramalData.forEach((element,i) => {
        wx.getImageInfo({   // 遍历获取图片数据
          src: element.image,
          success(res){
            element.Height = parseInt(Math.round(res.height * waterfallWidth / res.width)); // 图片高度计算
            that.setList(element);  // 调用【瀑布填充】方法
          }
        })
      })
    },

    /**
     * 方法--【瀑布填充】
     * @param {Array} element => 瀑布内容 
     */
    setList(element){
      var leftH = this.data.leftHight;    // 左瀑布高度
      var rightH = this.data.rightHight;  // 右瀑布高度
      var leftData = this.data.leftList;  // 左瀑布内容
      var rightData = this.data.rightList; // 右瀑布内容
      // 判断是否有标题
      if(!element.title){
        element.waterfallGap = this.properties.waterfallGap;
      }else{
        // 是否传入了自定义组件高度
        if(this.properties.cuSlotHeight1 !== 0){
          this.getSlotHeight(this.data.isSlotHeight); // 延时严重,不使用自动获取
          element.waterfallGap = this.properties.cuSlotHeight2;
        }else{
          element.waterfallGap = this.properties.cuSlotHeight1;
        }
      }
      // 判断瀑布长度,短瀑布增加
      if (leftH == rightH || leftH < rightH) {
        leftH += element.Height + element.waterfallGap;
        leftData.push(element);
        this.setData({
          leftList: leftData,
          leftHight: leftH,
        })
      } else {
        rightH += element.Height + element.waterfallGap;
        rightData.push(element);
        this.setData({
          rightList: rightData,
          rightHight: rightH,
        })
      };
    },

    /**
     * 方法--【获取自定义组件高度】
     * 因为延时太严重了,不适用自动获取方法 
     */
    getSlotHeight(type){
      if(type){ return };
      const that = this;
      that.createSelectorQuery().select('.slot-title').boundingClientRect((res) => {
        if(!res){ return };
        that.setData({
          isSlotHeight: true,
          cuSlotHeight2: Math.round(res.height),
        })
      }).exec();
    },

    /**
     * 方法--【点击元素】
     * @param {List} data => 点击元素 
     */
    clickDom(data){
      this.triggerEvent("clickDom", data.currentTarget.dataset.data);
    },
  },
})

/**
 * 原型方法--数组去重
 */
Array.prototype.removeDuplication = function(arr1,arr2){
  var arr0 = [],ifHas = false;
  this.forEach((ret) => {
    ifHas = false;
    arr1.forEach((ele1) => {
      if(ele1.id == ret.id) ifHas = true;
    })
    arr2.forEach((ele2) => {
      if(ele2.id == ret.id) ifHas = true;
    })
    if(!ifHas) arr0.push(ret);
  })
  return arr0;
}

waterFallFlow组件css代码

.waterfallflow-main-container{
  width: 100%;
  display: flex;
  flex-direction: row;
  overflow: hidden;
  border: 1px solid rebeccapurple;
}

.fallflow-left-content, .fallflow-right-content{
  width: 50%;
}

.page_right{
  width: 50%;
}

.page_image{
  width: 100%;
}

.page_title{
  width: 100%;
  border-radius: 0 0 10rpx 10rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-around;

}
.page_title_spen{
  color: rgb(116, 187, 187);
  font-size: 13px;
  font-weight: 700;
}

在其他页面使用:

<!-- 瀑布流插件 -->
<view class="waterfall-main-contaienr">
  <waterFallFlow
    noramalData="{{allData}}"
    middleGap="{{ middleGap }}"
    cuSlotHeight1="60"
    bind:clickDom="60"
  >
    <view wx:for="{{addMoreInfo}}" wx:key="index" wx:for-item="item" wx:for-index='index' slot="slot-{{item.id}}">
      <view class="page-slot-contaienr">
        <spen>ID:{{item.id}}</spen>
        <spen class="custom-title">自定义标题-{{index}}</spen>
      </view>
    </view>
  </waterFallFlow>
</view>

<view>
  <button bindtap="addPageInfo">增加数据</button>
</view>

js代码

const app = getApp()

Page({
  data: {
  	//新增的数据:分页后获得的数据,通过数组的concat方法拼起来的
    addMoreInfo:[],
   	//后台返回的全部数据,一般都是返回第一页的n条数据
    allData:[],
    analogData:[],
    middleGap:'5px'
  },
  onLoad: function () {
    // 获取模拟数据
    var arr1 = [],arr2 = []
    moNiData.forEach((ele,i) => {
      ele.id = i + '-' + new Date().getTime() // 模拟获取随机ID
      arr1.push(ele)
      arr2.push(ele)
    });
    this.setData({
      addMoreInfo: arr1,
      allData: arr2
    })
    
  },

  onReachBottom(){
    // console.log('小程序触底,触发加载数据')
    this.addPageInfo()
  },

  // 获取点击事件
  test(data){
    console.log(data)
  },
  // 增加数据
  addPageInfo(){
    var arr = this.data.addMoreInfo // 总数据集合(节点ID使用)
    var arr2 = []    // 新增数据集合(避免图片重复加载,导致速度过慢)
    var data = [
      {id:'0',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
      {id:'1',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
      {id:'2',image:'http://dashus.oss-cn-shenzhen.aliyuncs.com/DefaultImage/Game/20190313090409/完美9.png'},
      {id:'3',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
      {id:'4',image:'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg'},
      {id:'5',image:'http://img3.imgtn.bdimg.com/it/u=1417732605,3777474040&fm=26&gp=0.jpg'},
      {id:'6',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
    ];
    data.forEach((ele,i) => {
      ele.id = i + '-' + new Date().getTime() // 模拟获取随机ID
      arr.push(ele)
      arr2.push(ele)
    });
    this.setData({
      addMoreInfo: arr,
      allData: arr2
    })
    // console.log('新增的数据:',this.data.allData)
    // console.log('总数据:',this.data.addMoreInfo)
  },
})

var moNiData = [
  {id:'0',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
  {id:'1',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
  {id:'2',image:'http://dashus.oss-cn-shenzhen.aliyuncs.com/DefaultImage/Game/20190313090409/完美9.png'},
  {id:'3',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
  {id:'4',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
  {id:'5',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
  {id:'6',image:'http://img2.imgtn.bdimg.com/it/u=1561660534,130168102&fm=26&gp=0.jpg'},
];