UNPKG

sard-uniapp

Version:

sard-uniapp 是一套基于 Uniapp + Vue3 框架开发的兼容多端的 UI 组件库

145 lines (144 loc) 4.52 kB
import { computed, getCurrentInstance, onMounted, ref, unref, watch, } from 'vue'; import { useMouseDown, useTimeout } from '../../use'; import { clamp, getBoundingClientRect, getWindowInfo, uniqid, } from '../../utils'; export function useFloatingBubble(props, emit, options = {}) { const disabled = computed(() => !props.draggable || unref(options.disabled)); const instance = getCurrentInstance(); const bubbleId = uniqid(); const initialized = ref(false); let bubbleRect; const { windowWidth, windowHeight, windowTop, statusBarHeight, safeAreaInsets, } = getWindowInfo(); let downCoord = { x: 0, y: 0, }; const position = ref({ x: 0, y: 0, }); const animated = ref(false); const stopBubbling = ref(false); const { start: nonAnimatedLater, stop: cancelNonAnimated } = useTimeout(() => { animated.value = false; }, 500); function getMinX() { return props.gapX; } function getMaxX() { return windowWidth - props.gapX - bubbleRect.width; } function getMinY() { const navbarHeight = props.navbarHeight || 0; return props.gapY + (navbarHeight ? navbarHeight + statusBarHeight : 0); } function getMaxY() { const tabbarHeight = props.tabbarHeight || 0; return (windowHeight - tabbarHeight - safeAreaInsets.bottom - props.gapY - bubbleRect.height); } const onTouchStart = async (event) => { stopBubbling.value = false; if (disabled.value) return; cancelNonAnimated(); animated.value = false; downCoord = { x: event.touches[0].clientX, y: event.touches[0].clientY, }; bubbleRect = await getBoundingClientRect(`.${bubbleId}`, instance); }; const onTouchMove = (event) => { if (disabled.value) return; if (!bubbleRect) { return; } let x = 0; let y = 0; if (props.axis === 'none') { x = getMaxX(); y = getMaxY(); } else { const deltaX = event.touches[0].clientX - downCoord.x; const deltaY = event.touches[0].clientY - downCoord.y; const deviation = 10; if (Math.abs(deltaX) > deviation || Math.abs(deltaY) > deviation) { stopBubbling.value = true; } x = bubbleRect.left + deltaX; y = bubbleRect.top + deltaY; x = clamp(x, getMinX(), getMaxX()); y = clamp(y, getMinY(), getMaxY()); if (props.axis === 'y') { x = getMaxX(); } else if (props.axis === 'x') { y = getMaxY(); } } const offset = { x, y, }; position.value = offset; emit('update:offset', offset); }; const onTouchEnd = () => { if (disabled.value) return; if (bubbleRect) { if (props.magnet) { let { x, y } = position.value; if (props.magnet === 'x') { x = x < (windowWidth - bubbleRect.width) / 2 ? getMinX() : getMaxX(); } else if (props.magnet === 'y') { y = y < (windowHeight - bubbleRect.height) / 2 ? getMinY() : getMaxY(); } const offset = { x, y, }; position.value = offset; emit('update:offset', offset); } } animated.value = true; nonAnimatedLater(); bubbleRect = undefined; }; const onMouseDown = useMouseDown(onTouchStart, onTouchMove, onTouchEnd); onMounted(async () => { bubbleRect = await getBoundingClientRect(`.${bubbleId}`, instance); position.value = props.offset ?? { x: getMaxX(), y: getMaxY(), }; bubbleRect = undefined; initialized.value = true; }); watch(() => props.offset, () => { if (props.offset) { position.value = props.offset; } }); return { onTouchStart, onTouchMove, onTouchEnd, onMouseDown, position, initialized, animated, bubbleId, stopBubbling, windowWidth, windowHeight, windowTop, }; }