1
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
|
||||
import { PropType, computed, ref, provide, nextTick, onMounted, getCurrentInstance } from "vue"
|
||||
import { getUid, getUnit } from "../../core/util/xCoreUtil.uts"
|
||||
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
|
||||
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
|
||||
import { xWaterfallDataItemType } from "../../interface.uts"
|
||||
import { xConfig, xProvitae } from "../../config/xConfig.uts"
|
||||
type callType = () => Promise<boolean>
|
||||
|
||||
/**
|
||||
* @name 瀑布流 xWaterfall
|
||||
* @description 可以通过columnu来控制排版列数,默认是2列排版。可以无限加载不卡,全异步流加载.使用时请参考demo布局,已精心为你规划如何提高性能.
|
||||
* @page /pages/index/waterfall
|
||||
* @category 展示组件
|
||||
* @constant 平台兼容
|
||||
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
|
||||
*/
|
||||
defineOptions({name:"xWaterfall"})
|
||||
const emit = defineEmits([
|
||||
'bottom',
|
||||
'top',
|
||||
])
|
||||
const proxy = getCurrentInstance()?.proxy
|
||||
const xWaterFallWrap = ref<UniElement|null>(null)
|
||||
let resizeObserver = null as UniResizeObserver | null
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 瀑布流的列数
|
||||
* 不可动态动态更改。
|
||||
*/
|
||||
column: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
/**
|
||||
* 用来刷新数据的,你可以通过你的外面list.length来传递进来
|
||||
* 比如为0时,自动清空瀑布流数据。
|
||||
*/
|
||||
listCount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/**
|
||||
* 间距,单位不能为%比,可以是数字字符,带rpx,px等单位
|
||||
* 默认的数字符单位是全局的unit单位。
|
||||
*/
|
||||
gutter: {
|
||||
type: String,
|
||||
default: '8'
|
||||
},
|
||||
/**
|
||||
* 是否响应尺寸监测。设置为false可以关闭
|
||||
* 关闭后能增加性能。
|
||||
*/
|
||||
isResize:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
},
|
||||
disabledScroll:{
|
||||
type:Boolean,
|
||||
default:false
|
||||
}
|
||||
})
|
||||
const firstRender = ref(false)
|
||||
const domList = ref<xWaterfallDataItemType[]>([])
|
||||
const parentWidth = ref(0)
|
||||
// 视图的顶点.
|
||||
const parentTop = ref(0)
|
||||
const parentPositonTop = ref(0)
|
||||
// 可视范围高
|
||||
const parentShowViewHeight = ref(uni.getWindowInfo().windowHeight)
|
||||
|
||||
const tidkey = ref(true)
|
||||
const isResize = computed(():boolean=>props.isResize)
|
||||
const _disabledScroll = computed(():boolean=>props.disabledScroll)
|
||||
let timeid = 0
|
||||
const cols = ref<number[]>([])
|
||||
const totalHeight = computed(() : number => {
|
||||
let sort = cols.value.slice(0).sort((a : number, b : number) : number => b - a);
|
||||
if (sort.length == 0) return 0;
|
||||
return sort[0]
|
||||
})
|
||||
let tid = 0
|
||||
const currentId = ref("")
|
||||
|
||||
/**
|
||||
* 添加元素。
|
||||
* @hidden
|
||||
*/
|
||||
const pushChild = (item : xWaterfallDataItemType) : Map<string, number> => {
|
||||
let index = domList.value.findIndex((el : xWaterfallDataItemType) : boolean => el.id == item.id);
|
||||
let col = -1;
|
||||
let minHeight = 0;
|
||||
if (item.col == -1 && item.height > 0) {
|
||||
let minAr = cols.value.slice(0).sort((a : number, b : number) : number => a - b);
|
||||
let minnumber = minAr[0]
|
||||
let minindex = cols.value.findIndex((el : number) : boolean => el == minnumber);
|
||||
col = minindex;
|
||||
minHeight = minnumber;
|
||||
cols.value[minindex] = cols.value[minindex] + item.height
|
||||
item.col = col;
|
||||
}
|
||||
if (index == -1) {
|
||||
domList.value.push(item)
|
||||
} else {
|
||||
domList.value[index] = item;
|
||||
}
|
||||
if (currentId.value == "" && firstRender.value == false) {
|
||||
firstRender.value = true;
|
||||
nextTick(() => {
|
||||
currentId.value = item.id;
|
||||
})
|
||||
}
|
||||
let maps = new Map<string, number>();
|
||||
|
||||
maps.set('col', col)
|
||||
maps.set('top', minHeight)
|
||||
return maps;
|
||||
}
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
const refreshCallTop = () => {
|
||||
let cid = currentId.value;
|
||||
if (cid == "" && domList.value.length > 0) {
|
||||
cid = domList.value[0].id
|
||||
}
|
||||
if (cid == "") return;
|
||||
|
||||
clearTimeout(tid)
|
||||
function updateAsync(){
|
||||
tid = setTimeout(function () {
|
||||
let index = domList.value.findIndex((el : xWaterfallDataItemType) : boolean => el.id == cid);
|
||||
if (index == -1) return;
|
||||
index = index + 1;
|
||||
if (index >= domList.value.length) return;
|
||||
currentId.value = domList.value[index].id;
|
||||
}, 20);
|
||||
}
|
||||
|
||||
// #ifdef APP-ANDROID
|
||||
class IntentRunable extends Runnable {
|
||||
override run() {
|
||||
updateAsync()
|
||||
}
|
||||
}
|
||||
UTSAndroid.getUniActivity()!.runOnUiThread(new IntentRunable())
|
||||
// #endif
|
||||
// #ifndef APP-ANDROID
|
||||
updateAsync()
|
||||
// #endif
|
||||
|
||||
}
|
||||
const getColunm = () : number[] => {
|
||||
// #ifdef APP-ANDROID
|
||||
return Array.fromNative(new IntArray((props.column).toInt()))
|
||||
// #endif
|
||||
|
||||
return new Array(props.column).fill(0)
|
||||
}
|
||||
/**
|
||||
* 移除元素。
|
||||
* @hidden
|
||||
*/
|
||||
const removeChild = (id : string, height : number, mycol : number) => {
|
||||
let index = domList.value.findIndex((el : xWaterfallDataItemType) : boolean => el.id == id);
|
||||
|
||||
if (index > -1 && mycol > -1) {
|
||||
domList.value.splice(index, 1)
|
||||
cols.value[mycol] = cols.value[mycol] - height
|
||||
}
|
||||
}
|
||||
provide("xWaterFallColumn", computed(() : number => props.column))
|
||||
provide("xWaterGutter", computed(() : number => {
|
||||
let p = checkIsCssUnit(props.gutter, xConfig.unit)
|
||||
let unit = getUnit(p)
|
||||
let n = parseInt(p)
|
||||
if (unit == 'rpx') {
|
||||
n = uni.rpx2px(n)
|
||||
}
|
||||
return n;
|
||||
}))
|
||||
provide("xWaterWidthParent", computed(() : number => parentWidth.value))
|
||||
provide("xWaterTopParent", computed(() : number => parentTop.value))
|
||||
provide("xWaterPositionTopParent", computed(() : number => parentPositonTop.value))
|
||||
provide("xWaterFallCurrentId", computed(() : string => currentId.value))
|
||||
provide("xWaterParentShowViewHeight", computed(() : number => parentShowViewHeight.value))
|
||||
watch(() : number => props.listCount, () => {
|
||||
if (props.listCount == 0) {
|
||||
cols.value = getColunm()
|
||||
domList.value = [] as xWaterfallDataItemType[];
|
||||
currentId.value = ''
|
||||
firstRender.value = false
|
||||
} else {
|
||||
nextTick(() => {
|
||||
refreshCallTop()
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
const resize = ()=>{
|
||||
|
||||
let ele = xWaterFallWrap.value
|
||||
if (ele == null) return;
|
||||
// #ifdef APP||WEB
|
||||
resizeObserver = new UniResizeObserver((entries : Array<UniResizeObserverEntry>) => {
|
||||
entries.forEach(entry => {
|
||||
if(!isResize.value) return;
|
||||
if (entry.target == ele) {
|
||||
let bound = ele.getBoundingClientRect();
|
||||
if(bound.width==parentWidth.value) return;
|
||||
clearTimeout(timeid)
|
||||
timeid = setTimeout(function() {
|
||||
tidkey.value = false
|
||||
domList.value = [] as xWaterfallDataItemType[];
|
||||
cols.value = getColunm()
|
||||
parentWidth.value = bound.width!;
|
||||
currentId.value = ''
|
||||
firstRender.value = false
|
||||
timeid = setTimeout(function() {
|
||||
tidkey.value = true
|
||||
}, 50);
|
||||
|
||||
}, 250);
|
||||
}
|
||||
})
|
||||
})
|
||||
resizeObserver?.observe(ele!)
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
ele.getBoundingClientRectAsync().then(bound=>{
|
||||
|
||||
if(bound.width==parentWidth.value) return;
|
||||
tidkey.value = false
|
||||
domList.value = [] as xWaterfallDataItemType[];
|
||||
cols.value = getColunm()
|
||||
parentWidth.value = bound.width!;
|
||||
currentId.value = ''
|
||||
firstRender.value = false
|
||||
timeid = setTimeout(function() {
|
||||
tidkey.value = true
|
||||
}, 50);
|
||||
})
|
||||
|
||||
|
||||
// #endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
const getParentNodeinfo = () => {
|
||||
uni.createSelectorQuery()
|
||||
.in(proxy)
|
||||
.select(".xWaterFallWrap")
|
||||
.boundingClientRect()
|
||||
.exec((res) => {
|
||||
let node = res[0] as NodeInfo;
|
||||
parentWidth.value = node.width!;
|
||||
// parentShowViewHeight.value = node.bottom!
|
||||
parentPositonTop.value = node.top!;
|
||||
resize();
|
||||
})
|
||||
}
|
||||
let tidsd = 12
|
||||
onMounted(() => {
|
||||
cols.value = getColunm()
|
||||
// #ifndef APP-HARMONY
|
||||
getParentNodeinfo()
|
||||
// #endif
|
||||
// #ifdef APP-HARMONY
|
||||
tidsd = setTimeout(function() {
|
||||
parentShowViewHeight.value = uni.getSystemInfoSync().windowHeight
|
||||
getParentNodeinfo()
|
||||
}, 150);
|
||||
// #endif
|
||||
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
// uni.$off("onResize",resize)
|
||||
resizeObserver?.disconnect()
|
||||
clearTimeout(timeid)
|
||||
clearTimeout(tidsd)
|
||||
})
|
||||
|
||||
onPageScroll((opts:OnPageScrollOptions)=>{
|
||||
let ele = xWaterFallWrap.value
|
||||
if (ele == null) return;
|
||||
// let bounds = ele.getBoundingClientRect()
|
||||
// parentPositonTop.value = bounds.top;
|
||||
|
||||
// parentTop.value = opts.scrollTop;
|
||||
})
|
||||
|
||||
function onScroll(evt:UniScrollEvent){
|
||||
let ele = xWaterFallWrap.value
|
||||
if (ele == null) return;
|
||||
parentTop.value = evt.detail.scrollTop;
|
||||
}
|
||||
|
||||
function topCallFun(){
|
||||
emit('top')
|
||||
}
|
||||
function bootomCallFun(){
|
||||
emit('bottom')
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
pushChild,
|
||||
nextRender: refreshCallTop,
|
||||
removeChild
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<view ref="xWaterFallWrap" style="flex:1" >
|
||||
<scroll-view
|
||||
:show-scrollbar="false"
|
||||
@scrolltolower="bootomCallFun"
|
||||
@scrolltoupper="topCallFun"
|
||||
:direction="_disabledScroll?'none':'vertical'"
|
||||
@scroll="onScroll"
|
||||
class="xWaterFallWrapScoll">
|
||||
<view v-if="tidkey" class="xWaterFallWrap" :style="{height:totalHeight+'px'}">
|
||||
<!--
|
||||
@slot 只能放置直接子节点x-waterfall-item
|
||||
-->
|
||||
<slot></slot>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
<style scoped>
|
||||
.xWaterFallWrapScoll{
|
||||
height:100%;
|
||||
}
|
||||
.xWaterFallWrap {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user