上面来说下实现这个拖拽的各个档次
Level 1 拖拽起来
应用原生形式实现拖拽,有几个根底的知识点要分明
mousedown
mousemove
mouseup
能想到这三个事件阐明是有大体的思路的;
offsetLeft
offsetTop
clientLeft
clientTop
看到这几个概念,预计有些懵了,因为可能咱们写代码的时候,大部分工夫都用不到这几个;先不具体说这四个概念,咱们先剖析拖拽的思路而后娓娓道来;
拖拽的思路很简略,鼠标挪动的时候,让 div 跟上鼠标,那最简略的,让 div 的 position 是 absolute 的,他的 left 和 top 不停地更替为鼠标目前的坐标即可;
那么鼠标的坐标怎么示意呢?
神器来了,就是鼠标事件的 clientX 和 clientY,OK~ 那么就把每次的 clientX 和 clientY 付给 div 的 left 和 top 就好啦,每次完结的时候保留以后的地位即可;
然而实现之后会发现拖拽的时候 div 总是往右下方挪动一下,才随着鼠标去挪动,为什么会这样呢?
这就是鼠标事件的 offsetX,offsetY,offsetX 和 offsetY 就是鼠标指针地位绝对于触发事件的对象的 x 和 y 坐标。
好了,组装一下下面的事件和办法,就好了,代码如下:
~function(){
let container = document.getElementById('container');
[originX , originY ,dragTag]= [0, 0 ,false];
container.addEventListener('mousedown', startDarg);
container.addEventListener('mousemove', onDragging);
container.addEventListener('mouseup' , endDrag);
function startDarg(e) {
dragTag = true;
let {offsetX , offsetY} = e ;
[originX , originY] = [offsetX ,offsetY];
}
function onDragging(e) {if(dragTag){let left = +(e.clientX - originX) ;
let top = +(e.clientY - originY) ;
container.style.left = left + 'px';
container.style.top = top + 'px';
}
}
function endDrag (e){if(dragTag){dragTag = false ;}
}
}()
好了,这样就欢快的拖拽起来了~
然而霎时也发现问题了,我甩起来飞起来拖,div 块块就会跟不上鼠标了;而且我拖一拖的就跑到看不到的窗口区域去了,往右边,往下面拖超过窗口区域,好歹还有滚轮,滚过来能找到,然而往上面,右边,拖出去就没有啦!
可能意识到这些问题并且解决就是 level2 啦,上面进入
Level2 解决拖拽提早和拖拽边界问题
(1) 拖拽提早
首先要解释下为什么会有拖拽提早
鼠标挪动过快时,鼠标挪动出了 div 的范畴,要再计算 div 的 left 和 top 就会呈现延缓,解决办法就是把 mousemove 和 mouesup 绑定在 document 上即可;
this.ele.addEventListener(‘mousedown’, startDarg);
document.addEventListener(‘mousemove’,onDragging);
document.addEventListener(‘mouseup’ , endDrag);
解决完拖拽提早的问题,上面就是拖拽边界的问题
(2) 边界问题
边界问题次要解决的是下面和右边的问题;
let left = +(e.clientX – e.offsetX) ;
let top = +(e.clientY – e.offsetY) ;
[left , top] = [Math.max(0, left), Math.max(0, top) ] ;
当 left 和 top 小于零的时候,重置为 0
好了,到这里就基本上就能解决大部分问题了;基本上能够无 bug 运行了;(兼容性的问题本篇不重点阐明,毕竟用的是 es6,IE 本省很多都是没有反对的)
上面想想这样拖拽只绑定在了一个元素上,如果想新增一个拖拽的元素;怎么办?这个问题抛出来就是为了往封装下来想。
Level3 封装起来
以正当的形式封装必要内容集体认为是从性能开发进阶到底层开发最无效和简略的形式,能在开发中思考他人用我的货色如何用能更好用是应该逐步锤炼起来的。
js 的封装不想其余语言那么顺畅,但自从 es6 进去之后,有个 class 呈现后封装变得更为不便,当然,更为反对用一些设计模式去搞定,毕竟题目是原生,用 es6 有点赖皮,class 实现如下
class Drag{
constructor(ele){
this.ele = ele ;
[this.originX , this.originY ,this.dragTag]= [0, 0 ,false];
this.ele.addEventListener('mousedown', this.startDarg.bind(this));
document.addEventListener('mousemove', this.onDragging.bind(this));
document.addEventListener('mouseup' , this.endDrag.bind(this));
}
startDarg(e) {
e = e || window.event ;
this.dragTag = true;
let {offsetX , offsetY} = e ;
[this.originX , this.originY] = [offsetX ,offsetY];
}
onDragging(e) {
e = e || window.event ;
if(this.dragTag){let left = +(e.clientX - this.originX) ;
let top = +(e.clientY - this.originY) ;
let {clientWidth , clientHeight} = document.body;
[clientWidth , clientHeight] = [clientWidth - this.ele.clientWidth , clientHeight - this.ele.clientHeight] ;
[left , top] = [Math.max(0, left), Math.max(0, top) ] ;
[left , top] =
[Math.min(clientWidth, left), Math.min(clientHeight, top) ]
if(this.validatePos(left ,top)){
this.ele.style.left = left + 'px';
this.ele.style.top = top + 'px';
}
}
}
endDrag (e){
e = e || window.event ;
if(this.dragTag){document.removeEventListener('mousemove',() => {});
document.removeEventListener('mouseup',() => {});
this.dragTag = false ;
}
}
validatePos(left ,top) {if(Number.isNaN(left) || Number.isNaN(top)){return false;}
return true ;
}
}
这样的话,只有申明为每一个要拖拽的对象,申明本人独立的拖动对象即可
let container = document.getElementById(‘container’);
let dargObj = new Drag(container);