135 lines
4.0 KiB
Vue
135 lines
4.0 KiB
Vue
<template>
|
||
<!-- 使用 view 作为悬浮球容器,通过绑定 style 进行定位 -->
|
||
<view class="floating-ball" v-show="isShow"
|
||
:style="{ left: ballLeft + 'px', top: ballTop + 'px' }"
|
||
@touchstart="handleTouchStart"
|
||
@touchmove="handleTouchMove"
|
||
@touchend="handleTouchEnd"
|
||
@touchcancel="handleTouchEnd">
|
||
<image class="floating-ball-img" src="/static/index/caigouqingdan.png" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted,defineEmits } from 'vue';
|
||
const props = defineProps({
|
||
isShow: {
|
||
type: Boolean,
|
||
required: true,
|
||
},
|
||
});
|
||
const emit = defineEmits(['clickBall'])
|
||
// 定义悬浮球尺寸和长按阈值
|
||
const ballWidth = 60; // 悬浮球宽度,单位 px,与 CSS 中保持一致
|
||
const ballHeight = 60; // 悬浮球高度
|
||
const longPressThreshold = 300; // 长按时间阈值(毫秒)
|
||
|
||
// 悬浮球当前位置
|
||
const ballLeft = ref(1090);
|
||
const ballTop = ref(120);
|
||
|
||
// 用于判断是否进入拖动状态
|
||
const isDragging = ref(false);
|
||
|
||
// 记录初始按下时坐标以及悬浮球起始位置
|
||
let startTouchX = 0;
|
||
let startTouchY = 0;
|
||
let initialLeft = 0;
|
||
let initialTop = 0;
|
||
let longPressTimer = null;
|
||
|
||
// 屏幕尺寸
|
||
let windowWidth = 0;
|
||
let windowHeight = 0;
|
||
|
||
onMounted(() => {
|
||
// 获取系统信息,初始化屏幕宽高(uni-app API)
|
||
const res = uni.getSystemInfoSync();
|
||
windowWidth = res.windowWidth;
|
||
windowHeight = res.windowHeight;
|
||
});
|
||
|
||
// 触摸开始:记录初始位置,同时启动长按定时器
|
||
function handleTouchStart(e) {
|
||
const touch = e.touches[0];
|
||
// console.log("????0",touch.clientX )
|
||
startTouchX = touch.clientX.toFixed(2);
|
||
startTouchY = touch.clientY.toFixed(2);
|
||
initialLeft = ballLeft.value;
|
||
initialTop = ballTop.value;
|
||
|
||
// 设置定时器,达到长按后进入拖动状态
|
||
longPressTimer = setTimeout(() => {
|
||
isDragging.value = true;
|
||
}, longPressThreshold);
|
||
}
|
||
|
||
// 触摸移动:如果进入拖动状态,则计算新位置,同时限制悬浮球不超出屏幕边界
|
||
function handleTouchMove(e) {
|
||
|
||
// 如果尚未进入拖动状态,且手指移动距离较大,则可以提前进入拖动模式
|
||
if (!isDragging.value) {
|
||
const touch = e.touches[0];
|
||
// console.log("????0",touch.clientX )
|
||
const deltaX = Math.abs(touch.clientX.toFixed(2) - startTouchX);
|
||
const deltaY = Math.abs(touch.clientY.toFixed(2) - startTouchY);
|
||
if(deltaX > 5 || deltaY > 5){
|
||
clearTimeout(longPressTimer);
|
||
isDragging.value = true;
|
||
}
|
||
}
|
||
|
||
// 正在拖动,更新悬浮球位置
|
||
if(isDragging.value){
|
||
|
||
const touch = e.touches[0];
|
||
// console.log("????0",touch.clientX )
|
||
let newLeft = initialLeft + (touch.clientX.toFixed(2) - startTouchX);
|
||
let newTop = initialTop + (touch.clientY.toFixed(2) - startTouchY);
|
||
// 限制左右边界:不让超出屏幕(计算时需要考虑悬浮球自身尺寸)
|
||
newLeft = Math.max(0, Math.min(newLeft, windowWidth - ballWidth));
|
||
// 限制上下边界
|
||
newTop = Math.max(0, Math.min(newTop, windowHeight - ballHeight));
|
||
|
||
ballLeft.value = newLeft;
|
||
ballTop.value = newTop;
|
||
}
|
||
}
|
||
|
||
// 触摸结束或取消:若未进入拖动状态则视为点击,触发点击处理方法;否则结束拖动状态
|
||
function handleTouchEnd(e) {
|
||
clearTimeout(longPressTimer);
|
||
if(isDragging.value){
|
||
// 拖动结束后重置状态
|
||
isDragging.value = false;
|
||
} else {
|
||
// 非拖动状态下,触发点击事件
|
||
triggerClick();
|
||
}
|
||
}
|
||
// 点击事件处理方法
|
||
function triggerClick() {
|
||
emit('clickBall')
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.floating-ball {
|
||
position: fixed;
|
||
width: 140rpx;
|
||
height: 140rpx;
|
||
border-radius: 50%;
|
||
background: linear-gradient(to bottom right,#3FBBFE,#A541FF);
|
||
border: 2rpx solid #fff;
|
||
z-index: 999;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
/* 可根据需要添加阴影或其他样式 */
|
||
.floating-ball-img{
|
||
width: 70rpx;
|
||
height: 70rpx;
|
||
}
|
||
}
|
||
</style>
|