vue3 uniapp封装一个瀑布流组件
新增组件m-waterfall 这样就可以在页面直接使用 不用在引入了
<template>
<view class="m-waterfall">
<view id="m-left-column" class="m-column">
<slot name="left" :leftList="leftList"></slot>
</view>
<view id="m-right-column" class="m-column">
<slot name="right" :rightList="rightList"></slot>
</view>
</view>
</template>
<script setup>
/**
* @param value 瀑布流数据
* @param addTime 插入数据的时间间隔
* @param keyIdData / id值,用于清除某一条数据时,根据此idKey名称找到并移除
*/
import {
computed,
defineProps,
toRefs
} from "vue"
const props=defineProps({
// 瀑布流数据
value: {
required: true,
type: Array,
default: ()=>[]
},
// 每次向结构插入数据的时间间隔,间隔越长,越能保证两列高度相近,但是对用户体验越不好
addTime: {
type: [Number, String],
default: 200
},
// id值,用于清除某一条数据时,根据此idKey名称找到并移除
keyIdData: {
type: String,
default: 'id'
}
})
const {
value,
addTime,
keyIdData
} = toRefs(props)
const leftList = ref([])
const rightList = ref([])
const tempList = ref([])
const copyFlowList= computed (()=> {
return cloneData(value.value);
})
watch(()=>value.value,(nVal,oVal)=>{
let startIndex = Array.isArray(oVal) && oVal.length > 0 ? oVal.length : 0;
tempList.value = tempList.value.concat(cloneData(nVal.slice(startIndex)));
splitData();
})
onMounted(()=>{
tempList.value = cloneData(copyFlowList.value);
setTimeout(()=>{
splitData();
},200)
})
const instance = getCurrentInstance()
const getysHeigth=(classd)=>{
return new Promise((resolve, reject) => {
uni.createSelectorQuery().in(instance)
.select(classd)
.boundingClientRect(data => {
if (data) {
resolve(data);
} else {
reject(new Error('获取节点信息失败'));
}
})
.exec();
});
}
const splitData = async () => {
if (tempList.value.length==0) return;
let leftRect = await getysHeigth('#m-left-column');
let rightRect = await getysHeigth('#m-right-column');
// 如果左边小于或等于右边,就添加到左边,否则添加到右边
let 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();
}, addTime)
}
}
// 复制而不是引用对象和数组
const cloneData=(data)=>{
if(data){
return JSON.parse(JSON.stringify(data));
}else{
return [];
}
}
</script>
<style lang="scss" scoped>
.m-waterfall {
display: flex;
flex-direction: row;
align-items: flex-start;
}
.m-column {
display: flex;
flex: 1;
flex-direction: column;
height: auto;
}
.m-image {
width: 100%;
}
</style>
组件使用
<m-waterfall :value="product">
<!-- 左边数据 -->
<template v-slot:left="{leftList}">
<view @click="addDta" class="prodecutitem" v-for="(item,index) in leftList" :key="index">
<view style="width: 100%;">
<m-imgage :url="item.image"></m-imgage>
</view>
<view class="title">{{item.title}}</view>
<view class="desc">{{item.title}}</view>
</view>
</template>
<!-- 右边数据 -->
<template v-slot:right="{rightList}">
<view class="prodecutitem" v-for="(item,index) in rightList" :key="index">
<view>
<m-imgage :url="item.image"></m-imgage>
</view>
<view class="title">{{item.title}}</view>
<view class="desc">{{item.title}}</view>
</view>
</template>
</m-waterfal>
数据
const product=ref([
{
title:'水果蔬菜1',
image:imgSrc.value
},
{
title:'水果蔬菜2',
image:"https://img2.baidu.com/it/u=3893165480,918722033&fm=253&fmt=auto&app=120&f=JPEG?w=729&h=1215"
},
{
title:'水果蔬菜3',
image:imgSrc.value
},
{
title:'水果蔬菜1',
image:imgSrc.value
},
{
title:'水果蔬菜3',
image:imgSrc.value
}
])
原文地址:https://blog.csdn.net/u010843503/article/details/145120258
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!