mirror of
http://36.133.248.69:3088/admin/RentWeAppFront.git
synced 2026-06-07 06:22:27 +08:00
236 lines
4.8 KiB
Vue
236 lines
4.8 KiB
Vue
|
|
<template>
|
|||
|
|
<!-- 唯一根节点 → 修复报错 -->
|
|||
|
|
<view class="float-guide-wrapper">
|
|||
|
|
<!-- 纯JS丝滑悬浮球 -->
|
|||
|
|
<view
|
|||
|
|
class="float-ball"
|
|||
|
|
:style="{
|
|||
|
|
left: x + 'px',
|
|||
|
|
top: y + 'px',
|
|||
|
|
transition: isAnimating ? 'all 0.2s ease-out' : 'none'
|
|||
|
|
}"
|
|||
|
|
@touchstart="onStart"
|
|||
|
|
@touchmove.stop.prevent="onMove"
|
|||
|
|
@touchend="onEnd"
|
|||
|
|
@click="handleBallClick"
|
|||
|
|
>
|
|||
|
|
<!-- 展开状态:箭头在左,文字在右 -->
|
|||
|
|
<view class="ball-content" v-if="!isFolded">
|
|||
|
|
<view class="fold-btn" @click.stop="fold">
|
|||
|
|
<text class="fold-icon">›</text>
|
|||
|
|
</view>
|
|||
|
|
<view class="ball-main">
|
|||
|
|
<text class="text">使用教程</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 收起状态 -->
|
|||
|
|
<view class="ball-folded" v-else>
|
|||
|
|
<text class="text">展开</text>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 视频弹窗 -->
|
|||
|
|
<view class="video-mask" v-if="showVideoPlayer" @click="closeVideo">
|
|||
|
|
<view class="video-wrapper" @click.stop>
|
|||
|
|
<view class="close-btn" @click="closeVideo">×</view>
|
|||
|
|
<video :src="videoSrc" class="guide-video" controls loop show-progress="false"
|
|||
|
|
object-fit="contain"></video>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script>
|
|||
|
|
export default {
|
|||
|
|
name: "floatGuide",
|
|||
|
|
props: {
|
|||
|
|
videoSrc: {
|
|||
|
|
type: String,
|
|||
|
|
default: "/static/guide.mp4"
|
|||
|
|
},
|
|||
|
|
initX: {
|
|||
|
|
type: Number,
|
|||
|
|
default: null
|
|||
|
|
},
|
|||
|
|
initY: {
|
|||
|
|
type: Number,
|
|||
|
|
default: null
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
data() {
|
|||
|
|
return {
|
|||
|
|
x: 0,
|
|||
|
|
y: 0,
|
|||
|
|
isFolded: false,
|
|||
|
|
showVideoPlayer: false,
|
|||
|
|
isAnimating: false,
|
|||
|
|
startX: 0,
|
|||
|
|
startY: 0,
|
|||
|
|
pageWidth: 0,
|
|||
|
|
pageHeight: 0
|
|||
|
|
};
|
|||
|
|
},
|
|||
|
|
mounted() {
|
|||
|
|
this.initPosition();
|
|||
|
|
},
|
|||
|
|
methods: {
|
|||
|
|
initPosition() {
|
|||
|
|
uni.getSystemInfo({
|
|||
|
|
success: (res) => {
|
|||
|
|
this.pageWidth = res.windowWidth;
|
|||
|
|
this.pageHeight = res.windowHeight;
|
|||
|
|
if (this.initX !== null && this.initY !== null) {
|
|||
|
|
this.x = this.initX;
|
|||
|
|
this.y = this.initY;
|
|||
|
|
} else {
|
|||
|
|
this.x = this.pageWidth - 160;
|
|||
|
|
this.y = this.pageHeight - 260;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
},
|
|||
|
|
onStart(e) {
|
|||
|
|
const tx = e.touches[0].clientX;
|
|||
|
|
const ty = e.touches[0].clientY;
|
|||
|
|
this.startX = tx - this.x;
|
|||
|
|
this.startY = ty - this.y;
|
|||
|
|
this.isAnimating = false;
|
|||
|
|
},
|
|||
|
|
onMove(e) {
|
|||
|
|
const tx = e.touches[0].clientX;
|
|||
|
|
const ty = e.touches[0].clientY;
|
|||
|
|
let newX = tx - this.startX;
|
|||
|
|
let newY = ty - this.startY;
|
|||
|
|
|
|||
|
|
newX = Math.max(10, Math.min(newX, this.pageWidth - 80));
|
|||
|
|
newY = Math.max(40, Math.min(newY, this.pageHeight - 100));
|
|||
|
|
|
|||
|
|
this.x = newX;
|
|||
|
|
this.y = newY;
|
|||
|
|
},
|
|||
|
|
onEnd() {
|
|||
|
|
this.isAnimating = true;
|
|||
|
|
},
|
|||
|
|
handleBallClick() {
|
|||
|
|
if (this.isFolded) {
|
|||
|
|
this.isFolded = false;
|
|||
|
|
} else {
|
|||
|
|
this.showVideoPlayer = true;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
fold() {
|
|||
|
|
this.isFolded = true;
|
|||
|
|
},
|
|||
|
|
closeVideo() {
|
|||
|
|
this.showVideoPlayer = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.float-guide-wrapper {
|
|||
|
|
display: block;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.float-ball {
|
|||
|
|
position: fixed;
|
|||
|
|
z-index: 99999;
|
|||
|
|
user-select: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* 展开样式:箭头左,文字右,正常顺序 */
|
|||
|
|
.ball-content {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
background: linear-gradient(90deg, #ff2f31 0%, #ff9379 100%);
|
|||
|
|
border-radius: 40rpx;
|
|||
|
|
height: 80rpx;
|
|||
|
|
padding: 0 20rpx;
|
|||
|
|
box-shadow: 0 6rpx 20rpx rgba(255, 60, 60, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ball-main {
|
|||
|
|
padding: 0 10rpx;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ball-content .text {
|
|||
|
|
color: #fff;
|
|||
|
|
font-size: 26rpx;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.fold-btn {
|
|||
|
|
width: 44rpx;
|
|||
|
|
height: 44rpx;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
background: rgba(255, 255, 255, 0.2);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.fold-icon {
|
|||
|
|
color: #fff;
|
|||
|
|
font-size: 28rpx;
|
|||
|
|
font-weight: bold;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ball-folded {
|
|||
|
|
width: 80rpx;
|
|||
|
|
height: 80rpx;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
background: linear-gradient(90deg, #ff2f31 0%, #ff9379 100%);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
box-shadow: 0 6rpx 20rpx rgba(255, 60, 60, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.ball-folded .text {
|
|||
|
|
color: #fff;
|
|||
|
|
font-size: 24rpx;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.video-mask {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
bottom: 0;
|
|||
|
|
background: rgba(0, 0, 0, 0.7);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
z-index: 999999;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.video-wrapper {
|
|||
|
|
position: relative;
|
|||
|
|
width: 90%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.guide-video {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 420rpx; /* 必须加高度! */
|
|||
|
|
border-radius: 20rpx;
|
|||
|
|
background-color: #000; /* 防止加载瞬间闪白 */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.close-btn {
|
|||
|
|
position: absolute;
|
|||
|
|
top: -88rpx;
|
|||
|
|
right: 0;
|
|||
|
|
width: 64rpx;
|
|||
|
|
height: 64rpx;
|
|||
|
|
background: rgba(255, 255, 255, 0.2);
|
|||
|
|
color: #fff;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
font-size: 36rpx;
|
|||
|
|
}
|
|||
|
|
</style>
|