You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.9 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view :class="`wd-resize ${customClass}`" :style="rootStyle">
<!--插槽需要脱离父容器文档流防止父容器固宽固高进而导致插槽大小被被父容器限制-->
<view :class="`wd-resize__container ${customContainerClass}`">
<!--被监听的插槽-->
<slot />
<!--监听插槽变大-->
<scroll-view
class="wd-resize__wrapper"
:scroll-y="true"
:scroll-top="expandScrollTop"
:scroll-x="true"
:scroll-left="expandScrollLeft"
@scroll="onScrollHandler"
>
<view class="wd-resize__wrapper--placeholder" style="height: 100000px; width: 100000px"></view>
</scroll-view>
<!--监听插槽变小-->
<scroll-view
class="wd-resize__wrapper"
:scroll-y="true"
:scroll-top="shrinkScrollTop"
:scroll-x="true"
:scroll-left="shrinkScrollLeft"
@scroll="onScrollHandler"
>
<view class="wd-resize__wrapper--placeholder" style="height: 250%; width: 250%"></view>
</scroll-view>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-resize',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, getCurrentInstance, onMounted, ref } from 'vue'
import { addUnit, objToStyle } from '../common/util'
interface Props {
customClass?: string
customContainerClass?: string
customStyle?: string
}
const props = withDefaults(defineProps<Props>(), {
customStyle: '',
customClass: '',
customContainerClass: ''
})
const expandScrollTop = ref<number>(0)
const shrinkScrollTop = ref<number>(0)
const expandScrollLeft = ref<number>(0)
const shrinkScrollLeft = ref<number>(0)
const height = ref<number>(0)
const width = ref<number>(0)
const scrollEventCount = ref<number>(0)
const rootStyle = computed(() => {
const style: Record<string, string | number> = {
width: addUnit(width.value),
height: addUnit(height.value)
}
return `${objToStyle(style)};${props.customStyle}`
})
let onScrollHandler = () => {}
const { proxy } = getCurrentInstance() as any
const emit = defineEmits(['resize'])
onMounted(() => {
const query = uni
.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(proxy)
// #endif
.select('.wd-resize__container')
.boundingClientRect()
query.exec(([res]) => {
// 闭包记录容器高度
let lastHeight = res.height
let lastWidth = res.width
// 立即填充父容器高宽
height.value = lastHeight
width.value = lastWidth
// 监听滚动事件
onScrollHandler = () => {
const query = uni
.createSelectorQuery()
// #ifndef MP-ALIPAY
.in(proxy)
// #endif
.select('.wd-resize__container')
.boundingClientRect()
query.exec(([res]) => {
// 前两次滚动事件被触发,说明 created 的修改已渲染,通知用户代码当前容器大小
if (scrollEventCount.value++ === 0) {
const result: Record<string, string | number> = {}
;['bottom', 'top', 'left', 'right', 'height', 'width'].forEach((propName) => {
result[propName] = res[propName]
})
emit('resize', result)
}
// 滚动条拉到底部会触发两次多余的事件,屏蔽掉。
if (scrollEventCount.value < 3) return
// 手动设置父容器高宽,防止父容器坍塌
// 滚动完,重新获取容器新的高度
const newHeight = res.height
const newWidth = res.width
// 立即填充父容器高宽
height.value = newHeight
width.value = newWidth
// 宽高都改变时,只需要触发一次 size 事件
const emitStack: number[] = []
if (newHeight !== lastHeight) {
lastHeight = newHeight
emitStack.push(1)
}
if (newWidth !== lastWidth) {
lastWidth = newWidth
emitStack.push(1)
}
if (emitStack.length !== 0) {
const result = {}
;['bottom', 'top', 'left', 'right', 'height', 'width'].forEach((propName) => {
result[propName] = res[propName]
})
emit('resize', result)
}
// 滚动条拉到底部(如果使用 nextTick 效果更佳)
scrollToBottom({
lastWidth: lastWidth,
lastHeight: lastHeight
})
})
}
// 滚动条拉到底部(如果使用 nextTick 效果更佳)
scrollToBottom({
lastWidth: lastWidth,
lastHeight: lastHeight
})
})
})
function scrollToBottom({ lastWidth, lastHeight }) {
expandScrollTop.value = 100000 + lastHeight
shrinkScrollTop.value = 3 * height.value + lastHeight
expandScrollLeft.value = 100000 + lastWidth
shrinkScrollLeft.value = 3 * width.value + lastWidth
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>