移动端JS实现拖拽两种方法解析


Posted in Javascript onOctober 12, 2020

移动端的项目经常会引入手势库来实现拖拽

不过如果只是一两个页面用到拖拽,再引入一个手势库就很不划算

最近的项目中就有这么一个需求:

移动端JS实现拖拽两种方法解析

因为就这一个地方需要拖拽,所以我就没有引入第三方库

移动端的拖拽有两种主流的实现方案:

1. 将元素设置为固定定位,然后在拖拽的时候修改其定位,实现拖拽的效果;

2. 使用 transform 中的平移translate 属性实现拖拽。

方案一:固定定位 fixed

这种方案的核心就是给元素添加固定定位position:fixed;

但定位之后,元素会脱离文档流,会影响原有但布局

因此在开始拖拽 (触发touchstart事件) 的时候,需要将原本的元素 A 拷贝一份 (cloneNode())

给新元素 A2 添加定位,同时给原本的元素 A 设置visibility:hidden; 使之隐藏并占位

1.1 创建遮罩

首先封装一个创建遮罩的方法,用于放置拷贝出来的元素,并防止误触

createModal (id) {
 let modal = document.getElementById(id)
 if (!modal) { // 在没有遮罩的时候创建遮罩
  modal = document.createElement('div')
  modal.id = id
  modal.style.cssText = `position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 999;`
  document.body.appendChild(modal)
 }
},

1.2 开始拖拽

在触发touchstart事件的时候,首先创建遮罩

并通过getBoundingClientRect()方法获取到元素 A 的坐标,记录起点信息

为了记录起点信息,需要 data 中创建一个对象source,用于记录点击的位置 client,和初始定位坐标 start

handleTouchstart (e) { // 开始拖拽
 // 创建遮罩层
 this.createModal(this.modalID) // modalID 遮罩层的id,由外部定义
 let element = e.targetTouches[0]
 let target = e.target.cloneNode(true) // 拷贝目标元素
 target.id = this.copyID // copyID 拷贝元素的id,由外部定义
 // 记录初始点击位置 client,用于计算移动距离
 this.source.client = {
  x: element.clientX,
  y: element.clientY
 }
 // 算出目标元素的固定位置
 let disX = this.source.start.left = element.target.getBoundingClientRect().left
 let disY = this.source.start.top = element.target.getBoundingClientRect().top
 target.style.cssText = `position: fixed; left: ${disX}px; top: ${disY}px;`
 // 将拷贝的元素放到遮罩中
 document.getElementById(this.modalID).appendChild(target)
},

1.3 处理拖拽

拖拽的时候,监听touchmove事件

用【当前鼠标点位置】减去【初始点击位置】得到移动的距离

再结合初始坐标信息,更新拖拽元素的坐标

handleTouchmove (e) { // 拖拽中
 let element = e.targetTouches[0]
 let target = document.getElementById(this.copyID)
 // 根据初始点击位置 client 计算移动距离
 let left = this.source.start.left + element.clientX - this.source.client.x
 let top = this.source.start.top + element.clientY - this.source.client.y
 // 移动当前元素
 target.style.left = `${left}px`
 target.style.top = `${top}px`
},

1.4 拖拽结束

拖拽结束的时候,记录终点位置,删除遮罩

handleTouchend (e) { // 拖拽结束
 let end = {
  x: e.changedTouches[0].clientX,
  y: e.changedTouches[0].clientY
 }
 // 删除遮罩层
 let modal = document.getElementById(this.modalID)
 document.body.removeChild(modal)
 // 处理结果
 this.doingSth(end)
},

不过上面的代码只实现了拖拽的功能,并没有对目标元素 A 进行显示/隐藏的操作

可以根据业务场景自行添加,或者参考方案二

方案二:平移动画translate

这种方案更为简单,不需要创建额外的 DOM 元素

只需对原本的元素添加 transform 属性,甚至不需要 transition 属性

然后在拖拽过程中,实时更新transform: translate(X, Y)中 x, y 的坐标信息,实现拖拽

2.1 开始拖拽

开始拖拽的时候,只需要记录起点坐标

handleTouchstart (e) { // 开始拖拽
 let element = e.targetTouches[0]// 记录初始 client 位置,用于计算移动距离
 this.source.client = {
  x: element.clientX,
  y: element.clientY
 }
},

为了防止拖拽的过程中误触,建议使用方案一的createModal()方法创建一个遮罩

2.2 处理拖拽

根据当前坐标和起点坐标,计算出距离,然后更新 translate 的坐标

handleTouchmove (e) { // 拖拽中
 let element = e.targetTouches[0]
 // 根据初始 client 位置计算移动距离
 let x = element.clientX - this.source.client.x
 let y = element.clientY - this.source.client.y
 // 移动当前元素
 element.target.style.cssText = `transform: translate(${x}px, ${y}px);`
},

