拖拽功能主要操作的是真实的DOM元素以及鼠标事件,在vue中使用自定义指令最合适不过了。 当然使用组件封装起来,然后拖拽这个组件,也是可以实现相同的效果,不过我觉得这样有点大材小用,也缺乏灵活性,好处是可以传入更多的控制属性。总之,在组件与指令之间找到平衡点,自有取舍。
言归正传,要想实现拖拽的效果 要么使用 drag 事件, 要么使用 mouse 鼠标事件,我这里选用的是 mouse 鼠标事件组合。
v-draggable, 全局注册使用或者局部注册使用全局注册
// main.js
import draggable from "./directives/draggable";
Vue.directive("draggable", draggable);
局部注册
// 需要使用的文件
import draggable from "./directives/draggable";
export default {
data() {
return {
// ...
};
},
directives:{
directive
}
};
元素可以随意拖拽,没有边界限制
<div style="position: fixed;" v-draggable>div>
元素可以随意拖拽,有边界限制, 无法拖动到屏幕之外, 关键在于 设置 【sticky】 修饰符
<div style="position: fixed;" v-draggable.sticky>div>
拖拽的数据,可以通过 handleDraggable 返回,由你决定数据如何使用, 如何显示
<div style="position: fixed;" v-draggable="handleDraggable">div>
<script>
export default {
methods: {
handleDraggable(config){
console.log(config)
if (config.dragging) {
this.style = {
left: `${config.x}px`,
top: `${config.y}px`,
width: `${config.rect.width}px`,
height: `${config.rect.height}px`,
};
}
}
}
};
script>
当拖拽的元素,必须需要获取点击事件时,我们可以通过自定义应用的方式实现这一个目标;
一般情况下,我们认为元素没有移动时,鼠标的一次按下与松开,为一次点击事件,故而可以如下处理
handleDraggable(config){
console.log(config)
const {type, isMove} = config
if (type === "mouseup" && !isMove) {
// 这里为点击事件
return;
}
}
id: 唯一自增id
binding, 自定义指令中的 binding
vnode, 自定义指令中的 vnode
target: 拖拽元素
type: 触发的鼠标事件名称,用于区分不同阶段 mousedown、mousemove、mouseup
rect: 返回getBoundingClientRect()获取拖拽元素的位置
x: 拖拽元素距离屏幕左侧的距离
y: 拖拽元素距离屏幕上方的距离
dragstartX: 鼠标按下时坐标
dragstartY:
dragendX: 鼠标松开时坐标, 无值时为undefined
dragendY:
startX: 拖拽起点坐标
startY:
diffX: 拖拽元素当前移动端额狙击
diffY:
dragging: 元素是否可以拖拽
isMove: 拖拽的元素是否有移动
let seed = 0;
const ctx = "@@draggableContext";
function handleMousedown(event) {
event.preventDefault();
const el = this;
const rect = el.getBoundingClientRect();
Object.assign(el[ctx], {
type: "mousedown",
rect: rect,
x: rect.x || rect.left,
y: rect.y || rect.top,
dragstartX: event.clientX, // 鼠标按下时坐标
dragstartY: event.clientY,
dragendX: void 0, // 鼠标松开时坐标
dragendY: void 0,
startX: event.clientX, // 起点坐标
startY: event.clientY,
dragging: true,
isMove: false,
});
callback(el);
window.addEventListener("mousemove", el[ctx]._handleMousemove, false);
window.addEventListener("mouseup", el[ctx]._handleMouseup, false);
}
function handleMousemove(el) {
return function(event) {
event.preventDefault();
if (event.target === document.documentElement) return;
const current = {
x: event.clientX,
y: event.clientY,
};
const diff = {
x: current.x - el[ctx].startX,
y: current.y - el[ctx].startY,
};
if (el[ctx].binding.modifiers.sticky) {
// 不会拖出屏幕边缘
const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;
const {
x,
y,
rect: { width, height },
} = el[ctx];
if (diff.x < 0 && x + diff.x <= 0) {
el[ctx].x = 0;
} else if (diff.x > 0 && x + width - clientWidth >= 0) {
el[ctx].x = clientWidth - width;
} else {
el[ctx].x += diff.x;
}
if (diff.y < 0 && y + diff.y <= 0) {
el[ctx].y = 0;
} else if (diff.y > 0 && y + height - clientHeight >= 0) {
el[ctx].y = clientHeight - height;
} else {
el[ctx].y += diff.y;
}
} else {
el[ctx].x += diff.x;
el[ctx].y += diff.y;
}
Object.assign(el[ctx], {
type: "mousemove",
startX: current.x,
startY: current.y,
diffX: diff.x,
diffY: diff.y,
isMove: true,
});
callback(el);
};
}
function handleMouseup(el) {
return function(event) {
event.preventDefault();
const lastType = el[ctx].type;
Object.assign(el[ctx], {
type: "mouseup",
dragendX: event.clientX, // 鼠标按下时坐标
dragendY: event.clientY,
dragging: false,
isMove: lastType === "mousemove",
});
callback(el);
window.removeEventListener("mousemove", el[ctx]._handleMousemove, false);
window.removeEventListener("mouseup", el[ctx]._handleMouseup, false);
};
}
function callback(el) {
const bindingFn = el[ctx]?.binding?.value;
if (typeof bindingFn === "function") {
bindingFn({ ...el[ctx], target: el });
} else {
const { x, y, rect, dragging } = el[ctx];
if (!dragging) return;
el.style.cssText = `
left: ${x}px;
top: ${y}px;
width: ${rect.width}px;
height: ${rect.height}px;
`;
}
}
/**
* v-draggable
* @desc
* @example
* ```vue
*
*
*
*
* ```
*/
export default {
bind(el, binding, vnode) {
const id = seed++;
el[ctx] = {
id,
binding,
vnode,
_handleMousemove: handleMousemove(el, binding, vnode),
_handleMouseup: handleMouseup(el, binding, vnode),
};
el.addEventListener("mousedown", handleMousedown, false);
},
unbind(el) {
window.removeEventListener("mousemove", el[ctx]._handleMousemove, false);
window.removeEventListener("mouseup", el[ctx]._handleMouseup, false);
el.removeEventListener("mousedown", handleMousedown, false);
delete el[ctx];
},
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
-
相关阅读:
如何进行数据库备份
数据结构“入门”—堆的实现
【前端设计模式】之组合模式
用Notepad++写java代码
还在为sql注入眼花缭乱的过滤而烦恼?一文教您快速找出所有过滤内容
c++ vector的模拟实现以及迭代器失效问题
ctfshow sql171-179
现代计算与光学的跨界机遇——
第一季:8spring支持的常用数据库事务传播属性和事务隔离级别【Java面试题】
【【萌新的FPGA学习之Vivado下的仿真入门-2】】
-
原文地址:https://blog.csdn.net/shijue98/article/details/126022408