web端(PC与H5)实现 可拖拽的悬浮盒子

718 阅读2分钟

先上效果图

image.png

再了解下相关知识

当我们进行鼠标操作,比如mousedown、mousemove、mouseup时,能得到一个返回参数,从参数中我们能够得到鼠标相关数据信息。在这我简单对几个常用的参数进行介绍:

1、clientX、clientY:返回触发鼠标事件时,鼠标指针相对于当前窗口的水平和垂直坐标

2、offsetX、offsetY:返回鼠标指针相对于目标元素边缘位置的水平和垂直坐标。

3、pageX、pageY:回触发鼠标事件时鼠标指针相对于文档的水平和垂直坐标。

H5无法触发MouseEvent问题

当我们在移动端H5操作时,页面点击、拖动,无法触发mouse相关事件,此时我们需要通过TouchEvent来进行替换捕获相关事件,比如touchstart对应mousedown。

封装组件理由

在很多网站中都会有这么一个悬浮的导航栏/按钮 之类的东西,想要做一个固定的悬浮盒子并不难,直接一个固定定位即可完成需求,但是有时候会有奇葩的要求:需要让这个悬浮盒子可以拖动!

组件源码代码

HTML代码

<!-- 侧边导航栏 -->
		<div class="sidebar" :style="dragStyle" ref="dragBox">
			<div
				class="drag-box"
				@mousedown="startDrag"
				@touchstart="startDrag"
				@mouseup="endDrag"
				@touchend="endDrag"
				@touchmove="moveTouch"
			>
                        拖拽
			</div>
                  </div>

JS代码

let props = defineProps({ right: { type: String, default: "20px" }, bottom: { type: String, default: "20%" } })

//拖动样式
				let = dragStyle = ref({
					right: props.right,
                                        bottom: props.bottom,
					transform: 'translate(0,0)',
				})
				let = isDragging = false //是否拖动
				// 拖动信息
				let = dragInfo = {
					offsetX: 0,
					offsetY: 0,
					endX: 0,
					endY: 0,
				}
     
			//适配pc端鼠标移动事件卡顿问题
			document.onmousemove = (e) => {
				if (isDragging) {
                                        // 获取屏幕宽高
					let clientWidth = document.body.clientWidth;
					let clientHeight = document.body.clientHeight;
					let element = this.$refs.dragBox;
					// 获取元素宽高
					let elementWidth = element.offsetWidth;
					let elementHeight = element.offsetHeight;
                                
					let offsetX = e.pageX - dragInfo.offsetX;
					let offsetY = e.pageY - dragInfo.offsetY;
                                        
                                        // 如果拖动到屏幕外面,将拖动的位置设置为最后一次合法的位置
					if (
						e.pageX < 0 + elementWidth / 2 ||
						e.pageX > clientWidth - elementWidth / 2 ||
						e.pageY < 0 ||
						e.pageY > clientHeight - elementWidth
					) {
						dragStyle.value.transform = `translate(${dragInfo.endX}px,${dragInfo.endY}px)`;
						return;
					}
                                        
					dragInfo.endX = offsetX;
					dragInfo.endY = offsetY;
					dragStyle.value.transform = `translate(${offsetX}px,${offsetY}px)`;
				}
			}
                
             //拖拽开始
		function startDrag(e) {
			dragInfo.offsetX = (e.pageX || e.touches[0].pageX) - dragInfo.endX;
			dragInfo.offsetY = (e.pageY || e.touches[0].pageY) - dragInfo.endY;
			isDragging = true;
		},
			//拖动中,动态改变
			function moveTouch(e) {
                            if (isDragging) {
                                / 获取屏幕宽高
				let clientWidth = document.body.clientWidth;
				let clientHeight = document.body.clientHeight;
				let element = this.$refs.dragBox;
				// 获取元素宽高
				let elementWidth = element.offsetWidth;
				let elementHeight = element.offsetHeight;
                                     
				// h5 touch事件
				let offsetX = e.touches[0].pageX - dragInfo.offsetX;
				let offsetY = e.touches[0].pageY - dragInfo.offsetY;
                                
                                // 如果拖动到屏幕外面,将拖动的位置设置为最后一次合法的位置
					if (
					e.touches[0].pageX < 0 + elementWidth / 2 ||
					e.touches[0].pageX > clientWidth - elementWidth / 2 ||
					e.touches[0].pageY < 0 ||
					e.touches[0].pageY > clientHeight - elementWidth
				) {
						dragStyle.value.transform = `translate(${dragInfo.endX}px,${dragInfo.endY}px)`;
						return;
					}

				// 保存最新偏移量
				dragInfo.endX = offsetX;
				dragInfo.endY = offsetY;
				dragStyle.value.transform = `translate(${offsetX}px,${offsetY}px)`;
			}
			},
			//结束拖动
			function endDrag() {
				isDragging = false;
			},

CSS代码

.sidebar {
                        user-select: none; 
                        touch-action: none;
			cursor: pointer;
			position: fixed;
			z-index: 1000;
			background: #fff;
			box-shadow: 0px 0px 15px 0px rgba(76, 80, 81, 0.2);
			.drag-box {
				display: flex;
				justify-content: center;
				margin: 10px 0;
			}
                       }

为什么PC的鼠标移动事件不绑定到元素上,而是要绑定到DOM上 ? 因为将mousemove事件直接绑定在拖动的元素上时会出现鼠标滑动太快导致拖拽物脱离鼠标,或者拖拽卡顿。

解决方案:将mousemove事件挂在docment,而不是对应的element,此时鼠标滑动只要不出docment范围就不会触发上述情况。

页面使用

<drag-sidebar right="20px" bottom="30px"></drag-sidebar>
import dragSidebar from '../../components/dragSidebar.vue';

仅用作学习笔记,参考链接:juejin.cn/post/729444…