Files
RentWeAppFront/components/floatGuide/floatGuide.vue
2026-06-02 16:23:56 +08:00

236 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>