uniapp瀑布流组件

779 阅读1分钟

uniapp的瀑布流组件,实现思路,核心函数setSplitData,先判断左右两边的高度是否一致,不一直就往高度小的一边push数据

<template>
	<view class="waterfall">
		<view class="column" id="left-column"><slot name="left" :leftList="leftList"></slot></view>
		<view class="column" id="right-column"><slot name="right" :rightList="rightList"></slot></view>
	</view>
</template>

<script>
export default {
	name: 'waterfall',
	data() {
		return {
			leftList: [], //左边的数据
			rightList: [] //右边的数据
		};
	},
	props: {
		// 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
		// 单位ms
		addTime: {
			type: [Number, String],
			default: 50
		}
	},
	methods: {
		// 清空数据
		onDel() {
			this.leftList = []; //左边的数据
			this.rightList = []; //右边的数据
		},
		// 设置数据
		setData(data) {
			console.log(Array.isArray(data) || data.length === 0);
			if (!Array.isArray(data) || data.length === 0) return;
			this.setSplitData(data);
		},
		// 设置瀑布流数据
		async setSplitData(data = []) {
			// 如果data没有数据或者不是数组,不执行
			if (!Array.isArray(data) || data.length === 0) return;
			console.log(4, data);
			let data_deepClone = this.deepClone(data); //深拷贝
			let lefrRect = await this.getRect('#left-column');
			let rightRect = await this.getRect('#right-column');
			// 如果左边小于或等于右边,就添加到左边,否则添加到右边
			let item = data_deepClone[0];
			// 解决多次快速上拉后,可能数据会乱的问题,因为经过上面的两个await节点查询阻塞一定时间,加上后面的定时器干扰
			// 数组可能变成[],导致此item值可能为undefined
			if (!item) return;
			if (lefrRect.height < rightRect.height) {
				this.leftList.push(item);
			} else if (lefrRect.height > rightRect.height) {
				this.rightList.push(item);
			} else {
				// 这里是为了保证第一和第二张添加时,左右都能有内容
				// 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
				this.leftList.length <= this.rightList.length ? this.leftList.push(item) : this.rightList.push(item);
			}
			// 移除临时列表的第一项
			data_deepClone.splice(0, 1);
			// 如果临时数组还有数据,继续循环
			if (data_deepClone.length) {
				setTimeout(() => {
					this.setSplitData(data_deepClone);
				}, this.addTime);
			}
		},
		deepClone(target, map = new Map()) {
			if (target !== null && typeof target === 'object') {
				// 从缓存容器中读取克隆对象
				let cloneTarget = map.get(target);
				// 如果存在, 返回前面缓存的克隆对象
				if (cloneTarget) {
					return cloneTarget;
				}
				// 创建克隆对象(可能是{}或者[])
				if (target instanceof Array) {
					cloneTarget = [];
					// 缓存到map中
					map.set(target, cloneTarget);
					target.forEach((item, index) => {
						cloneTarget[index] = this.deepClone(item, map);
					});
				} else {
					cloneTarget = {};
					// 缓存到map中
					map.set(target, cloneTarget);
					Object.keys(target).forEach(key => {
						cloneTarget[key] = this.deepClone(target[key], map);
					});
				}
				return cloneTarget;
			}
			return target;
		},
		// 查询节点信息
		getRect(id) {
			if (!id) return '';
			return new Promise(resolve => {
				const query = uni.createSelectorQuery().in(this);
				query
					.select(id)
					.boundingClientRect(data => {
						resolve(data);
					})
					.exec();
			});
		}
	}
};
</script>

<style lang="scss" scoped>
.waterfall {
	display: flex;
	flex-direction: row;
	align-items: flex-start;
}

.column {
	display: flex;
	flex: 1;
	flex-direction: column;
	height: auto;
}

.image {
	width: 100%;
}
</style>