# Waterfall 瀑布流

这是一个瀑布流形式的组件,内容支持多列,结合懒加载组件效果更佳。
相较于某些只是奇偶数左右分别,或者没有利用vue作用域插槽的做法,uview-plus的瀑布流实现了真正的 组件化,搭配LazyLoad 懒加载loadMore 加载更多组件,让您开箱即用,眼前一亮。

提示

由于右侧的演示是通过iframe标签引入的,缺少了手机端运行的相关API,或者因为演示区域太小,或者电脑分别率不够高 ,导致演示可能会有问题,手机端有不会这些问题,请在右上角的"演示"中用手机扫码查看对应的效果。

注意

  1. 在微信小程序中,需要hx2.8.11才支持在懒加载中结合其他组件
  2. 由于hx的问题,支付宝小程序需要hx2.8.2版本及以上才支持本组件
  3. 3.4.77版本开始瀑布流组件升级改造,目前仍然兼容老的用法,建议逐步迁移至新用法。

# 平台差异说明

App H5 微信小程序 支付宝小程序 百度小程序 头条小程序 QQ小程序

# 基本使用

本组件利用了vue的作用域插槽(详见vue文档 (opens new window))特性, 将传入waterfall内部的数据,通过slot(作用域插槽)让用户能在父组件中引用和配置对应的数据,这样做的 原因是可以让用户自定义列表item的结构和样式,达到真正的组件化。

需要注意的是,组件内部导出的数据,需要使用template元素接收,同时通过v-slot指定列插槽,如 v-slot:column="{colList, colIndex}",这里的colList是当前列的数据列表),见如下完整示例:

# 核心代码

<up-waterfall v-model="flowList" columns="2">
	<template v-slot:column="{colList, colIndex}">
		<view v-for="(item, index) in colList" :key="index">
			<!-- 这里编写您的内容,item为您传递给v-model的数组元素 -->
		</view>
	</template>
</up-waterfall>

# 移除或清空数据

移除或者清空,都需要通过ref调用组件内部的方法。

  1. 移除数据

组件内部方法名为remove,需要传入"id"值,这个"id"键值的名称配置参数为idKey(默认id),如下:

假设您的数据为:

let arr = [
	{idx: 1, name: 'lisa'},
	{idx: 2, name: 'mary'}
]

那么您应该配置idKeyidx

  1. 清空数据

通过ref手动调用组件内部的clear方法,可以清空左右两列的数据。

说明:具体实现方法,请见下方的示例代码

# 完整应用示例

<template>
	<view class="wrap">
		<up-button @click="clear">清空列表</up-button>
		<up-waterfall v-model="flowList" ref="uWaterfallRef" columns="2">
			<template v-slot:column="{colList, colIndex}">
				<view class="demo-warter" v-for="(item, index) in colList" :key="index">
					<!-- 警告:微信小程序中需要hx2.8.11版本才支持在template中结合其他组件,比如下方的lazy-load组件 -->
					<up-lazy-load threshold="-450" border-radius="10" :image="item.image" :index="index"></up-lazy-load>
					<view class="demo-title">
						{{item.title}}
					</view>
					<view class="demo-price">
						{{item.price}}元
					</view>
					<view class="demo-tag">
						<view class="demo-tag-owner">
							自营
						</view>
						<view class="demo-tag-text">
							放心购
						</view>
					</view>
					<view class="demo-shop">
						{{item.shop}}
					</view>
					<up-icon name="close-circle-fill" color="#fa3534" size="34" class="u-close" @click="remove(item.id)"></up-icon>
				</view>
			</template>
		</up-waterfall>
		<up-loadmore bg-color="rgb(240, 240, 240)" :status="loadStatus" @loadmore="addRandomData"></up-loadmore>
	</view>
</template>
<style>
	/* page不能写带scope的style标签中,否则无效 */
	page {
		background-color: rgb(240, 240, 240);
	}
