写一个拖拽类

173 阅读2分钟

要求:

写一个drag类,实现鼠标在窗口工具条上可以在页面上拖动矩形窗口,并能合理控制矩形窗口的拖动边界或拖动范围。

拆解

  • html
<body>
<div id="dragContainer">
      <div id="dragBar"></div>
    </div>
    <iframe></iframe>
</body>
  • css
   iframe {
        border: none;
        height: 100vh;
        width: 100vw;
      }

      #dragContainer {
        position: absolute;
        top: 0px;
        left: 0px;
        width: 400px;
        height: 300px;
        background-color: #fff;
        box-shadow: 5px 5px 10px #ccc;
        border:1px solid #eee
      }

      #dragBar {
        width: 400px;
        height: 40px;
        background-color: #036087;
      }
  • 此时,页面展示如图

image.png

实现可拖拽类

  1. 首先初始化构造函数,定义拖拽会用到的参数,dragBar 为拖拽的控制按钮,DragContainer则为要拖拽的盒子。
  2. 定义一个初始化位置和拖拽完成时的位置,用来计算拖拽结束时,dragContainer的位置,以及必不可以的拖拽方法。
class Drag{

constructor(el) {
    this.el = el;
    this.dragDiv = el.parentNode;
    this.startPosition = {};//鼠标初始位置 
    this.endPosition = {};//鼠标结束位置
    this.initPosition={}//dragDiv初始位置
    this.dragStart();
  }
  dragStart(){
  
  }

}

3.首先我们先完成对点击鼠标和松开鼠标的动作进行监听,通过计算鼠标松开时的位置,和起始位置的结果,来判断dragDiv需要移动的距离

this.el.addEventListener('mousedonw',()=>{
  this.el.addEventListener("mousedown", (e) => {
      this.initPosition = {
        x: this.dragDiv.offsetLeft,
        y: this.dragDiv.offsetTop,
      };
      this.startPosition = {
        x: e.clientX,
        y: e.clientY,
      }; 
    });

})
this.el.addEventListener('mouseup',()=>{
  this.el.addEventListener("mousedown", (e) => {
   
      this.endPosition = {
        x: e.clientX,
        y: e.clientY,
      }; 
      const { x, y } = this.calculatePosition();
     this.drag()
      
    });

})
 drag() {
    const { x, y } = this.calculatePosition();
    this.elDiv.style.left = this.initPosition.x + x + "px";
    this.elDiv.style.top = this.initPosition.y + y + "px";
  }
 calculatePosition() {
    const x = this.endPosition.x - this.startPosition.x;
    const y = this.endPosition.y - this.startPosition.y;
    return { x, y };
  }
  • 接下来就是拖拽时,也就是对mousemove的监听
 this.el.addEventListener("mousemove", (e) => {
      this.endPosition = {
        x: e.clientX,
        y: e.clientY,
      };
      this.drag();
    });
  • 到这一步发现,当松开鼠标时,弹窗还是会移动。因为只添加对mousemove的监听,当我们松开时,要移除对mousemove的监听,如果使用removeListner的方法,需要两个监听器时使用的参数完全一致,包括内存中回调的相同引用,所以需要将回调函数声明为一个变量,再进行调用。
const dragMove=(event) => {
        this.dragMove(event);
      };
      
this.el.addEventListener("mousemove", dragMove);
this.el.addEventListener("mouseup", () => {
        this.el.removeEventListener("mousemove", dragMove);
      });
  • 为了解决这个麻烦的问题,我们可以使用AbortController() 来做监听的移除。
    const controller = new AbortController();
    const { signal } = controller;
      this.el.addEventListener(
      "mousemove",
      (e) => {
        this.endPosition = {
          x: e.clientX,
          y: e.clientY,
        };
        this.drag();
      },
      { signal }
    );
    //  监听松开鼠标,取消对mousemove的监听
      document.addEventListener("mouseup", (e) => {
      this.endPosition = {
        x: e.clientX,
        y: e.clientY,
      };
      this.drag();
      controller.abort();
    });

总结

监听mousedown mouseup mousemove三个事件,且记录下初始位置和结束位置,对要拖拽的盒子进行控制,当松开鼠标时,清除对鼠标移动时的监听函数,当然最后还要对拖拽盒子的边界位置进行判断,使其不超出可视区。