【sgDragMove】自定义拖拽组件,仅支持拖拽、设置吸附屏幕边界距离
特性:
- 支持拖拽元素与被拖拽元素可以分离
- 可自定义吸附边缘的距离,设置为0即可实现不超出界面停靠边界
- 拖拽过程,物体有半透明效果
- 拖拽的过程中可以通过disabled设置中断拖拽行为
- 吸附边缘的不同方向距离可以自定义,值可以是number或array类型,例如:是5代表[5,5,5,5],其中数组以此代表[上,右,下,左]
- 可以设置拖拽物体停靠距离边界的像素距离,值可以是number或array类型(规律同上)
sgDragMove源码
<template> <div :class="$options.name"></div> </template> <script> export default { name: 'sgDragMove', data: () => ({ offset: { x: 0, y: 0, },//偏移量 style: { top: '0px', left: '0px', }, canDragDom: null,//触发拖拽事件的物体 moveDom: null,//可以移动的物体 }), props: [ "data",//可以被拖拽的元素数组(必选) /* data格式说明: [ ... { canDragDom: elementDOM,//可以拖拽的位置元素数组or单个元素 moveDom: elementDOM,//拖拽同步移动的元素数组or单个元素 }, ... ] */ "disabled",//屏蔽 "mousedownNearSide",//按下鼠标吸附边界 "mousemoveNearSide",//移动鼠标吸附边界 "mouseupNearSide",//弹起鼠标吸附边界 "nearPadding",//距离边界多少像素自动吸附(mousedownNearSide||mousemoveNearSide||mouseupNearSide=true的时候才能生效),值可以是number或array类型,例如:是5代表[5,5,5,5],其中数组以此代表[上,右,下,左] "stopBoundary",//停靠边界距离,移动物体将按照这个值作为界限不再移出该范围(mousedownNearSide||mousemoveNearSide||mouseupNearSide=true的时候才能生效,值可以是number或array类型,例如:是5代表[5,5,5,5],其中数组以此代表[上,右,下,左] "cursor",//鼠标样式 /*cursor格式说明:{ grab:'default',//移入可拖拽区域的鼠标样式 grabbing:'default',//拖拽过程中鼠标样式 } */ ], watch: { data: { handler(newValue, oldValue) { newValue ? this.__addDragsEvents(newValue) : this.__removeDragsEvents(oldValue); }, deep: true, immediate: true, }, disabled: { handler(newValue, oldValue) { newValue ? this.__removeAllEvents() : this.__addAllEvents(); }, deep: true, immediate: true, }, style: { handler(newValue, oldValue) { if (this.moveDom && newValue && Object.keys(newValue).length) { let d = newValue; Object.keys(d).forEach(k => { this.moveDom.style[k] = d[k] }); this.moveDom.style.right = 'revert'; this.moveDom.style.bottom = 'revert'; } }, deep: true,//深度监听 immediate: true,//立即执行 }, }, destroyed() { this.__removeAllEvents(); }, mounted() { this.$parent.$el.style.setProperty("--sgDragMove-grab", (this.cursor || {}).grab || 'grab'); //js往css传递局部参数 this.$parent.$el.style.setProperty("--sgDragMove-grabbing", (this.cursor || {}).grabbing || 'grabbing'); //js往css传递局部参数 }, methods: { __addAllEvents() { this.__addDragsEvents(this.data); }, __removeAllEvents() { this.__removeWindowEvents(); this.__removeDragsEvents(this.data); }, __addWindowEvents() { this.__removeWindowEvents(); addEventListener('mousemove', this.mousemove_window); addEventListener('mouseup', this.mouseup_window); }, __removeWindowEvents() { removeEventListener('mousemove', this.mousemove_window); removeEventListener('mouseup', this.mouseup_window); }, // 初始化需要拖拽的DIV __addDragsEvents(doms) { (doms || []).forEach(dom => { this.__removeDraggedEvents(dom.canDragDom); this.__addDraggedEvents(dom.canDragDom); }); }, __removeDragsEvents(doms) { (doms || []).forEach(dom => { this.__removeDraggedEvents(dom.canDragDom); }); }, __addDraggedEvents(dom) { dom.setAttribute('sgDragMove_grab', 'ready'); dom.addEventListener('dragstart', this.dragstart); dom.addEventListener('mousedown', this.mousedown); }, __removeDraggedEvents(dom) { dom.removeEventListener('dragstart', this.dragstart); dom.removeEventListener('mousedown', this.mousedown); }, dragstart(e) { e.stopPropagation(); e.preventDefault(); return false; }, mousedown(e) { if (this.disabled) return this.mouseup_window(e); if (e.button === 2) return this.mouseup_window(e);//点击了鼠标右键 this.canDragDom = e.currentTarget; this.moveDom = this.data.find(v => v.canDragDom == this.canDragDom).moveDom; this.canDragDom.setAttribute('sgDragMove_grab', 'down'); this.moveDom.setAttribute('sgDragMove_move', 'ready'); let or = this.moveDom.getBoundingClientRect(); this.offset = { x: e.clientX - or.x, y: e.clientY - or.y, }; (this.mousedownNearSide || this.mousedownNearSide === "") && this.nearSide(); this.$emit('dragStart', this.getResult(e)); this.__addWindowEvents(); }, setOffset(d) { this.offset = { ...this.offset, ...d }; }, mousemove_window(e) { this.canDragDom.setAttribute('sgDragMove_grab', 'down'); this.moveDom.setAttribute('sgDragMove_move', 'ing'); let x = e.clientX - this.offset.x; let y = e.clientY - this.offset.y; this.style = { left: x + 'px', top: y + 'px', }; this.style['transition-property'] = 'left,top'; this.style['transition-duration'] = '0s,0s'; this.$nextTick(() => { (this.mousemoveNearSide || this.mousemoveNearSide === "") && this.nearSide(); this.$emit('dragging', this.getResult(e)); }); }, mouseup_window(e) { this.$emit('dragEnd', this.getResult(e)); (this.mouseupNearSide || this.mouseupNearSide === "") && this.nearSide(); this.offset = null; this.style = null; this.canDragDom.setAttribute('sgDragMove_grab', 'ready'); this.moveDom.setAttribute('sgDragMove_move', 'end'); setTimeout(() => { this.moveDom && this.moveDom.removeAttribute('sgDragMove_move') }, 100); this.canDragDom = null; this.moveDom = null; this.__removeWindowEvents(); }, // 自动吸附网页边界 nearSide() { let arr = this.nearPadding ? JSON.parse(JSON.stringify(this.nearPadding)) : 0; Array.isArray(arr) || (arr = [...Array(4)].map(v => arr)); let [dis_top, dis_right, dis_bottom, dis_left] = arr; arr = this.stopBoundary ? JSON.parse(JSON.stringify(this.stopBoundary)) : 0; Array.isArray(arr) || (arr = [...Array(4)].map(v => arr)); let [stopBoundary_top, stopBoundary_right, stopBoundary_bottom, stopBoundary_left] = arr; let x = parseFloat(this.moveDom.style.left); let y = parseFloat(this.moveDom.style.top); let rect = this.moveDom.getBoundingClientRect(); let min_side_x = 0, min_x = min_side_x + dis_left, min_left = min_side_x + stopBoundary_left; let min_side_y = 0, min_y = min_side_y + dis_top, min_top = min_side_y + stopBoundary_top; x < min_x && (this.moveDom.style.left = `${min_left}px`); y < min_y && (this.moveDom.style.top = `${min_top}px`); let max_side_x = innerWidth - rect.width, max_x = max_side_x - dis_right, max_right = max_side_x - stopBoundary_right; let max_side_y = innerHeight - rect.height, max_y = max_side_y - dis_bottom, max_bottom = max_side_y - stopBoundary_bottom; x > max_x && (this.moveDom.style.left = `${max_right}px`); y > max_y && (this.moveDom.style.top = `${max_bottom}px`); }, getResult(e) { return { $event: e, canDragDom: this.canDragDom, moveDom: this.moveDom, canDragDomRect: this.canDragDom ? this.canDragDom.getBoundingClientRect() : null, moveDomRect: this.moveDom ? this.moveDom.getBoundingClientRect() : null, } }, } }; </script> <style lang="scss"> [sgDragMove_grab="ready"] { cursor: var(--sgDragMove-grab); //css获取js传递的参数 * { cursor: var(--sgDragMove-grab); //css获取js传递的参数 } &:hover { opacity: 1; } &:active { opacity: 0.9; } } [sgDragMove_grab="down"] { cursor: var(--sgDragMove-grabbing); //css获取js传递的参数 * { cursor: var(--sgDragMove-grabbing); //css获取js传递的参数 } } [sgDragMove_move="ready"] { opacity: 1; } [sgDragMove_move="ing"] { opacity: 0.9; } [sgDragMove_move="end"] { transition: .1s; } </style>
应用
<sgDragMove :data="dragMoveDoms" nearPadding="50" :disabled="traySize === 'lg'" @dragStart="$emit(`dragStart`, dragMoveDoms)" @dragging="showRightBottomBtn = true; $emit(`dragging`, dragMoveDoms)" @dragEnd="$emit(`dragEnd`, dragMoveDoms)" mouseupNearSide/>