</style>

<style lang="scss" scoped>
	.demo-warter {
		border-radius: 8px;
		margin: 5px;
		background-color: #ffffff;
		padding: 8px;
		position: relative;
	}
	
	.u-close {
		position: absolute;
		top: 32rpx;
		right: 32rpx;
	}
	
	.demo-image {
		width: 100%;
		border-radius: 4px;
	}
	
	.demo-title {
		font-size: 30rpx;
		margin-top: 5px;
		color: $u-main-color;
	}
	
	.demo-tag {
		display: flex;
		margin-top: 5px;
	}
	
	.demo-tag-owner {
		background-color: $u-type-error;
		color: #FFFFFF;
		display: flex;
		align-items: center;
		padding: 4rpx 14rpx;
		border-radius: 50rpx;
		font-size: 20rpx;
		line-height: 1;
	}
	
	.demo-tag-text {
		border: 1px solid $u-type-primary;
		color: $u-type-primary;
		margin-left: 10px;
		border-radius: 50rpx;
		line-height: 1;
		padding: 4rpx 14rpx;
		display: flex;
		align-items: center;
		border-radius: 50rpx;
		font-size: 20rpx;
	}
	
	.demo-price {
		font-size: 30rpx;
		color: $u-type-error;
		margin-top: 5px;
	}
	
	.demo-shop {
		font-size: 22rpx;
		color: $u-tips-color;
		margin-top: 5px;
	}
</style>
<script setup>
import { ref, onMounted } from 'vue';
import { random, guid } from 'uview-plus';

const uWaterfallRef = ref(null);
const loadStatus = ref('loadmore');
const flowList = ref([]);
const list = ref([
  {
    price: 35,
    title: '北国风光,千里冰封,万里雪飘',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic.sc.chinaz.com/Files/pic/pic9/202002/zzpic23327_s.jpg',
  },
  {
    price: 75,
    title: '望长城内外,惟余莽莽',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic.sc.chinaz.com/Files/pic/pic9/202002/zzpic23325_s.jpg',
  },
  {
    price: 385,
    title: '大河上下,顿失滔滔',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic2.sc.chinaz.com/Files/pic/pic9/202002/hpic2119_s.jpg',
  },
  {
    price: 784,
    title: '欲与天公试比高',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic2.sc.chinaz.com/Files/pic/pic9/202002/zzpic23369_s.jpg',
  },
  {
    price: 7891,
    title: '须晴日,看红装素裹,分外妖娆',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic2.sc.chinaz.com/Files/pic/pic9/202002/hpic2130_s.jpg',
  },
  {
    price: 2341,
    shop: '李白杜甫白居易旗舰店',
    title: '江山如此多娇,引无数英雄竞折腰',
    image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23346_s.jpg',
  },
  {
    price: 661,
    shop: '李白杜甫白居易旗舰店',
    title: '惜秦皇汉武,略输文采',
    image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23344_s.jpg',
  },
  {
    price: 1654,
    title: '唐宗宋祖,稍逊风骚',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23343_s.jpg',
  },
  {
    price: 1678,
    title: '一代天骄,成吉思汗',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23343_s.jpg',
  },
  {
    price: 924,
    title: '只识弯弓射大雕',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23343_s.jpg',
  },
  {
    price: 8243,
    title: '俱往矣,数风流人物,还看今朝',
    shop: '李白杜甫白居易旗舰店',
    image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23343_s.jpg',
  },
]);

onMounted(() => {
  addRandomData();
});

const addRandomData = () => {
  for(let i = 0; i < 10; i++) {
    let index = random(0, list.value.length - 1);
    // 先转成字符串再转成对象,避免数组对象引用导致数据混乱
    let item = JSON.parse(JSON.stringify(list.value[index]))
    item.id = guid();
    flowList.value.push(item);
  }
};

const remove = (id) => {
  uWaterfallRef.value.remove(id);
};

