Files
2026-09-24 16:25:22 +08:00

194 lines
6.0 KiB
Plaintext

<script lang="ts" setup>
import { PropType, inject, getCurrentInstance, onMounted, watch, onBeforeUnmount } from "vue"
import { getUid } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig, xProvitae } from "../../config/xConfig.uts"
import { xWaterfallDataItemType } from "../../interface.uts"
type itemobjType = {
len : number,
height : number,
col : number,
firstHeight : number
}
/**
* @name 瀑布流子组件 xWaterfallItem
* @description 注意事项:内容不可异步,不可直接修改本节点style上的margin,padding你可以通过在节点内套一个view来改变间隙。如果你放置了异步图片你应该写死图片的高。总之不可异步改变内容高,否则排版会失效和错乱。
* @page /pages/index/waterfall
* @category 展示组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({name:"xWaterfallItem"})
const props = defineProps({
/**
* 循环时,请填写key同等index即可
* 暂时无作用。
*/
order: {
type: Number,
default: -1
}
})
const proxy = getCurrentInstance()!.proxy!
const parent = (proxy.$parent) as XWaterfallComponentPublicInstance | null;
const isRender = ref(false)
const isCalcNodeInfo = ref(false)
const selfCol = ref(-1)
const selfTop = ref(-1)
const sefLeft = ref(-1)
const sefHeight = ref(-1)
const uid = ('xWm' + getUid()) as string
let tid = 0
const xWaterFallColumn = inject("xWaterFallColumn", computed(() : number => 2))
const xWaterGutter = inject("xWaterGutter", computed(() : number => 0))
const xWaterFallCurrentId = inject("xWaterFallCurrentId", computed(() : string => ''))
const xWaterWidthParent = inject("xWaterWidthParent", computed(() : number => 0))
const xWaterTopParent = inject("xWaterTopParent", computed(() : number => 0))
const xWaterPositionTopParent = inject("xWaterPositionTopParent", computed(() : number => 0))
const xWaterParentShowViewHeight = inject("xWaterParentShowViewHeight", computed(() : number => 0))
const _widthItem = computed(() : string => {
if(xWaterWidthParent.value==0) return '0px'
let w = ((xWaterWidthParent.value - (xWaterFallColumn.value - 1) * xWaterGutter.value) / xWaterFallColumn.value).toString()
return w + 'px'
})
const ishowViewEle = ref(true)
// 是否在可视范围内
const _isShowViewEle = computed(() : boolean => {
if (!isCalcNodeInfo.value) return true;
// 元素自身的顶自身的顶+容器的顶
let self_top = Math.ceil(selfTop.value + xWaterPositionTopParent.value)
let self_bottom = Math.ceil(self_top + sefHeight.value)
// 容器当前的scrolltop
let parent_top = Math.ceil(xWaterTopParent.value)
let parent_bottom = Math.ceil(xWaterTopParent.value + xWaterParentShowViewHeight.value)
let bottom = self_top - xWaterTopParent.value + sefHeight.value
const diff = -100
let top = self_top - xWaterTopParent.value + diff
return bottom >= 0 && top <= xWaterParentShowViewHeight.value
})
const boxwidth = () : string => {
return (100 / xWaterFallColumn.value).toString() + "%"
}
const leftgutter = computed(() : string => {
let o = (selfCol.value + 1) % 2
return o > 0 ? xWaterGutter.value + 'px' : '0px'
})
const getNodeinfo = () => {
uni.createSelectorQuery()
.in(proxy)
.select(".xWaterFallItem")
.boundingClientRect()
.exec((res) => {
if (res.length == 0) return;
let node = res[0] as NodeInfo
sefHeight.value = node.height!;
let opts = {
id: uid,
top: node.top!,
left: node.left!,
width: node.width!,
height: node.height!,
col: -1,
isRender: isRender.value,
order: props.order
} as xWaterfallDataItemType
if (parent == null) {
console.error('xWaterFallItem组件不能单独使用。')
return;
}
let datas = parent.pushChild(opts)
selfCol.value = datas.get('col')!!
selfTop.value = datas.get("top")!!
let w = ((xWaterWidthParent.value - (xWaterFallColumn.value - 1) * xWaterGutter.value) / xWaterFallColumn.value)
sefLeft.value = (w * selfCol.value + selfCol.value * xWaterGutter.value)
isCalcNodeInfo.value = true;
ishowViewEle.value = false
parent.nextRender()
})
}
const renderInit = () => {
let opts = {
id: uid as string,
top: 0,
left: 0,
width: 0,
height: 0,
col: -1,
isRender: false,
order: props.order as number
} as xWaterfallDataItemType
if (parent == null) {
console.error('xWaterFallItem组件不能单独使用。')
return;
}
parent.pushChild(opts)
}
watch(xWaterFallCurrentId, (newval : string) => {
if (newval != uid || isRender.value) return;
isRender.value = true;
let timerout = 10
// #ifdef APP-IOS || MP-WEIXIN
timerout = 110
// #endif
// #ifdef APP-ANDROID
timerout = 90
// #endif
// #ifdef MP-WEIXIN || APP-HARMONY
timerout = 100
// #endif
tid = setTimeout(function () {
getNodeinfo()
}, timerout);
})
onMounted(() => {
renderInit()
})
onBeforeUnmount(() => {
clearTimeout(tid)
if (parent == null) return;
parent.removeChild(uid, sefHeight.value, selfCol.value)
})
</script>
<template>
<view v-if='isRender&&_isShowViewEle' class="xWaterFallItem" :ref="uid" :style="{
width:boxwidth(),
visibility:isCalcNodeInfo?'visible':'hidden',opacity:isCalcNodeInfo?'1':'0',left:sefLeft+'px',top:selfTop+'px'}">
<view :style="{
width:_widthItem,
marginBottom:xWaterGutter+'px'
}">
<!--
@slot 默认内容插槽,不要有动态高或者异步加载内容的行为。
@prop {number} default 当前所在列
-->
<slot name="default" :col='selfCol'></slot>
</view>
</view>
</template>
<style scoped>
.xWaterFallItem {
position: absolute;
transition-duration: 200ms;
transition-property: opacity;
transition-timing-function: linear;
}
</style>