JS实现拖拽事件

467 阅读3分钟

CSS

*{
    margin:0px;
    padding:0px; 
}
html{
    height: 100%;
}
body{
    width: 100%;
    height: 100%;

}
.box{
    width: 100px;
    height: 100px;
    background-color: lightskyblue;
    position: fixed;
    top: 100px;
    left:100px;
    cursor: move;
} 

HTML

// 实现拖拽此box
<div class="box"> </div>

JS

 let box = document.querySelector('.box')
 // 计算边界值
 let HTML = document.documentElement,
     minL =0,
     minT = 0,
     maxL = HTML.clientWidth - box.offsetWidth,
     maxT = HTML.clientHeight - box.offsetHeight
 
 
  // 鼠标按下 开始拖拽
  const down =function down(ev){
    // 盒子的  getBoundingClientRect 距离当前窗口,视口的  bottom=top+width
    // console.log(box.getBoundingClientRect()) 
    // 鼠标的  clientX clientY  也是距离窗口的  
    // 但是box的top left absolute是距离body的。也就是页面的。。 所以第一种方法:定位absolute 改成fixed的,距离窗口的
    // 第二种方法:也可以将 鼠标的位置和盒子的位置 都用距离body的方法获取值 用 pageX pageY ,盒子的话用直接获取值,或者offsetLeft
    // 计算的话 要统一参照物,距离页面或者body就都用页面的方法,距离窗口都用窗口的方法。

    // 记录鼠标的开始位置和盒子的开始位置  距离窗口的位置  
    // 这些信息会在鼠标移动的方法中使用。(把信息挂载盒子的自定义属性上,后期在其他的方法中只要获取到盒子,就可以获取到记录的这些开始信息了,防止全局变量污染。)
    // this->box
    let {top,left} = this.getBoundingClientRect()
    // 盒子的
    this.startT = top
    this.startL = left
    // 鼠标的
    this.startX = ev.clientX
    this.startY = ev.clientY 

    // 只有鼠标按下的时候 才开始绑定move
    this.addEventListener('mousemove',move);
    this.addEventListener('mouseup',up 
}

// 鼠标移动拖拽中
 const move = function move(ev){
    //获取盒子当前的位置
    let curL = ev.clientX-this.startX+this.startL,
        curT = ev.clientY-this.startY + this.startT

    curL = curL<minL ? minL:(curL>maxL?maxL:curL)
    curT = curT<minT ? minT:(curT>maxT?maxT:curT)

    //  要读写分离
    this.style.left = `${curL}px`
    this.style.top = `${curT}px`
 }

 // 鼠标抬起拖拽中
 const up = function up(ev){  
    // 移除事件
    box.removeEventListener('mousemove',move);
    box.removeEventListener('mouseup',up); 
 }

 box.addEventListener('mousedown',down);
 
 /**
  * 总结:
  * 距离窗口的
  *     盒子:getBoundingClientRect 
  *     鼠标:clientX clientY 
  *     盒子样式:固定定位 fixed
  * 距离body,页面的
  *     盒子:offsetLeft offsetRight 或者直接读取 left top值
  *     鼠标:pageX pageY 
  *     盒子样式:绝对定位 absolute
  * 
  *    鼠标焦点丢失问题:
  *    事件都绑定到box上的,鼠标移动过快,计算盒子位置的程序跟不上处理,导致鼠标移除盒子,从而引发一系列的问题。
  */

解决鼠标焦点丢失问题

// 解决鼠标焦点丢失问题
//   + IE/ 火狐 :把盒子和鼠标绑定在一起  
        // + setCapture 
        // + releaseCapture
//   + 谷歌:所有的原因是因为鼠标不在盒子上了,很多事件触发不了,鼠标不管移动多块,也逃离不去浏览器,所以mousemove/mouseup都绑定给window。  
//在IE和火狐都兼容。          
let box = document.querySelector('.box')
let HTML = document.documentElement,
     minL =0,
     minT = 0,
     maxL = HTML.clientWidth - box.offsetWidth,
     maxT = HTML.clientHeight - box.offsetHeight
  
 const down =function down(ev){
    
    let {top,left} = this.getBoundingClientRect() 
    this.startT = top
    this.startL = left 
    this.startX = ev.clientX
    this.startY = ev.clientY 
    
    // move up中的this还是盒子 现在是window了  并且考虑移除的时候如何移除
    this._move = move.bind(this)
    this._up = up.bind(this)
    window.addEventListener('mousemove',this._move);
    window.addEventListener('mouseup',this._up);

 }

// 鼠标移动拖拽中
 const move = function move(ev){
    //获取盒子当前的位置
    let curL = ev.clientX-this.startX+this.startL,
        curT = ev.clientY-this.startY + this.startT
    
    curL = curL<minL ? minL:(curL>maxL?maxL:curL)
    curT = curT<minT ? minT:(curT>maxT?maxT:curT)

    //  要读写分离
    this.style.left = `${curL}px`
    this.style.top = `${curT}px`
 }

 // 鼠标抬起拖拽中
 const up = function up(ev){  
    // 移除事件
    window.removeEventListener('mousemove',this._move);
    window.removeEventListener('mouseup',this._up); 
 }

 box.addEventListener('mousedown',down);


 // HTML5中内置的拖拽事件:把目标元素的拖拽到指定的区域中。这次是用mousedown mouseup mousemove 实现