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

这个看似简单的需求,然而实现起来却并不那么顺利。我首先想到的是如何通过哪个现有的插件来快速解决这个问题,然而找了半天,并未找到合适的原生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] 上预览。

References

[1] Github: https://github.com/chchlsh/drag

sortable 拖拽时互换目标的位置_双端通用型JS拖拽插件的封装与应用相关推荐

  1. sortable 拖拽时互换目标的位置_报表如何实现行列互换效果?

    通常我们设计的二维的交叉报表,横向的维度和纵向的维度是固定的,而用户希望更希望能根据自己的需要快速转换横向纵向维度来查看报表. 如上图所示,我们通过点击一个按钮或文字,就可将报表的行列维度互相转换,这 ...

  2. vue适配双端浮标(悬浮球)拖拽

    适配双端浮标拖拽 最近写到一个需求是悬浮球拖动,看了很多网上的案例,要么移动端很流畅那么PC端就很卡,要么双端(ps:双端是指移动端和PC端)都很卡.于是自己就在网上百度改造了一款双端基本上很流畅的就 ...

  3. 摄像头图像分析目标物体大小位置_小目标检测的增强算法

    版权声明:本文来源 程序媛的小世界https://zhuanlan.zhihu.com/p/135397874 Augmentation for small object detection 摘要 近 ...

  4. 摄像头图像分析目标物体大小位置_摄像头的原理、组成、选型及应用

    目录 一.摄像头成像的光学原理 二.摄像头模组构成以及功能原理 三.摄像头名词释义 四.摄像头参数解析 五.摄像头选型 一.摄像头成像的光学原理 1.1 光学的基本定律 (1)光线直线传播定律:在统一 ...

  5. 摄像头图像分析目标物体大小位置_一文读懂图像定位及跟踪技术

    文 | 传感器技术 在科学技术日新月异的今天,人们对机器设备的智能性.自主性要求也越来越高,希望其完全替代人的角色,把人们从繁重.危险的工作任务中解脱出来,而能否像人一样具有感知周围环境的能力已成为设 ...

  6. 摄像头图像分析目标物体大小位置_对智能驾驶系统三种环境传感器布局的冗余关系分析...

    " 目前的自动驾驶汽车需要三类传感器,第一类是IMU传感器.第二类是摄像头.毫米波雷达.激光雷达这类感知传感器.第三类是V2X这类超视距的特殊传感器.惯性测量单元的功能毫无疑问,是测量物体三 ...

  7. 摄像头图像分析目标物体大小位置_单个运动摄像头估计运动物体深度,谷歌挑战新难题...

    雷锋网 AI 科技评论按:人类视觉系统有一个我们习以为然但其实极其强大的功能,那就是可以从平面图像反推出对应的三维世界的样子.即便在有多个物体同时移动的复杂环境中,人类也能够对这些物体的几何形状.深度 ...

  8. 解决JS拖拽出现的问题

    解决JS拖拽出现的问题 参考文章: (1)解决JS拖拽出现的问题 (2)https://www.cnblogs.com/yuanjingjing/p/10154198.html 备忘一下.

  9. html元素拖动互换位置原理,【详】JS实现拖拽元素互换位置

    写在前面的废话 大家好,我是练习js时长接近两年半的个人练习生--李大雷 算了,直接 鸡,你太美~ 应用场景 很多时候,我们需要让用户来自定义自己想要的菜单顺序,或者一些按钮的排序,那么这个时候,怎么 ...

最新文章

  1. Cannot initialize a parameter of type ‘NSArray<id<RCTBridgeModule>> *‘ with an rvalue of type ‘NSArr
  2. Elasticsearch集群和索引常用命令
  3. Ubuntu18.04深度学习环境配置(简易方式)
  4. 《认知红利》读书笔记
  5. 静态多态之泛型编程(模板)
  6. vspy如何在图形面板显示报文_设备实时状态监控:如何进行工业生产设备数据采集?...
  7. 最近程序员频繁被抓,如何避免面向监狱编程?!
  8. Robin负载均衡策略存在问题及CSE解决方案
  9. word图片靠右_如何设置word图片边缘透明
  10. windows下利用批处理命令生成maven项目(java、javaWeb)
  11. 怎么改掉科学计数法_宝宝口唇干裂怎么回事?该怎么办?
  12. Python学习笔记--CSV模块读写数据(转)
  13. 张正友标定算法原理详解(一)
  14. 停电后文件变成二进制 修复_尝试并修补二进制启发
  15. 任正非:为什么华为选择与西工大合作,而没选清华北大
  16. ABAQUS中橡胶大变形问题的解决方案
  17. python qrcode库生成二维码的代码
  18. C Primer Plus编程题-第五章 运算符、表达式和语句
  19. socketio java如何知道消息被接收成功_5年经验Java程序员面试20天,拿下数个offer,总结出的经验感想!...
  20. 大事情!中国限制 AI 算法出口。网友:这是要阻止XX“下跪”

热门文章

  1. 最懒惰的算法—KNN
  2. SAP Spartacus PageLayoutComponent 如何知道自己应该显示哪些具体内容
  3. 如何使用 jMeter CSV Data Set config
  4. 什么是 SAP Core Data Service Annotation Propagation
  5. SAP Spartacus 和 SmartEdit 协同工作需要遵循的协议
  6. RxJs combineLatest Operator 的数组用法
  7. 从调试器里观察到的Observable对象反推出其赋值的源代码位置
  8. SAP Spartacus scss里的--cx-color-primary
  9. 本人使用abapgit遇到的一些错误
  10. Extension project: 404 Not Found for resources/cus/crm/notes/ext/Component-dbg.js