让元素实现拖动指定部位或者随意部位实现整个元素拖拽功能
tips: 如果项目版本过低,不支持 ?? 或者 ?. 语法的,可以自行替换(将?删除使用&&判断,将??替换为||)
代码部分 (如果有什么地方写的不好 欢迎大家指正)
const isDom = obj => {
if (!obj) return false
return typeof obj === 'object' ? obj instanceof HTMLElement : false
}
/*
* options: {
* dom: HTMLElement 需要移动的元素
* drag: HTMLElement 可拖动元素
* start: Function 开始拖动触发
* moveIng: Function 拖动中触发
* end: Function 拖动结束触发
* maxMoveDistance: Number 元素移动到最边上所暴露的最大宽度
* }
* */
export default function move (options = {}) {
if (Object.keys(options).length === 0) throw new Error('请传入正确的参数')
// drag 如果没有值,则默认dom为可以拖动元素
const drag = options?.drag ?? options?.dom
if (!isDom(drag)) {
throw new Error(`${options?.drag} or ${options?.dom} is not HTMLElement`)
}
// NOTE:如果需要拖拽,要将父元素设置为绝对定位
const parent = drag.parentElement ?? document.body
// 这里只做position判断,也可以加其他判断
const position = window.getComputedStyle(parent).getPropertyValue('position')
if (position !== 'absolute') {
parent.style.position = 'absolute'
}
const maxMoveDistance = options?.maxMoveDistance ?? 40
// 挂载到元素身上为了方便可以移除事件句柄
drag.moveHandler = e => {
if (e.which === 1) {
// 因为有选中内容会导致拖动有bug,所以每次点击的时候把页面选中的状态取消
document?.selection?.empty || window.getSelection?.().removeAllRanges?.()
typeof options.start === 'function' && options.start(e)
const startX = e.clientX
const startY = e.clientY
const left = options.dom.offsetLeft
const top = options.dom.offsetTop
// 最大可移动区域
const maxTop = document.body.clientHeight
const maxLeft = document.body.clientWidth
// 移动的距离
let distance = {x: 0, y: 0}
document.onmousemove = ev => {
// 移动的距离
let x = ev.clientX - startX + left
let y = ev.clientY - startY + top
if ((x + maxMoveDistance) > maxLeft) {
x = maxLeft - maxMoveDistance
} else if (x < 0 && Math.abs((x - maxMoveDistance)) > options.dom.offsetWidth) {
x = -(options.dom.offsetWidth - maxMoveDistance)
}
if ((y + maxMoveDistance) > maxTop) {
y = maxTop - maxMoveDistance
} else if (y <= 0) {
y = 0
}
distance.x = x - left
distance.y = y - top
// 只要鼠标移动距离大于8才会开始移动元素,要不然没有必要
if (Math.abs(distance.x) > 8 || Math.abs(distance.y) > 8) {
options.dom.style.left = x + 'px'
options.dom.style.top = y + 'px'
typeof options.moveIng === 'function' && options.moveIng(x, y, distance)
}
}
document.onmouseup = e => {
typeof options.end === 'function' && options.end(e, distance)
document.onmousemove = document.onmouseup = null
}
}
}
drag.addEventListener('mousedown', drag.moveHandler, false)
return drag
}
使用
<template>
<div class="example-container">
<div class="content" ref="parent">
<div class="title" ref="title"></div>
<div class="body"></div>
</div>
</div>
</template>
<script>
import move from '@/utils/move/index'
export default {
name: 'example',
mounted () {
const self = this
move({
dom: self.$refs?.parent,
drag: self.$refs?.title
})
}
}
</script>
<style lang="scss" scoped>
.example-container {
height: 100%;
width: 100%;
.content {
height: 300px;
width: 300px;
background-color: #67C23A;
position: absolute;
.title {
background-color: #409eff;
height: 50px;
}
}
}
</style>