Uniapp全局显示 悬浮组件/无需单页面引入

一、创建一个悬浮组件,代码示例如下,根据自己情况而定。

html 复制代码
<template>
	<view class="hover-ball" :style="ballStyle" @touchstart="handleTouchStart" @touchmove="handleTouchMove"
		@touchend="handleTouchEnd" @click="handleClick">
		<slot>
			<text class="ball-text">+</text>
		</slot>
	</view>
</template>

<script>
	export default {
		name: "HoverBall",
		props: {
			initPosition: {
				type: Object,
				default: () => ({
					x: 30,
					y: 200
				})
			},
			size: {
				type: Number,
				default: 80
			},
			bgColor: {
				type: String,
				default: "#409eff"
			},
			show: {
				type: Boolean,
				default: true
			}
		},
		data() {
			return {
				ballX: 0,
				ballY: 0,
				startX: 0,
				startY: 0,
				screenWidth: 0,
				screenHeight: 0,
				isDragging: false
			};
		},
		computed: {
			ballStyle() {
				if (!this.show) return {
					display: 'none'
				};
				return {
					left: `${this.ballX}px`,
					top: `${this.ballY}px`,
					width: `${this.size}px`,
					height: `${this.size}px`,
					backgroundColor: this.bgColor
				};
			}
		},
		mounted() {
			const sysInfo = uni.getSystemInfoSync();
			this.screenWidth = sysInfo.windowWidth;
			this.screenHeight = sysInfo.windowHeight;
			this.ballX = this.initPosition.x;
			this.ballY = this.initPosition.y;
			// 从本地恢复位置(可选)
			const savedPos = uni.getStorageSync('hoverBallPos');
			if (savedPos) {
				this.ballX = savedPos.x;
				this.ballY = savedPos.y;
			}
			uni.onWindowResize(() => {
				const sysInfo = uni.getSystemInfoSync();
				this.screenWidth = sysInfo.windowWidth;
				this.screenHeight = sysInfo.windowHeight;
			});
		},
		methods: {
			handleTouchStart(e) {
				this.isDragging = true;
				this.startX = e.touches[0].clientX;
				this.startY = e.touches[0].clientY;
				this.startBallX = this.ballX;
				this.startBallY = this.ballY;
			},
			handleTouchMove(e) {
				if (!this.isDragging) return;
				const dx = e.touches[0].clientX - this.startX;
				const dy = e.touches[0].clientY - this.startY;
				let newX = this.startBallX + dx;
				let newY = this.startBallY + dy;
				newX = Math.max(0, Math.min(newX, this.screenWidth - this.size));
				newY = Math.max(0, Math.min(newY, this.screenHeight - this.size - 100));
				this.ballX = newX;
				this.ballY = newY;
			},
			handleTouchEnd() {
				this.isDragging = false;
				const centerX = this.screenWidth / 2;
				const margin = 10;
				this.ballX = this.ballX < centerX ? margin : this.screenWidth - this.size - margin;
				this.$emit('position-change', {
					x: this.ballX,
					y: this.ballY
				});
			},
			handleClick() {
				console.log('悬浮球打印 - "778899665544112233"')
				if (!this.isDragging) {
					this.$emit('click');
				}
			}
		}
	};
</script>

<style scoped>
	.hover-ball {
	  position: fixed !important; /* 强制fixed定位,不受父元素影响 */
	  border-radius: 50%;
	  display: flex;
	  align-items: center;
	  justify-content: center;
	  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
	  z-index: 9999999 !important; /* 足够高的层级,覆盖所有页面内容 */
	  touch-action: none;
	  transition: left 0.3s ease, top 0.3s ease;
	  pointer-events: auto; /* 确保能点击,不被穿透 */
	}
	
	.ball-text {
	  color: white;
	  font-size: 24px;
	  font-weight: bold;
	}
</style>

二、项目终端 使用npm下载 vue-inset-loader /或/ vite-inset-loader 根据自己项目下载

html 复制代码
npm i vue-inset-loader

npm i Vite-inset-loader

三、在main.js中悬浮组件进行全局注册 示例内容如下

html 复制代码
//导入悬浮球全局注册
import HoverBall from '@/components/HoverBall.vue'

Vue.component("HoverBall",HoverBall); 

四、配置Pages.json

html 复制代码
"uniIdRouter": {},
//在以下写入
"insetLoader": {
		"config": {
			"confirm": "<HoverBall></HoverBall>"
		},
		"label": ["confirm"],
		"rootEle":"view",

        //以下内容可选是否需要
		"package": {
			"label": "view",
			"options": {
				"class": "dev-style",
				"style": {
					"font-size": "24px"
				},
				"data-attr": "content"
			}
		}
	}

重点: 之哟啊关心insetLoader这个对象就行,以上步骤执行后 项目重启还是没有显示组件,注册你显示的悬浮组件是 "div" 还是 "view", 如果是 div: 那么"rootEle":"view"要更改为"rootEle":"div" 很重要!!!。

如果你的代码根元素 有 div 也有 view 请把rootEle设置为 ".*" 👇 。

html 复制代码
"rootEle": ".*"

五、配置vue.config.js / Vite.config.js

html 复制代码
// 解决跨域问题
import {
	defineConfig
} from "vite";
import uni from "@dcloudio/vite-plugin-uni";

//导入统一得基础URL配置
import {
	BASE_API_URL
} from "./common/baseUrl.js";
import {
	UniViteInsetLoader
} from "vite-inset-loader";

const components = {
	PickupDialog: '<PickupDialog ref="PickupDialogRef" />',
	confirm: '<HoverBall></HoverBall>'
};

export default defineConfig({
	plugins: [UniViteInsetLoader({
		include: "pages"
	}), uni()],
	server: {
		proxy: {
			"/api": {
				// 代理路径
				target: BASE_API_URL,
				changeOrigin: true, // 是否换源
				rewrite: (path) => path.replace(/^\/api/, ""), // 重写路径
			},
		},
	},
});

完成以上步骤 记得重启项目!!! 否则不会生效!!!

相关推荐
打小就很皮...10 分钟前
Tesseract.js OCR 中文识别
前端·react.js·ocr
qq_1777673720 分钟前
React Native鸿蒙跨平台实现应用介绍页,实现了应用信息卡片展示、特色功能网格布局、权限/联系信息陈列、评分展示、模态框详情交互等通用场景
javascript·react native·react.js·ecmascript·交互·harmonyos
2603_9494621025 分钟前
Flutter for OpenHarmony社团管理App实战:预算管理实现
android·javascript·flutter
wuhen_n31 分钟前
JavaScript内存管理与执行上下文
前端·javascript
Hi_kenyon1 小时前
理解vue中的ref
前端·javascript·vue.js
jin1233222 小时前
基于React Native鸿蒙跨平台地址管理是许多电商、外卖、物流等应用的重要功能模块,实现了地址的添加、编辑、删除和设置默认等功能
javascript·react native·react.js·ecmascript·harmonyos
2501_920931702 小时前
React Native鸿蒙跨平台医疗健康类的血压记录,包括收缩压、舒张压、心率、日期、时间、备注和状态
javascript·react native·react.js·ecmascript·harmonyos
落霞的思绪2 小时前
配置React和React-dom为CDN引入
前端·react.js·前端框架
Hacker_Z&Q2 小时前
CSS 笔记2 (属性)
前端·css·笔记
Anastasiozzzz2 小时前
LeetCode Hot100 295. 数据流的中位数 MedianFinder
java·服务器·前端