当前位置: 首页 > news >正文

uniapp实现全局拖拽按钮

要先引入 “vue3-draggable-resizable”: “^1.6.5”

1.创建DragComponent组件

<template>
	<!-- 抽屉组件 -->
	<div class="drag-container" id="dragBox" :style="{ zIndex: zIndex }">
		<Vue3DraggableResizable  :initW="64" :initH="64" v-model:x="x" v-model:y="y"
			:parent="false" :resizable="false" :draggable="true" class="drag-item" @drag-start="handleDragStart"
			@drag-end="handleDragEnd">
			<div class="home-icon-box" ref="draggableElement">
				<img :src="market_set.image" class="img" alt="" />
			</div>
		</Vue3DraggableResizable>
	</div>
</template>

<script setup>
	import {
		ref,
		onMounted,
		onUnmounted
	} from 'vue';
	import Vue3DraggableResizable from 'vue3-draggable-resizable';
	import 'vue3-draggable-resizable/dist/Vue3DraggableResizable.css';
	import {
		getMarket
	} from "@/api/global.js"

	const popupRef = ref(null)

	const x = ref(20);
	const y = ref();

	const initialY = ref(0); // 基准Y坐标,用于计算滚动偏移
	const isDragging = ref(false); //是否正在拖拽
	const dragStartPosition = ref({
		x: 0,
		y: 0
	}); // 拖拽起始位置
	const isLock = ref(true);
	const isShow = ref(false)

	// 初始化基准Y坐标
	const initBaseY = () => {
		initialY.value = y.value - window.scrollY;

	};

	function isElementInViewport(el) {
		const rect = el.getBoundingClientRect();

		// 获取视口的高度和宽度
		const windowHeight = (window.innerHeight || document.documentElement.clientHeight) - 50;
		const windowWidth = (window.innerWidth || document.documentElement.clientWidth);

		// 判断元素是否在视口内
		const vertInView = (rect.top <= windowHeight) && ((rect.top + rect.height) >= 0);
		const horInView = (rect.left <= windowWidth) && ((rect.left + rect.width) >= 0);

		return (vertInView && horInView);
	}

	// 拖动结束后更新基准Y坐标
	const handleDragEnd = (e) => {
		// initialY.value = y.value - window.scrollY;
		// 判断拖拽还是点击事件
		if (isDragging.value) {
			const dragDistance = Math.sqrt(
				Math.pow(e.x - dragStartPosition.value.x, 2) +
				Math.pow(e.y - dragStartPosition.value.y, 2)
			);
			if (dragDistance < 5) {
				// 如果拖拽距离小于 5px,则认为是点击事件
				console.log('点击事件', e);
				isLock.value = true
			} else {
				console.log('拖拽结束', e);
				isLock.value = false
			}
		}
		isDragging.value = false;
		// 判断悬浮球是靠近左边或右边
		const {
			x: newX,
			y: newY
		} = e; // 假设位置属性是 x 和 
		const page = document.querySelector('#app');
		const iconBox = document.querySelector('.home-icon-box');
		// alert(page.offsetWidth)

		if (newX + (iconBox.offsetWidth / 2) < page.offsetWidth / 2) {
			x.value = 0; // 靠左
		} else {
			x.value = page.offsetWidth - 64; // 靠右
		}


		const myElement = document.querySelector('.home-icon-box');
		if (isElementInViewport(myElement)) {
			console.log('元素在视口内');
		} else {
			console.log('元素不在视口内');
			y.value = window.innerHeight / 2
		}
	};

	const handleDragStart = (e) => {
		// 可根据需要添加拖动开始时的逻辑
		isDragging.value = true;
		dragStartPosition.value = {
			x: e.x,
			y: e.y
		};
	};
	

	// 计算按钮初始y位置
	const containerWidth = ref(0); // 容器宽度
	const width = ref(100); // 组件宽度
	// 计算初始 x 坐标(从右对齐)
	const calculateInitialX = () => {
		x.value = containerWidth.value - width.value;
		y.value = (window.innerHeight || document.documentElement.clientHeight) - 200
	};

	// 生命周期
	onMounted(() => {
		initBaseY();
		const container = document.querySelector('#app');
		if (container) {
			containerWidth.value = container.offsetWidth; //活动区域的宽度范围
			calculateInitialX();
		}

	})
</script>

