- 先看一个面向过程的拖拽实现方式
拖拽的实现思路:(下面参考代码)
-
鼠标触发mousedown时:记录起始的鼠标点击位置;记录起始的元素位置;事件绑定到该元素上
-
鼠标触发mousemove时:
记录鼠标当前的位置;
当前的鼠标距离 = 当前的鼠标距离 - 起始的鼠标距离;
现在的位置 = 开始的元素位置 + 鼠标移动的差值;
事件绑定到document上(防止鼠标移动过快将元素甩掉)
-
鼠标触发mouseup时:
取消事件处理函数 move,move 函数需要抽离,不可使用匿名函数。因为removeEventListener不可取消匿名函数;
设置 once : true 避免up时中间的代码执行多次
{
let box = document.querySelector("#box");
let startMouse = {}; // 摁下时鼠标位置
let startPosition = {}; // 摁下时元素位置
// 鼠标触发mousemove事件时的事件函数
let move = (e)=>{
// 鼠标当前的位置
let nowMouse={
x: e.clientX,
y: e.clientY
};
// 当前的鼠标距离:当前的鼠标距离 - 起始的鼠标距离
let dis = {
x: nowMouse.x - startMouse.x,
y: nowMouse.y - startMouse.y
};
// 现在的位置:开始的元素位置 + 鼠标移动的差值
box.style.left = dis.x + startPosition.x + "px";
box.style.top = dis.y + startPosition.y + "px";
};
box.addEventListener("mousedown",(e)=>{
// 起始的鼠标点击位置
startMouse={
x: e.clientX,
y: e.clientY
};
// 起始的元素位置
startPosition = {
x: box.offsetLeft,
y: box.offsetTop
}
document.addEventListener("mousemove",move);
// 这里move不能用匿名函数,否则removeEventListener的时候无法取消,removeEventListener取消事件监听不可使用匿名函数
document.addEventListener("mouseup",()=>{
document.removeEventListener("mousemove",move);
// 设定once: true 否则每次mouseup都会再触发一次这里面的代码
},{once:true})
});
}
<div id="box"></div>
<div id="box2"></div>
- 面向对象的拖拽实现方式
-
什么是对象?
对象就是一个封装了数据(属性)和方法的集合体。
-
什么是面向对象程序设计
(Object Oriented Programming -- OOP)
一种编程方法,关注某一个对象的属性和方法,关注对象与对象之间的联系
<script>
class Drag {
constructor(el){
this.el = el;
this.startMouse = {};
this.startPosition = {};
let move = (e)=>{
this.move(e);
};
//当开始拖拽时
this.ondragstart = null;
//拖拽中
this.ondrag = null;
//拖拽结束
this.ondragend = null;
this.el.addEventListener("mousedown",(e)=>{
this.start(e);
document.addEventListener("mousemove",move);
document.addEventListener("mouseup",(e)=>{
document.removeEventListener("mousemove",move);
this.end(e);
},{once:true})
});
}
start(e){
this.startMouse={
x: e.clientX,
y: e.clientY
};
this.startPosition = {
x: this.getStyle("left"),
y: this.getStyle("top")
};
e.preventDefault();
// 判断用户是否给该实例对象添加 ondragstart 的处理函数
this.ondragstart&&(this.ondragstart(e));
}
move(e){
let nowMouse = {
x: e.clientX,
y: e.clientY
};
let disMouse = {
x: nowMouse.x - this.startMouse.x,
y: nowMouse.y - this.startMouse.y
};
this.setStyle("left",disMouse.x + this.startPosition.x);
this.setStyle("top",disMouse.y + this.startPosition.y);
// 判断用户是否给该实例对象添加 ondrag 的处理函数
this.ondrag&&(this.ondrag(e));
}
end(e){
// 判断用户是否给该实例对象添加 ondragend 的处理函数
this.ondragend&&(this.ondragend(e));
}
//不加=,直接写个方法,会加在类的原型中
getStyle(attr){
return parseFloat(getComputedStyle(this.el)[attr]);
}
setStyle(attr,val){
this.el.style[attr] = val + "px";
}
}
// 拷贝拖拽
// 在子类中不写 constructor,会直接继承父类的 constructor
class CopyDrag extends Drag {
copyNode = null;
// 相当于 this.copyNode = null;
// 这里通过 = 赋值的属性会加在实例化对象中
ondragstart = (e) => {
this.copyNode = this.el.cloneNode(true);
this.el.parentNode.appendChild(this.copyNode);
this.el.style.opacity = 0.5;
}
ondragend = (e) => {
this.el.parentNode.removeChild(this.copyNode);
this.el.style.opacity = 1;
}
}
/*
怎么可以让我们的类使用起来更灵活
事件绑定:告诉使用者我有哪些事件
*/
{
let box1 = document.querySelector("#box1");
let box1Drag = new Drag(box1);
box1Drag.ondragstart = function(e){
console.log("dragstart");
};
box1Drag.ondrag = function(e){
console.log("drag");
};
box1Drag.ondragend = function(e){
// box1Drag.ondragstart = null;
console.log("dragend");
};
let box2 = document.querySelector("#box2");
let box2Drag = new Drag(box2);
}
</script>
- 事件监听的处理机制
/* el.addEventListener('eventName', fn)
* 事件处理机制:
* 1. 事件队列池
events = {
"click":[f1,f2,f3],
"mousedown":[f1,f2],
"mouseup":[f2,f4]
};
* 2. on 绑定事件
* 3. dispatch 派发事件
* 4. off 取消事件绑定
当用户点击时:
查看 events.click 中是否有事件处理函数
如果有,就循环把这些事件处理函数都执行了
*/
//实现一套处理监听机制
class Event {
//事件池,用来记录每一个事件的处理函数
events = {};
//添加事件监听
on(eventName,fn){
if(!this.events[eventName]){
this.events[eventName] = [];
}
this.events[eventName].push(fn);
}
//取消事件监听
off(eventName,fn){
if(this.events[eventName]){
this.events[eventName] = this.events[eventName].filter(item=>(item!=fn));
}
}
//触发器 调用dispatch(eventName) 时,将相应的函数都执行了
dispatch(eventName){
if(this.events[eventName]){
this.events[eventName].forEach(item => {
item.call(this);
});
}
}
}
- 实现一个复制拷贝的拖拽
<script>
class Event {
events = {}
on (eventName, fn) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(fn)
}
off (eventName, fn) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter((item, i) => {
return item !== fn
})
}
}
dispatch (eventName, event) {
if (this.events[eventName]) {
this.events[eventName].forEach((item, i) => {
item.call(this, event)
})
}
}
}
class Drag extends Event{
constructor(el) {
//注意:在子类中,写 constructor 一定要加 super
super()
this.el = el
this.startM = {}
this.startP = {}
this.init()
}
init() {
let move = (e) => {
this.move(e)
}
this.el.addEventListener('mousedown', (e) => {
this.start(e)
document.addEventListener('mousemove', move)
document.addEventListener('mouseup', () => {
this.end(e)
document.removeEventListener('mousemove', move)
}, {once: true})
})
}
start (e) {
this.startM = {
x: e.clientX,
y: e.clientY
}
this.startP = {
x: this.css(this.el, 'left'),
y: this.css(this.el, 'top')
}
e.preventDefault()
// 触发startDrag相应的事件处理
this.dispatch('startDrag', e)
}
move (e) {
let nowM = {
x: e.clientX,
y: e.clientY
}
let dis = {
x: nowM.x - this.startM.x,
y: nowM.y - this.startM.y
}
this.css (this.el, 'left', this.startP.x + dis.x)
this.css (this.el, 'top', this.startP.y + dis.y)
// 触发moveDrag相应的事件处理
this.dispatch('moveDrag', e)
}
end (e) {
this.dispatch('endDrag', e)
}
css (el, attr, val) {
if(arguments.length == 2) {
return parseInt(getComputedStyle(el)[attr])
} else {
el.style[attr] = val + 'px'
}
}
}
class copyDrag extends Drag{
constructor(...arg) {
console.log(...arg)
super(...arg)
this.on('startDrag', () => {
this.startDrag()
})
this.on('endDrag', () => {
this.endDrag()
})
}
startDrag(){
this.newNode = this.el.cloneNode(true)
this.el.style.opacity = 0.5
this.el.parentNode.appendChild(this.newNode)
}
endDrag() {
this.el.parentNode.removeChild(this.newNode)
this.el.style.opacity = 1
}
}
{
let box1 = document.querySelector('#box1')
let darg1 = new Drag(box1);
let box2 = document.querySelector('#box2')
let darg2 = new copyDrag(box2);
}
</script>