自学内容网 自学内容网

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)!