拖拽效果的原理及实现

2,609 阅读6分钟
我打算鼠标点击会创建一个新的元素,并给这个元素加上css样式。鼠标在元素内按下就可以实现拖拽,鼠标松开则元素停留在鼠标松开的位置上。


一、拖拽流程中的js事件

这里我们要用到的鼠标事件有:

1. 鼠标点击事件

 document.addEventListener("click",clickHandler); 
// 给document添加侦听事件,等于就是给html这个根元素添加侦听。侦听点击执行clickHandler函数

2. 鼠标划入事件

document.addEventListener("mouseover",mouseoverHandler); /* 添加鼠标划入侦听 */

3. 鼠标按下事件

divs[i].addEventListener("mousedown",mouseHandler); // 给divs添加鼠标按下事件侦听

4. 鼠标移动事件

document.addEventListener("mousemove",mouseHandler); /* 添加鼠标移动侦听 */

5. 鼠标松开事件

document.addEventListener("mouseup",mouseHandler); /* 添加鼠标松开侦听 */


二、实现原理

拖拽就是元素跟随鼠标的移动而移动,在这个过程中,元素的位置可以由定位来实现,我们可以注意到在拖拽过程中,无论元素的left和top如何变化,它始终是鼠标指针到窗口左上角顶点的位置减去鼠标到元素左上角定点的位置,如下:

document.div.style.left= e.clientX-document.offset.x+"px";
document.div.style.top= e.clientY-document.offset.y+"px";


1、创建元素和样式

我们用鼠标点击窗口,就在点击的位置上创建一个元素,元素的宽高是40~80之间的随机数,颜色是16进制的随机数的颜色。

给整个窗口添加点击事件侦听,并获取点击鼠标的位置相对窗口的位置:

        init(); // 初始化init函数
        function init(){
                document.addEventListener("click",clickHandler);
                  /* 添加侦听事件,侦听点击 */        
            }  
      
        function clickHandler(e){ // 侦听到点击并带入事件对象            
         createRect(e.clientX,e.clientY);
            /* 填入createRect函数,并获取点击鼠标的位置相对窗口的位置,并传参 */        
          }

在点击的位置上创建一个元素:

        function createRect(x,y){/* 创建函数,设置形参并传入参数 */ 
           var w=randoms(80,40);/* randoms函数设置实参,赋值给w */
            var rect=ce("div",{/* ce函数设置div样式 */
                width:w+"px",/* 宽高都是w */
                 height:w+"px",
                 backgroundColor:randomColor(),/* 背景色用下列随机函数获取 */
                 position:"absolute",/* 设置定位 */
                 left:x-w/2+"px", /* 点击的相对位置y-w除于2登出的距离,可以点击鼠标创建div的位置就是div的中心点 */
                 top:y-w/2+"px"            
         }); 
            document.body.appendChild(rect);/* 把div和样式添加到body里 */
            } 

        function ce(type,style){// ce函数把上面实参带入
           var elem=document.createElement(type);// 带入的type就是元素,并赋值给elem
                  Object.assign(elem.style,style); // 把元素的样式和元素返回
                  return elem; // 把elem返回        
           }

元素的宽高随机数的函数和颜色16进制的颜色

        function randoms(max,min){ // 随机40~80内的数,把80和40实参,带给max和min这两个形参数
           if(min===undefined) min=0;/* 如果min没有定义时进入 */            
               return Math.floor(Math.random()*(max-min)+min);/* 获取随机数40-80 */
         } 
      
        function randomColor(){ // 随机背景颜色
           var col="#";
           for(var i=0;i<6;i++){ // 循环6次获取6个16进制
             col+=randoms(16).toString(16);
               /* 把16带入到随机宽高中获取16内的随机数,并转换成16进制 */
           } 
             return col; /* 返回col值 */        
        }


2、给元素添加拖拽效果

给document添加鼠标划入侦听,并获取全部的div放在divs中,并给每个div添加鼠标按下事件侦听

document.addEventListener("mouseover",mouseoverHandler);/* 添加鼠标划入侦听 */
   function mouseoverHandler(e){/* 如果鼠标划入div获取全部div,赋值给divs */
        var divs=document.querySelectorAll("div"); // 获取所有div并放在divs里
       for(var i=0;i<divs.length;i++){ //遍历divs
           divs[i].addEventListener("mousedown",mouseHandler);/* 给所有的div添加鼠标按下侦听 */
       }    
   }

按下鼠标进入mouseHandler函数

