双端通用型JS拖拽插件的封装与应用

作者 : 开心源码 本文共2725个字,预计阅读时间需要7分钟 发布时间: 2022-05-13 共296人阅读

最近工作中遇到一个需求,需要将一个元素从某位置拖动到另一固定位置后执行某一交互行为,具体效果如下:

将红色定位标志拖动到蓝色标志位置后弹窗

这个看似简单的需求,然而实现起来却并不那么顺利。我首先想到的是如何通过哪个现有的插件来快速处理这个问题,然而找了半天,并未找到合适的原生js插件,总会在实际使用当中出现少量莫名其妙的问题。所以,一不做二不休,干脆自己封装一个得了!一方面以后可能还会遇到相似的需求,另一方面自己写的总归更加熟习,日后也更好维护和拓展。

闲言少叙,接下来就让我们一步步用原生js来实现这个简单的拖拽插件。


一、“类”的构建

插件,当然得有插件的样子。这里我用的是ES6中的class语法糖来实现“类”的封装,这样我们之后在使用插件时只要new一个对象即可以了。

//  定义拖拽插件class Drag{  constructor(selector, options){  }}
//  使用拖拽插件new Drag('.box');  // 该box元素可拖拽

二、获取元素

既然是拖拽插件,当然首先得获取需要被拖拽的元素,这里我们可以让插件使用者直接把已经获取到的元素对象传进来,也可以以CSS选择器的方式传进来,在插件内部进行元素获取。

具体可以通过类型判断来实现,代码如下:

getElement(selector){  if(typeof selector === 'string'){  // 传入css选择器    return document.querySelector(selector);  } else if(typeof selector === 'object'){  // 传入DOM对象    return selector;  } else {    throw '请传入正确的元素';  }}

而后我们可以在constructor中调用它:

constructor(selector, options){  this.el = this.getElement(selector);  if(!this.el){    throw `未找到移动元素`;  }}

三、通过事件对象获取鼠标(或者手指)位置

在解决拖拽事件之前,我们得先知道拖拽的基本原理是什么。

拖拽,本质上是鼠标(或者手指)在元素上按下后,移动鼠标(或者手指)时元素跟随鼠标指针(或者手指)位置移动,最后当鼠标(或者手指)松开时元素中止移动。

其中最关键的部分就是鼠标(或者手指)移动时,鼠标(或者手指)位置的获取,这时我们就要用到 事件对象 了。

1. PC端获取鼠标位置

e.clientX   // 横坐标e.clientY   // 纵坐标

2. 手机端获取手指位置

这里又得分两种情况,一种是手指移动时,一种是手指松开时。

① 手指移动时,也就是touchmove事件

e.touches[0].clientX   // 横坐标e.touches[0].clientY   // 纵坐标

为什么是touches[0]呢?由于我们只用到了一根手指呀!

② 手指松开时,也就是touchend事件

e.changedTouches[0].clientX   // 横坐标e.changedTouches[0].clientY   // 纵坐标

看到了吗?无论是哪种方式,我们获取鼠标(或者手指)的位置都是clientXclientY,只不过前面的那个对象不一样而已。这时为了代码良好的复用性,我们可以对前面的那个对象进行简单的封装。

eventObj(event,isEnd = false){  // isEnd代表能否是手指松开时  return isMobile() ? (isEnd ? event.changedTouches[0] : event.touches[0]) : event;}
// 判断能否是手机端,由于手机端才会有ontouchstartfunction isMobile() {  return document.body.ontouchstart;}

四、获取事件名称

在整个拖拽过程中,我们无非就用到三种事件:开始、移动、结束。而在PC端和手机端分别对应一组事件名称,我们将其分别用数组进行存储。

eventName(){  if(isMobile()){    return ['touchstart','touchmove','touchend'];  } else {    return ['mousedown','mousemove','mouseup'];  }}

五、实现拖拽

前面准备工作做了这么多,就是为了实现这最最关键的一步:拖拽,也就是这三种事件(开始、移动、结束)的实现。

initData(){  // 父元素的位置  this.parentPos = {    x: this.el.parentNode.getBoundingClientRect().left,    y: this.el.parentNode.getBoundingClientRect().top,    w: this.el.parentNode.getBoundingClientRect().width,    h: this.el.parentNode.getBoundingClientRect().height,  };  // 移动元素的初始位置和大小  this.elemPos = {    x: this.el.offsetLeft,    y: this.el.offsetTop,    w: this.el.offsetWidth,    h: this.el.offsetHeight,  };}
bindEvent(){  let eventName = this.eventName(),      status = false;  //  初始化数据  this.initData();   // 开始  this.el.addEventListener(eventName[0], e => {    status = true;  });  // 移动  document.addEventListener(eventName[1], e => {    if(status){      e = this.eventObj(e);      let left = e.clientX - this.elemPos.w / 2 - this.parentPos.x,          top = e.clientY - this.elemPos.h / 2 - this.parentPos.y;      this.el.style.cssText = `position: absolute; left: ${ left }px; top: ${ top }px;`;    }  });  // 结束  document.addEventListener(eventName[2], e => {    e = this.eventObj(e,true);    status = false;  });}

结束语

写到这里,一个简单的PC端和手机端双端通用性JS拖拽插件就已经完成了。当然,我在此基础上还加了拖拽目标和松开反弹等功能,完整代码可在我的 Github 上预览。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 双端通用型JS拖拽插件的封装与应用

发表回复