<style lang="scss" scoped>
	.dragBox {
		width: 960rpx;
		height: 100vh;
		position: fixed;
		top: 0;
		left: 50%;
		z-index: 99;
		transform: translateX(-50%);
		pointer-events: none;
		color: red;
	}

	.home-icon-box {
		position: relative;
		// left: 23px;
		// top: 23px;
		width: 128rpx;
		height: 128rpx;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 999;
		color: #fff;
		cursor: grab;
		/* 鼠标样式为抓取 */
		user-select: none;
		/* 防止拖拽时选中文本 */
		animation: pulse 1.5s infinite ease-in-out;
		transition: opacity 0.3s ease, left 0.3s ease, right 0.3s ease, top 0.3s ease;

		/* 按钮动画 */
		@keyframes pulse {
			0% {
				transform: scale(1);
				opacity: 1;
			}

			50% {
				transform: scale(1.2);
				opacity: 0.7;
			}

			100% {
				transform: scale(1);
				opacity: 1;
			}
		}

		/* 进度条容器 */
		.progress-container-icon {
			position: absolute;
			width: 80%;
			height: 10px;
			background: rgba(255, 255, 255, 0.5);
			border-radius: 5px;
			overflow: hidden;
			text-align: center;
			cursor: pointer;
			bottom: -14px;
		}

		/* 进度条 */
		.progress-bar-icon {
			width: 0%;
			height: 100%;
			background: #ff5722;
			border-radius: 5px;
		}

		/* 进度文本 */
		.progress-text-icon {
			position: absolute;
			width: 100%;
			text-align: center;
			top: 50%;
			transform: translateY(-50%);
			color: black;
			font-size: 10px;
			font-weight: bold;
		}

		.img {
			width: 100%;
			height: 100%;
		}
	}

	.drag-item {
		width: 64px;
		height: 64px !important;
		touch-action: none;
		border: none !important;
	}

	.drag-container {
		display: inline-block;
		position: fixed;
		top: 0;
		// z-index: 99;
	}

	.dragBoxPopup {
		position: fixed;
		top: 0;
		left: calc(50% - min(50%, 240px));
		width: min(100%, 480px);
		z-index: 999;
		height: 100%;
		background-color: rgba(0, 0, 0, 0.7);
	}

	}
</style>

2.createDrag.js 将组件dom创建到app根组件下

import { createVNode, render } from 'vue';
import DragComponent from '@/components/DragComponent/DragComponent.vue';

// 修改后的函数,负责渲染 DragComponent 和 DragComponent2
export default function createDrags() {


  // 创建一个组件的 DOM 容器
  const container1 = document.createElement('div');
  document.getElementById('app').appendChild(container1);

  // 创建一个组件的虚拟节点
  const vnode1 = createVNode(DragComponent, {
    // 如果需要,可以在这里传递其他 props 给 DragComponent
  });


  // 渲染一个组件
  render(vnode1, container1);
  const instance1 = vnode1.component?.proxy;

  // 返回两个组件的实例(如果需要后续操作)
  return {
    dragComponentInstance: instance1
  };
}

在App.vue中执行js

	import createDrag from "@/utils/createDrag.js"

    onLaunch: function(options) {
		createDrag()
	}

相关文章:

  • 力扣算法Hot100——75. 颜色分类
  • GaussDB 资源管理指南:冻结、解冻、释放与生命周期控制
  • Node.js 中使用 RabbitMQ
  • 【Golang】go如何通过atomic原子操作来确保数据一致性
  • AFFiNE:下一代开源全能知识库工具,重新定义协作与创作
  • 深入理解JVM类加载机制:从原理到实践
  • 拓扑排序——117. 软件构建
  • 持续升级的电子实验记录本系统,更加好用、安全
  • 数据操作 + 数据预处理
  • JavaScript中的闭包:解锁函数的神秘力量
  • Linux--软硬链接、动静态库
  • Javascript 中事件环以及宏任务微任务详细介绍
  • VS2019 快捷键及各项功能汇总
  • 【GNN】0.环境配置
  • 【Pandas】pandas Index str
  • Quartus + VScode 实现模块化流水灯
  • 【Dive Into Stable Diffusion v3.5】1:开源项目正式发布——深入探索SDv3.5模型全参/LoRA/RLHF训练
  • DAPO:一个开源的大规模大型语言模型LLM强化学习系统
  • 案例驱动的 IT 团队管理:创新与突破之路: 第四章 危机应对:从风险预见到创新破局-4.1.1案例:某金融系统“重构生死战“
  • JAVA-多线程join()等待一个线程
  • 牛市早报|4月新增社融1.16万亿,降准今日正式落地
  • 消息人士称泽连斯基已启程前往土耳其
  • 万科再获深铁集团借款,今年已累计获股东借款近120亿元
  • 总没胃口,一吃就饱……别羡慕,也可能是生病了
  • 体验中国传统文化、采购非遗文创,波兰游客走进上海市群艺馆
  • 警方通报男子地铁上拍视频致乘客恐慌受伤:列车运行一度延误,已行拘