const clear = () => {
  uWaterfallRef.value.clear();
};

// 模拟触底加载更多
const onReachBottom = () => {
  loadStatus.value = 'loading';
  // 模拟数据加载
  setTimeout(() => {
    addRandomData();
    loadStatus.value = 'loadmore';
  }, 1000)
};
</script>
<script>
	export default {
		data() {
			return {
				loadStatus: 'loadmore',
				flowList: [],
				list: [
					{
						price: 35,
						title: '北国风光,千里冰封,万里雪飘',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic.sc.chinaz.com/Files/pic/pic9/202002/zzpic23327_s.jpg',
					},
					{
						price: 75,
						title: '望长城内外,惟余莽莽',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic.sc.chinaz.com/Files/pic/pic9/202002/zzpic23325_s.jpg',
					},
					{
						price: 385,
						title: '大河上下,顿失滔滔',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic2.sc.chinaz.com/Files/pic/pic9/202002/hpic2119_s.jpg',
					},
					{
						price: 784,
						title: '欲与天公试比高',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic2.sc.chinaz.com/Files/pic/pic9/202002/zzpic23369_s.jpg',
					},
					{
						price: 7891,
						title: '须晴日,看红装素裹,分外妖娆',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic2.sc.chinaz.com/Files/pic/pic9/202002/hpic2130_s.jpg',
					},
					{
						price: 2341,
						shop: '李白杜甫白居易旗舰店',
						title: '江山如此多娇,引无数英雄竞折腰',
						image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23346_s.jpg',
					},
					{
						price: 661,
						shop: '李白杜甫白居易旗舰店',
						title: '惜秦皇汉武,略输文采',
						image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23344_s.jpg',
					},
					{
						price: 1654,
						title: '唐宗宋祖,稍逊风骚',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23343_s.jpg',
					},
					{
						price: 1678,
						title: '一代天骄,成吉思汗',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23343_s.jpg',
					},
					{
						price: 924,
						title: '只识弯弓射大雕',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23343_s.jpg',
					},
					{
						price: 8243,
						title: '俱往矣,数风流人物,还看今朝',
						shop: '李白杜甫白居易旗舰店',
						image: 'http://pic1.sc.chinaz.com/Files/pic/pic9/202002/zzpic23343_s.jpg',
					},
				]
			}
		},
		onLoad() {
			this.addRandomData();
		},
		onReachBottom() {
			this.loadStatus = 'loading';
			// 模拟数据加载
			setTimeout(() => {
				this.addRandomData();
				this.loadStatus = 'loadmore';
			}, 1000)
		},
		methods: {
			addRandomData() {
				for(let i = 0; i < 10; i++) {
					let index = this.$u.random(0, this.list.length - 1);
					// 先转成字符串再转成对象,避免数组对象引用导致数据混乱
					let item = JSON.parse(JSON.stringify(this.list[index]))
					item.id = this.$u.guid();
					this.flowList.push(item);
				}
			},
			remove(id) {
				this.$refs.uWaterfallRef.remove(id);
			},
			clear() {
				this.$refs.uWaterfallRef.clear();
			}
		}
	}
</script>

# API

# IndexBar Props

注意:通过v-model双向绑定传递参数,因为组件内部需要修改父组件的值。

参数 说明 类型 默认值 可选值
columns 瀑布流列数量 String | Number 2 支持数字和auto自动响应式判断
columnsMin 瀑布流最小列数 String | Number 2 在auto模式下最小列数判断
minColumnWidth 瀑布流最小列宽 Number 230 在auto模式下计算列数量
add-time 单条数据添加到队列的时间间隔,单位ms,见上方注意事项说明 String | Number 200 -
idKey 数据的唯一值的键名,见上方说明 String id -

# Methods

这些为组件内部的方法,需要通过ref调用

参数 说明
clear 清空列表数据
remove(id) id为唯一的"id"值,见上方说明