2.3 拖拽结束

拖拽完成后,清除平移动画

handleTouchend (e) { // 拖拽结束
 // 清除拖拽样式
 e.target.style.cssText = `transform: none;`
},

小结:

方案一在获取目标元素的坐标信息的时候使用了 getBoundingClientRect() 方法

但这个方法性能不高,应当少用

而且即时使用了该方法,最后得到的 left 和 top 也不够精确,touchstart 的时候,元素有明显的闪动

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
使用jQuery实现dropdownlist的联动效果(sharepoint 2007)
Mar 30 Javascript
javascript 实现 秒杀,团购 倒计时展示的记录 分享
Jul 12 Javascript
深入学习AngularJS中数据的双向绑定机制
Mar 04 Javascript
Javascript的表单与验证-非空验证
Mar 18 Javascript
jQuery EasyUI API 中文帮助文档和扩展实例
Aug 01 Javascript
Vue + Webpack + Vue-loader学习教程之功能介绍篇
Mar 14 Javascript
JavaScript获取tr td 的三种方式全面总结(推荐)
Aug 15 Javascript
Three.js实现绘制字体模型示例代码
Sep 26 Javascript
完美解决linux下node.js全局模块找不到的情况
May 16 Javascript
微信小程序自定义音乐进度条的实例代码
Aug 28 Javascript
JavaScript中构造函数与原型链之间的关系详解
Feb 25 Javascript
JS 4个超级实用的小技巧 提升开发效率
Oct 05 Javascript
JavaScript读取本地文件常用方法流程解析
Oct 12 #Javascript
vue实现移动端返回顶部
Oct 12 #Javascript
用JavaScript实现贪吃蛇游戏
Oct 23 #Javascript
手机浏览器唤起微信分享(JS)
Oct 11 #Javascript
js canvas实现俄罗斯方块
Oct 11 #Javascript
利用js canvas实现五子棋游戏
Oct 11 #Javascript
H5+css3+js搭建带验证码的登录页面
Oct 11 #Javascript
You might like
最省空间的计数器
2006/10/09 PHP
php简单的会话类代码
2011/08/08 PHP
php中导出数据到excel时数字变为科学计数的解决方法
2013/02/03 PHP
php结合mysql与mysqli扩展处理事务的方法
2016/06/29 PHP
yii2使用gridView实现下拉列表筛选数据
2017/04/10 PHP
PHP中register_shutdown_function函数的基础介绍与用法详解
2017/11/28 PHP
JS HTML5 音乐天气播放器(Ajax获取天气信息)
2013/05/26 Javascript
JS使用replace()方法和正则表达式进行字符串的搜索与替换实例
2014/04/10 Javascript
js实现顶部可折叠的菜单工具栏效果实例
2015/05/09 Javascript
理解javascript异步编程
2016/01/27 Javascript
javascript断点调试心得分享
2016/04/23 Javascript
微信小程序实现弹出菜单
2018/07/19 Javascript
js canvas实现红包照片效果
2018/08/21 Javascript
nodejs 使用nodejs-websocket模块实现点对点实时通讯
2018/11/28 NodeJs
解决微信小程序中转换时间格式IOS不兼容的问题
2019/02/15 Javascript
layui实现数据分页功能
2019/07/27 Javascript
vue实现在线预览pdf文件和下载(pdf.js)
2019/11/26 Javascript
webpack打包html里面img后src为“[object Module]”问题
2019/12/22 Javascript
javascript+Canvas实现画板功能
2020/06/23 Javascript
我所理解的JavaScript中的this指向
2020/09/04 Javascript
python实现可将字符转换成大写的tcp服务器实例
2015/04/29 Python
python3编码问题汇总
2016/09/06 Python
python获取指定字符串中重复模式最高的字符串方法
2018/06/29 Python
Python3爬虫学习入门教程
2018/12/11 Python
如何使用selenium和requests组合实现登录页面
2020/02/03 Python
python实现翻译word表格小程序
2020/02/27 Python
OpenCV 表盘指针自动读数的示例代码
2020/04/10 Python
python单元测试框架pytest的使用示例
2020/10/07 Python
CSS3使用多列制作瀑布流
2016/05/10 HTML / CSS
美国网上订购鲜花:FTD
2016/09/23 全球购物
澳大利亚领先的在线美容商城:Adore Beauty
2017/04/14 全球购物
Puccini乌克兰:购买行李箱、女士手袋网上商店
2020/08/06 全球购物
计算机操作自荐信
2013/12/07 职场文书
4S店销售内勤岗位职责
2015/04/13 职场文书
SQL Server2019数据库备份与还原脚本,数据库可批量备份
2021/11/20 SQL Server
SpringBoot深入分析讲解监听器模式下
2022/07/15 Java/Android