小程序实现瀑布流布局
小程序瀑布流
基于vue3
的小程序瀑布流
<template>
<view class="u-waterfall">
<view id="u-left-column" class="u-column"
><slot name="left" :left-list="leftList"></slot
></view>
<view id="u-right-column" class="u-column">
<slot name="right" :right-list="rightList"></slot>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, watch, getCurrentInstance } from "vue";
const props = defineProps({
flowList: {
type: Array,
default: () => [],
},
// 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
addTime: {
type: Number,
default: 250,
},
});
const leftList = ref([]);
const rightList = ref([]);
const tempList = ref([]);
const instance = getCurrentInstance();
const uGetRect = (id: string) => {
return new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(instance);
query
.select(id)
.boundingClientRect((data) => {
resolve(data);
})
.exec();
});
};
const splitData = async () => {
if (!tempList.value.length) {
return;
}
const leftRect: any = await uGetRect("#u-left-column");
const rightRect: any = await uGetRect("#u-right-column");
// 如果左边小于或等于右边,就添加到左边,否则添加到右边
const item = tempList.value[0];
// 解决多次快速上拉后,可能数据会乱的问题,因为经过上面的两个await节点查询阻塞一定时间,加上后面的定时器干扰
// 数组可能变成[],导致此item值可能为undefined
if (!item) {
return;
}
if (leftRect.height < rightRect.height) {
leftList.value.push(item);
} else if (leftRect.height > rightRect.height) {
rightList.value.push(item);
} else {
// 这里是为了保证第一和第二张添加时,左右都能有内容
// 因为添加第一张,实际队列的高度可能还是0,这时需要根据队列元素长度判断下一个该放哪边
if (leftList.value.length <= rightList.value.length) {
leftList.value.push(item);
} else {
rightList.value.push(item);
}
}
// 移除临时列表的第一项
tempList.value.splice(0, 1);
// 如果临时数组还有数据,继续循环
if (tempList.value.length) {
setTimeout(() => {
splitData();
}, props.addTime);
}
};
watch(
() => props.flowList,
(nVal, oVal) => {
if (!oVal) {
oVal = [];
}
if (nVal.length <= oVal.length) {
leftList.value = [];
rightList.value = [];
}
const length = leftList.value.length + rightList.value.length;
const diff = nVal.slice(length);
//差值
tempList.value = tempList.value.concat(JSON.parse(JSON.stringify(diff)));
splitData();
},
{
immediate: true,
}
);
</script>
<style lang="scss" scoped>
.u-waterfall {
display: flex;
flex-direction: row;
align-items: flex-start;
}
.u-column {
display: flex;
flex: 1;
flex-direction: column;
height: auto;
}
.u-image {
width: 100%;
}
</style>