右下角悬浮随意拖动banner组件封装
前言
在参加掘金1024活动时,注意到页面右下角的悬浮的banner,这种表现效果优于直接悬浮位置固定不变的方式,因为它可以随意拖动并改变位置。在以前的H5开发中,也遇到了类似的效果。因此决定将它抽离为一个单独的组件,防止以后重复搬砖。直接访问获取源码:https://gitee.com/fcli/vue_drag.git
先了解下相关知识
MouseEvent
当我们进行鼠标操作,比如mousedown、mousemove、mouseup时,能得到一个返回参数,从参数中我们能够得到鼠标相关数据信息。在这我简单对几个常用的参数进行介绍:
1、clientX、clientY:返回触发鼠标事件时,鼠标指针相对于当前窗口的水平和垂直坐标
2、offsetX、offsetY:返回鼠标指针相对于目标元素边缘位置的水平和垂直坐标。
3、pageX、pageY:回触发鼠标事件时鼠标指针相对于文档的水平和垂直坐标。
H5无法触发MouseEvent问题
1、当我们在移动端H5操作时,页面点击、拖动,无法触发mouse相关事件,此时我们需要通过TouchEvent来进行替换捕获相关事件,比如touchstart对应mousedown。
实现思路
1、首先需要初始化悬浮位置,需要设置fixed布局,以保证设置的位置是相对于浏览器窗口,不受其他样式影响。
.drag-content {
user-select: none;
touch-action: none;
cursor: pointer;
position: fixed;
}
2、在拖动时通过监听鼠标按下后位置的变化,动态改变悬浮Dom的位置,此处可使用两种方案:
(1)动态改变Dom距离浏览器left和bottom的位置
(2)使用transform改变dom的位置
我采用第二中方式,这样性能上更好一点。
3、保存最新位置,再下次刷新页面或者重新拖动时能够从上一次拖拽的位置重新计算移动距离。
//拖动中,动态改变
const moveTouch = (e: any) => {
if (isDragging) {
//h5 touch事件
let offsetX = e.touches[0].pageX - dragInfo.offsetX;
let offsetY = e.touches[0].pageY - dragInfo.offsetY;
//保存最新便宜量
dragInfo.endX = offsetX;
dragInfo.endY = offsetY;
dragStyle.value.transform = `translate(${offsetX}px,${offsetY}px)`
}
}
遇到的问题
在开发组件时候遇到一个问题,如果我将mousemove事件直接绑定在拖动的Dom上时会出现鼠标滑动太快导致拖拽物脱离鼠标,或者拖拽卡顿。解决方案:将mousemove事件挂在docment,而不是对应的element,此时鼠标滑动只要不出docment范围就不会触发上述情况。
源码
<template>
<div class="drag-content" :style="dragStyle" @mousedown="startTouch" @touchstart="startTouch"
@touchend="endTouch" @mouseup="endTouch" @touchmove="moveTouch">
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const props = defineProps({
right: {
type: String,
default: "20px"
},
bottom: {
type: String,
default: "30px"
}
})
//拖动样式
const 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
}
//拖拽开始
const startTouch = (e: any) => {
dragInfo.offsetX = (e.pageX || e.touches[0].pageX) - dragInfo.endX;
dragInfo.offsetY = (e.pageY || e.touches[0].pageY) - dragInfo.endY;
isDragging = true;
}
//适配pc端鼠标移动事件卡顿问题
document.onmousemove = (e)=> {
if (isDragging) {
let offsetX = e.pageX - dragInfo.offsetX;
let offsetY = e.pageY - dragInfo.offsetY;
dragInfo.endX = offsetX;
dragInfo.endY = offsetY;
dragStyle.value.transform = `translate(${offsetX}px,${offsetY}px)`
}
}
//拖动中,动态改变
const moveTouch = (e: any) => {
if (isDragging) {
//h5 touch事件
let offsetX = e.touches[0].pageX - dragInfo.offsetX;
let offsetY = e.touches[0].pageY - dragInfo.offsetY;
//保存最新便宜量
dragInfo.endX = offsetX;
dragInfo.endY = offsetY;
dragStyle.value.transform = `translate(${offsetX}px,${offsetY}px)`
}
}
//结束拖动
const endTouch = () => {
isDragging = false;
}
</script>
<style>
.drag-content {
user-select: none;
touch-action: none;
cursor: pointer;
position: fixed;
}
</style>
使用示例
npm install @fcli/vue-drag --save-dev 来安装
在项目中使用
import VerCode from '@fcli/vue-vercode';
const app=createApp(App)
app.use(VerCode);
<div class="content">
<vue-drag right="20px" bottom="30px">
<div class="circle">+</div>
</vue-drag>
</div>
参数说明
属性 | 属性名称 | 类型 | 可选值 |
---|---|---|---|
right | 初始化时距离右边的距离 | string | 20px |
bottom | 初始化时距离底部的距离 | string | 30px |
slot
可拖拽组件中的自定义内容