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/, ""), // 重写路径
			},
		},
	},
});

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

相关推荐
前端工作日常16 小时前
我学习到的A2UI的功能:纯粹的UI生成
前端
Jing_Rainbow16 小时前
【 前端三剑客-37 /Lesson61(2025-12-09)】JavaScript 内存机制与执行原理详解🧠
前端·javascript·程序员
UIUV16 小时前
模块化CSS学习笔记:从作用域问题到实战解决方案
前端·javascript·react.js
aoi16 小时前
解决 Vue 2 大数据量表单首次交互卡顿 10s 的性能问题
前端·vue.js
Kakarotto16 小时前
使用ThreeJS绘制东方明珠塔模型
前端·javascript·vue.js
donecoding16 小时前
TypeScript `satisfies` 的核心价值:两个例子讲清楚
前端·javascript
德育处主任16 小时前
『NAS』在群晖部署一个文件加密工具-hat.sh
前端·算法·docker
cup11316 小时前
【原生 JS】支持加密的浏览器端 BYOK AI SDK,助力 Vibe Coding
前端
Van_Moonlight16 小时前
RN for OpenHarmony 实战 TodoList 项目:顶部导航栏
javascript·开源·harmonyos
技术狂小子16 小时前
前端开发中那些看似微不足道却影响体验的细节
javascript