- 移除 paymentInput 组件中的冗余属性 - 在 liveMinWindow 组件中添加路由监听,实现自动关闭功能 - 删除 PaymentInput 组件中的无用日志输出
183 lines
4.7 KiB
Vue
183 lines
4.7 KiB
Vue
<template>
|
|
<div ref="dragRef"
|
|
:style="style"
|
|
class="fixed rounded-5px overflow-hidden shadow-lg cursor-move z-50"
|
|
@mousedown.stop="handleDragStart"
|
|
@touchstart.stop="handleDragStart">
|
|
<div class="relative" @click.stop="handleClick">
|
|
<img :src="props.snapshot"
|
|
class="w-80px object-cover"
|
|
alt="直播画面">
|
|
|
|
<div class="absolute inset-0 bg-black/40 flex items-center justify-center"
|
|
>
|
|
<span class="text-white text-12px">点击回到直播</span>
|
|
</div>
|
|
<button @click.stop="handleClose"
|
|
class="absolute top-10px right-10px text-white bg-black/40 rounded-full w-5 h-5 flex items-center justify-center hover:bg-black/60 cursor-pointer">
|
|
<van-icon name="cross" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onBeforeUnmount, shallowRef, onMounted } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useThrottleFn } from '@vueuse/core'
|
|
|
|
const props = defineProps({
|
|
snapshot: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
onClose: {
|
|
type: Function,
|
|
default: () => {}
|
|
},
|
|
onClick: {
|
|
type: Function,
|
|
default: () => {}
|
|
},
|
|
initialPosition: {
|
|
type: Object,
|
|
default: () => ({
|
|
top: '80px',
|
|
right: '16px'
|
|
})
|
|
}
|
|
})
|
|
|
|
const router = useRouter()
|
|
const dragRef = shallowRef(null)
|
|
const route = useRoute()
|
|
|
|
const isDragging = ref(false)
|
|
const startX = ref(0)
|
|
const startY = ref(0)
|
|
const left = ref(0)
|
|
const top = ref(0)
|
|
|
|
onMounted(() => {
|
|
const rect = dragRef.value.getBoundingClientRect()
|
|
left.value = window.innerWidth - rect.width - parseInt(props.initialPosition.right)
|
|
top.value = parseInt(props.initialPosition.top)
|
|
})
|
|
|
|
const style = computed(() => ({
|
|
left: left.value ? `${left.value}px` : 'auto',
|
|
top: top.value ? `${top.value}px` : 'auto',
|
|
right: !left.value ? props.initialPosition.right : 'auto',
|
|
transition: isDragging.value ? 'none' : 'all 0.3s ease',
|
|
}))
|
|
|
|
const handleDragStart = (event) => {
|
|
event.stopPropagation()
|
|
isDragging.value = true
|
|
|
|
const point = event.touches ? event.touches[0] : event
|
|
|
|
const rect = dragRef.value.getBoundingClientRect()
|
|
left.value = rect.left
|
|
top.value = rect.top
|
|
|
|
startX.value = point.clientX - left.value
|
|
startY.value = point.clientY - top.value
|
|
|
|
if (event.type === 'mousedown') {
|
|
document.addEventListener('mousemove', handleDragMove)
|
|
document.addEventListener('mouseup', handleDragEnd)
|
|
} else {
|
|
document.addEventListener('touchmove', handleDragMove, { passive: false })
|
|
document.addEventListener('touchend', handleDragEnd)
|
|
}
|
|
}
|
|
|
|
const handleDragMove = useThrottleFn((event) => {
|
|
if (!isDragging.value) return
|
|
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
|
|
const point = event.touches ? event.touches[0] : event
|
|
const rect = dragRef.value.getBoundingClientRect()
|
|
const maxX = window.innerWidth - rect.width
|
|
const maxY = window.innerHeight - rect.height
|
|
|
|
left.value = Math.min(Math.max(0, point.clientX - startX.value), maxX)
|
|
top.value = Math.min(Math.max(0, point.clientY - startY.value), maxY)
|
|
}, 16)
|
|
|
|
const handleEdgeSnap = useThrottleFn(() => {
|
|
const rect = dragRef.value.getBoundingClientRect()
|
|
const centerX = rect.left + rect.width / 2
|
|
|
|
left.value = centerX > window.innerWidth / 2
|
|
? window.innerWidth - rect.width - 16
|
|
: 16
|
|
}, 100)
|
|
|
|
const handleDragEnd = () => {
|
|
if (!isDragging.value) return
|
|
|
|
isDragging.value = false
|
|
handleEdgeSnap()
|
|
|
|
document.removeEventListener('mousemove', handleDragMove)
|
|
document.removeEventListener('mouseup', handleDragEnd)
|
|
document.removeEventListener('touchmove', handleDragMove)
|
|
document.removeEventListener('touchend', handleDragEnd)
|
|
}
|
|
|
|
const handleClick = (event) => {
|
|
event.stopPropagation()
|
|
if (!isDragging.value) {
|
|
handleReturnLive()
|
|
}
|
|
}
|
|
const handleReturnLive = () => {
|
|
props.onClick()
|
|
}
|
|
|
|
const handleClose = (event) => {
|
|
event?.stopPropagation()
|
|
props.onClose()
|
|
}
|
|
watch(() => route.path, (newVal) => {
|
|
if (['/','/home'].includes(newVal)){
|
|
handleClose()
|
|
}
|
|
})
|
|
onBeforeUnmount(() => {
|
|
document.removeEventListener('mousemove', handleDragMove)
|
|
document.removeEventListener('mouseup', handleDragEnd)
|
|
document.removeEventListener('touchmove', handleDragMove)
|
|
document.removeEventListener('touchend', handleDragEnd)
|
|
})
|
|
</script>
|
|
<style scoped>
|
|
.fixed {
|
|
will-change: transform, opacity;
|
|
touch-action: none;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
transform: translateZ(0);
|
|
}
|
|
|
|
.min-window-enter-active,
|
|
.min-window-leave-active {
|
|
transition: transform 0.3s ease, opacity 0.3s ease;
|
|
}
|
|
|
|
.min-window-enter-from,
|
|
.min-window-leave-to {
|
|
transform: translateY(100%);
|
|
opacity: 0;
|
|
}
|
|
|
|
img {
|
|
pointer-events: none;
|
|
-webkit-user-drag: none;
|
|
user-drag: none;
|
|
}
|
|
</style> |