function mouseHandler(e){/* 如果鼠标按下div则这个div执行下列语句 */
   if(e.type==="mousedown"){/* 如果鼠标是按下则进入语句 */
       document.div=e.target;/* 让被点击的元素等于document.div */
       document.offset={x:e.offsetX,y:e.offsetY};/* 获取相对目标左上角的位置 */
       document.addEventListener("mousemove",mouseHandler);/* 添加鼠标移动侦听 */
       document.addEventListener("mouseup",mouseHandler);/* 添加鼠标松开侦听 */ 
   }else if(e.type==="mousemove"){/* 如果鼠标是移动进入语句 */
       document.div.style.left=e.clientX-document.offset.x+"px";
       /* div的位置是鼠标相对窗口的位置减去鼠标相对元素左上角的位置,算出移动到的位置 */
       document.div.style.top=e.clientY-document.offset.y+"px";
       document.removeEventListener("click",clickHandler);/* 删除点击侦听,避免鼠标松开会再创建一个div */
   }else if(e.type==="mouseup"){
       document.removeEventListener("mousemove",mouseHandler);/* 删除鼠标移动侦听,避免松开鼠标div还会跟着鼠标走 */
       document.removeEventListener("mouseup",mouseHandler);/* 删除鼠标松开侦听,避免下次不能使用 */
   }        
}

前面删除了document的点击侦听,后面就再不能创建元素了。所以最后还要给document做划出侦听,划出则重新给document添加点击侦听。

document.addEventListener("mouseout",mouseoutHandler);/* 设置鼠标划出侦听 */        
   function mouseoutHandler(){            
       document.addEventListener("click",clickHandler);
       /* 重新添加点击侦听,这样移动完元素,还能再创建div */  
   }

3、创建元素和拖拽元素合并的完整代码

       init();        
        function init(){
          document.addEventListener("click",clickHandler);/* 添加侦听事件,侦听点击 */
        }    
    
        function clickHandler(e){
          createRect(e.clientX,e.clientY);/* 填入createRect函数,并获取点击鼠标的位置相对窗口的位置 */
        }

        function createRect(x,y){/* 创建函数,设置形参并传入参数 */
            var w=randoms(80,40);/* randoms函数设置实参,赋值给w */ 
          var rect=ce("div",{/* ce函数设置div样式 */
          width:w+"px",/* 宽高都是w */
          height:w+"px",
          backgroundColor:randomColor(),/* 背景色用下列随机函数获取 */
          position:"absolute",/* 设置定位 */
          left:x-w/2+"px", /* 点击的相对位置y-w除于2登出的距离,可以点击鼠标创建div的位置就是div的中心点 */
          top:y-w/2+"px" 
          });
              document.body.appendChild(rect);/* 把div和样式添加到body里 */
          }

       function ce(type,style){
          var elem=document.createElement(type);
          Object.assign(elem.style,style);
              return elem;
          }

       function randoms(max,min){
          if(min===undefined) min=0;/* 如果min没有定义时进入 */
               return Math.floor(Math.random()*(max-min)+min);/* 获取随机数40-80 */
          }

       function randomColor(){
          var col="#";
          for(var i=0;i<6;i++){
               col+=randoms(16).toString(16);/* 获取随机颜色转换成16进制 */
          }
            return col;/* 返回col值 */
          }

       document.addEventListener("mouseover",mouseoverHandler);/* 添加鼠标划入侦听 */
       function mouseoverHandler(e){/* 如果鼠标划入div获取全部div,赋值给divs */
          var divs=document.querySelectorAll("div")
          for(var i=0;i<divs.length;i++){
              divs[i].addEventListener("mousedown",mouseHandler);/* 给所有的div添加鼠标按下侦听 */
          }    
       }        

       function mouseHandler(e){/* 如果鼠标按下div则这个div执行下列语句 */
          if(e.type==="mousedown"){/* 如果鼠标是按下则进入语句 */
             document.div=e.target;/* 让被点击的元素等于document.div */
             document.offset={x:e.offsetX,y:e.offsetY};/* 获取相对目标左上角的位置 */
             document.addEventListener("mousemove",mouseHandler);/* 添加鼠标移动侦听 */
             document.addEventListener("mouseup",mouseHandler);/* 添加鼠标松开侦听 */
          }else if(e.type==="mousemove"){/* 如果鼠标是移动进入语句 */
             document.div.style.left=e.clientX-document.offset.x+"px";/* div的位置是鼠标相对窗口的位置减去鼠标相对元素左上角的位置,算出移动到的位置 */
             document.div.style.top=e.clientY-document.offset.y+"px";
              document.removeEventListener("click",clickHandler);/* 删除点击侦听,避免鼠标松开会再创建一个div */
          }else if(e.type==="mouseup"){
             document.removeEventListener("mousemove",mouseHandler);/* 删除鼠标移动侦听,避免松开鼠标div还会跟着鼠标走 */
             document.removeEventListener("mouseup",mouseHandler);/* 删除鼠标松开侦听,避免下次不能使用 */
          }
      }

       document.addEventListener("mouseout",mouseoutHandler);/* 设置鼠标划出侦听 */
       function mouseoutHandler(){
          document.addEventListener("click",clickHandler);/* 重新添加点击侦听,这样移动完元素,还能再创建div */
       }