悬浮按钮支持可拖拽。可移动。
javascript
<floatBtn :disabled="['CM1100','CM2100','CM1400','CM2400','CM1505','CM2505','CM1502','CM2502','CM3140'].indexOf(formData.oper)==-1">
<view @click="floatBtnCall" class="clof p5_10">采集<br/>参数</view>
</floatBtn>
代码:
javascript
<template>
<view>
<view class="topBtn" :class="{ 'disabled': disabled }" :style="{
left: `${position.x}px`,
top: `${position.y}px`
}" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd">
<slot></slot>
</view>
</view>
</template>
<script>
export default {
props: {
// 控制按钮功能是否可用(点击/置顶)
disabled: {
type: Boolean,
default: false,
},
// 控制是否允许拖拽位置(与 disabled 无关)
draggable: {
type: Boolean,
default: true,
}
},
data() {
return {
isFirstClick: true,
hideTimer: null,
// 拖拽相关
startTouch: {
x: 0,
y: 0
},
startPosition: {
x: 0,
y: 0
},
position: {
x: 0,
y: 0
},
isDragging: false,
dragThreshold: 5, // px,小于该值视为点击
};
},
mounted() {
this.initPosition();
},
beforeDestroy() {
if (this.hideTimer) clearTimeout(this.hideTimer);
},
methods: {
initPosition() {
const sys = uni.getSystemInfoSync();
const rpx2px = (rpx) => (rpx * sys.windowWidth) / 750;
const btnSize = rpx2px(100); // 按钮宽高 100rpx
const margin = rpx2px(30); // 右边距 30rpx
// 初始位置:right: 30rpx, bottom: 30vh
const x = sys.windowWidth - btnSize - margin;
const y = sys.windowHeight * 0.7 - btnSize; // 30vh from bottom → top = 70vh - height
this.position = {
x: Math.round(x),
y: Math.round(y)
};
},
onTouchStart(e) {
if (!this.draggable) return; // 👈 仅由 draggable 控制
const touch = e.touches[0];
this.startTouch.x = touch.clientX;
this.startTouch.y = touch.clientY;
this.startPosition = {
...this.position
};
this.isDragging = false;
},
onTouchMove(e) {
if (!this.draggable) return;
const touch = e.touches[0];
const deltaX = touch.clientX - this.startTouch.x;
const deltaY = touch.clientY - this.startTouch.y;
const sys = uni.getSystemInfoSync();
const btnSize = (100 * sys.windowWidth) / 750;
let newX = this.startPosition.x + deltaX;
let newY = this.startPosition.y + deltaY;
// 限制在屏幕可视区内
newX = Math.max(0, Math.min(sys.windowWidth - btnSize, newX));
newY = Math.max(0, Math.min(sys.windowHeight - btnSize, newY));
this.position = {
x: newX,
y: newY
};
this.isDragging = true;
},
onTouchEnd(e) {
if (!this.draggable) return;
const touch = e.changedTouches[0];
const dx = touch.clientX - this.startTouch.x;
const dy = touch.clientY - this.startTouch.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// 如果移动很小,视为点击
if (!this.isDragging || distance < this.dragThreshold) {
this.handleClick();
}
},
handleClick() {
if (this.disabled) return; // 👈 仅由 disabled 控制功能
this.$emit('click');
if (this.hideTimer) clearTimeout(this.hideTimer);
if (this.isFirstClick) {
this.isFirstClick = false;
this.hideTimer = setTimeout(() => {
this.isFirstClick = true;
}, 5000);
} else {
uni.pageScrollTo({
scrollTop: 0,
duration: 300,
success: () => {
setTimeout(() => {
this.isFirstClick = true;
}, 500);
}
});
}
}
}
};
</script>
<style lang="scss" scoped>
.topBtn {
position: fixed !important;
z-index: 9999 !important;
width: 100rpx !important;
height: 100rpx !important;
background: #3c9cff !important;
border-radius: 50% !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
box-shadow: 0 4rpx 12rpx rgba(60, 156, 255, 0.6) !important;
cursor: pointer !important;
user-select: none !important;
touch-action: none !important;
}
.topBtn.disabled {
opacity: 0.5 !important;
/* 注意:不要加 pointer-events: none,
否则 draggable=true 时也无法拖拽! */
}
</style>