点击上方“前端公虾米”,选择“置顶或者星标”

你的关注意义重大!

前言

最近团队需要做一个智能客服悬浮窗功能,需要支持拖动、放大缩小等功能,因为这个是全局插件,为了兼容性考虑全部使用原生 JS 实现,不引用任何第三方库或者插件。开发过程中遇到的一些问题及解决方法,在这里和大家分享交流一下。

注:下文出现的“采宝”二字,为这个功能的产品名。

先看效果


看这个效果,相信大部分开发都会觉得实现起来比较容易。在实际开发中,笔者总结了三个主要的坑点,及其解决方案。

三个坑点

  • 拖拽采宝时会导致采宝放大缩小
  • 采宝显示在屏幕边界时被遮挡显示不全
  • 采宝放大和缩小后,位置发生变化

(一)拖拽时会导致采宝放大缩小

我们在操作采宝时,不管是鼠标拖动还是点击放大缩小,我们的事件都需要绑定在采宝头部的图标上,这样我们就需要在图标上同时绑定点击和拖拽事件。但是当我们直接添加 click 事件和 mousedown 事件的时候,我们发现在触发 mousedown 事件的时候,也会去触发 click 事件。这样就会出现在拖动采宝的时候,采宝会放大和缩小。


这个效果是我们不想看到的,所以我们就需要区分开采宝上的 click 事件和 mousedown 事件,想办法使两个事件的触发相互不影响。

所以我们在同一个 DIV 上同时绑定 mousedown 事件和 click 事件,然后通过控制台输出每个事件,查看过程中的每个事件的触发顺序。

const moveBox = document.querySelector('.move');moveBox.onmousedown = function (evt) {  console.log('触发鼠标按下')  moveBox.onmousemove = function (evt) {console.log('触发鼠标拖动')  }}function moveBoxClick(e) {  console.log('触发click')}moveBox.onmouseup = function () {  console.log('触发鼠标抬起')}

然后我们得到的结果是:

通过控制台的输出情况,我们就可以看到鼠标点击后的各个事件触发情况:首先执行的是 mousedown 事件,然后是 mousemove 事件,再然后是 mouseup 事件,最后是 click 事件。

知道了事件的触发顺序,我们就可以通过设置一个变量 isMove 来区分开鼠标的拖动事件和点击事件,每次鼠标按下的时候我们将 isMove 复原,鼠标移动的时候将 isMove 的状态改变。

因为每次触发 click 事件的时候也都会去先去触发 mousedown 事件,所以我们在 click 事件里增加一个判断,鼠标移动时,不触发 click 事件。这样就可以把 click 事件和 mousedown 事件区分开来,实现 mousedown 和 click 事件的隔离。

click 事件增加判断

function moveBoxClick(e) {// 点击采宝const target = document.querySelector(".move");const smallImg = document.querySelector(".small-img");const magnifyImg = document.querySelector(".magnify-img");// 点击move盒子if (!isMove) {if (isBig) {      smallImg.style.display = "block";      magnifyImg.style.display = "none";      target.style.width = "32px";    } else {      smallImg.style.display = "none";      magnifyImg.style.display = "block";      target.style.width = "130px";    }    isBig = !isBig;  }}

mousedown 事件重置 isMove 和 mousemove 改变 isMove

let isMove = false; // 是否是拖动let isBig = false; // 是否是变大的盒子let isMove = false; // 判断是否移动采宝smallImg.onmousedown = magnifyImg.onmousedown = function(evt) {  isMove = false; // 每次鼠标按下时,重置isMovedocument.onmousemove = function(e) {    isMove = true; // 每次鼠标移动时,改变isMove  };};

通过 isMove 的状态,我们就可以区分开 mousemove 事件和 click 事件,使得我们在拖动采宝的时候,可以不去触发采宝放大缩小。

(二)采宝显示在屏幕边界时被遮挡显示不全

我们在拖动采宝时,判断采宝拖动的当前定位坐标是否超出了当前显示屏的高度和宽度,我们需要限制采宝拖动的最大距离。小采宝在点击放大时,也需要做一下处理,把采宝全部显示出来。

拖动时

const moveBox = document.querySelector(".move");const smallImg = document.querySelector(".move .small-img");const magnifyImg = document.querySelector(".move .magnify-img");let isMove = false; // 是否是拖动let isBig = false; // 是否是变大的盒子

smallImg.onmousedown = magnifyImg.onmousedown = function(evt) {// 拖动div盒子const clientX = evt.clientX;const clientY = evt.clientY;const pageX = moveBox.offsetLeft;const pageY = moveBox.offsetTop;const x = clientX - pageX;const y = clientY - pageY;

document.onmousemove = function(e) {// 拖动后采宝的坐标let _x = e.clientX - x;let _y = e.clientY - y;const boxWidth = moveBox.offsetWidth;const boxHeight = moveBox.offsetHeight;if (_x < 0) {      _x = 0;    }// X坐标的最大值if (_x > window.screen.width - boxWidth) {      _x = window.screen.width - boxWidth;    }if (_y < 0) {      _y = 0;    }// Y坐标的最大值if (_y > document.documentElement.clientHeight - boxHeight) {      _y = document.documentElement.clientHeight - boxHeight;    }  };};

小采宝在边界放大时

// 点击时,判断采宝是否超出显示屏function autoPotion () {let x = moveBox.offsetLeft;let y = moveBox.offsetTop;

if (x < 0) {    x = 0;  } else if (x > document.documentElement.clientWidth - moveBox.offsetWidth) {    x = document.documentElement.clientWidth - moveBox.offsetWidth;  }

if (y < 0) {    y = 0;  } else if (y > document.documentElement.clientHeight - moveBox.offsetHeight) {    y = document.documentElement.clientHeight - moveBox.offsetHeight;  }

  moveBox.style.left = x + "px";  moveBox.style.top = y + "px";}

效果如下


(三)采宝放大和缩小后,位置发生变化

通过上图,我们可以看到,当小采宝处在显示屏边界时,点击放大后再点击缩小,我们发现采宝的位置发生了变化。这个是因为采宝是根据左上角的坐标来定位的,当小采宝移动到右下角时,点击放大以后,采宝左上角的坐标发生了变化,这样就使得采宝在放大缩小时,位置在发生变化。所以,我们在采宝移动完成时需要记录采宝左上角的坐标,在点击时,需要将采宝上次移动完成的坐标重新赋值给采宝,这样就使得采宝在放大缩小时,位置不会发生变化。


这样,我们把每次 mouseup 事件的时候记录下采宝的位置,这样我们解决了采宝放大缩小时位置发生变化的问题。

完整的代码

HTML:

<div class="box"><div class="move"><imgonclick="moveBoxClick()"class="small-img"draggable="false"src="https://zcy-cdn.oss-cn-shanghai.aliyuncs.com/f2e-assets/103bbf76-6248-421c-a3d6-28a525c459db.png"alt=""    /><imgonclick="moveBoxClick()"class="magnify-img"draggable="false"src="https://zcy-cdn.oss-cn-shanghai.aliyuncs.com/f2e-assets/90e26f49-9824-4443-b4aa-8aa64a3c8690.png"alt=""    /><div class="content">div>div>div>

JavaScript

const moveBox = document.querySelector(".move");const smallImg = document.querySelector(".move .small-img");const magnifyImg = document.querySelector(".move .magnify-img");var initX = 0; // 记录小采宝的x坐标var initY = 0; // 记录小采宝的y坐标let isMove = false; // 是否是拖动let isBig = false; // 是否是变大的盒子

smallImg.onmousedown = magnifyImg.onmousedown = function(evt) {// 拖动div盒子const clientX = evt.clientX;const clientY = evt.clientY;const pageX = moveBox.offsetLeft;const pageY = moveBox.offsetTop;const x = clientX - pageX;const y = clientY - pageY;

  isMove = false;

document.onmousemove = function(e) {const boxWidth = moveBox.offsetWidth;const boxHeight = moveBox.offsetHeight;let _x = e.clientX - x;let _y = e.clientY - y;if (_x < 0) {      _x = 0;    }if (_x > window.screen.width - boxWidth) {      _x = window.screen.width - boxWidth;    }if (_y < 0) {      _y = 0;    }if (_y > document.documentElement.clientHeight - boxHeight) {      _y = document.documentElement.clientHeight - boxHeight;    }

if (isBig) {      initX = _x;      initY = _y;    }

    moveBox.style.left = _x + "px";    moveBox.style.top = _y + "px";

    isMove = true;  };};

document.onmouseup = function() {if (isMove) {    initX = moveBox.offsetLeft;    initY = moveBox.offsetTop;  }document.onmousemove = null;};

function moveBoxClick(e) {const target = document.querySelector(".move");const smallImg = document.querySelector(".small-img");const magnifyImg = document.querySelector(".magnify-img");// 点击move盒子if (!isMove) {if (isBig) {      smallImg.style.display = "block";      magnifyImg.style.display = "none";      target.style.width = "32px";      target.style.left = initX + 'px';      target.style.top = initY + 'px';    } else {      smallImg.style.display = "none";      magnifyImg.style.display = "block";      target.style.width = "130px";    }    isBig = !isBig;

    setTimeout(() => {      autoPotion();    }, 100)  }}

// 点击时,判断采宝是否超出显示屏function autoPotion () {let x = moveBox.offsetLeft;let y = moveBox.offsetTop;

if (x < 0) {    x = 0;  } else if (x > document.documentElement.clientWidth - moveBox.offsetWidth) {    x = document.documentElement.clientWidth - moveBox.offsetWidth;  }

if (y < 0) {    y = 0;  } else if (y > document.documentElement.clientHeight - moveBox.offsetHeight) {    y = document.documentElement.clientHeight - moveBox.offsetHeight;  }

  moveBox.style.left = x + "px";  moveBox.style.top = y + "px";} 

❤️ 最后

如果你觉得这篇内容对你有帮助,点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)

bootstrap缩小后div互相叠加_纯 JS 实现放大缩小拖拽踩坑之旅相关推荐

  1. F2工作流引擎之-纯JS Web在线可拖拽的流程设计器(八)

         Web纯JS流程设计器无需编程,完全是通过鼠标拖.拉.拽的方式来完成,支持串行.并行.分支.异或分支.M取N路分支.会签.聚合.多重聚合.退回.传阅.转交,都可以非常方便快捷地实现,管理员还 ...

  2. java使用重绘实现拖动_原生JS使用Canvas实现拖拽式绘图功能

    一.实现的功能 1.基于oop思想构建,支持坐标点.线条(由坐标点组成,包含方向).多边形(由多个坐标点组成).圆形(包含圆心坐标点和半径)等实体 2.原生JavaScript实现,不依赖任何第三方j ...

  3. draggable禁止拖动_通过 JS 实现简单的拖拽功能并且可以在特定元素上禁止拖拽...

    前言 关于讲解 JS 的拖拽功能的文章数不胜数,我确实没有必要大费周章再写一篇重复的文章来吸引眼球.本文的重点是讲解如何在某些特定的元素上禁止拖拽.这是我在编写插件时遇到的问题,其实很多插件的拖拽功能 ...

  4. Js实现放大缩小html页面整体

    Js实现放大缩小html页面整体 不包含iframe的写法 包含iframe的写法 不包含iframe的写法 html代码 <body><div ><button onc ...

  5. JQuery Dialog(JS模态窗口,可拖拽的DIV)

    JQuery Dialog(JS模态窗口,可拖拽的DIV) 吴剑 2012-08-08 原创文章,转载必需注明出处:http://www.cnblogs.com/wu-jian/ 效果图 调用示意图 ...

  6. js实现放大缩小页面

    js实现放大缩小页面 在firefox,chrom,ie11测试过可以,其它未测 <script type="text/javascript"> var size = ...

  7. jsplumb拖线_基于jsplumb插件制作可拖拽、保存流程图、重绘保存后的流程图总结...

    1.重点参考博文 https://blog.csdn.net/j_bean/article/details/78092647 2.关键点总结 1)实现可视区域图形画满后,拖动整个画布的效果 a.最好不 ...

  8. react加水印_多功能React影像组件(拖拽、水印、缩放、切换、旋转)

    cxj-react-image 用法如下: yarn add cxj-react-image // npm i cxj-react-image import ImageModal from 'cxj- ...

  9. 图片上传组件_博客必备功能,拖拽上传图片!

    公众号关注 "GitHub 热门" 设为 "星标",带你了解技术圈内热门新鲜事! 现在很多小伙伴都在搭建自己的博客,把自己的一些心得和经验都写在博客上面给一些新 ...

最新文章

  1. 中国矿业大学计算机英语复试,中国矿业大学2020计算机科学与技术学院考研复试科目参考书目...
  2. Windows7下最新Android开发环境搭建(JDK1.7 + Eclipse Indigo(3.7) + SDK-r12 + ADT-12)
  3. ExtJs_关于combobox的那些分页二三事
  4. Nature今年首次撤稿给了微软:研究团队成员自曝删改不利数据,量子计算重大进展是假的...
  5. HTTPS通信的C++实现
  6. 笔记-信息系统开发基础-面向对象基本概念-对象和类
  7. CodeForces - 1561E Bottom-Tier Reversals(构造)
  8. 编译器预定义宏的查看和使用
  9. 隐藏/显示jeecg-boot 后端管理页面的右侧的系统设置
  10. 拉斯维加斯算法结合八皇后问题
  11. 基于JAVA的在线图书销售系统
  12. CENTOS安装后没有图形界面GUI,怎么办?
  13. 贪心算法三个经典例题
  14. 芯片级维修一台指示灯全不亮的小米路由器PRO
  15. JAVA:一个简易的文本编辑器
  16. 知网下载文献格式caj/pdf
  17. OTDR常见测试曲线​
  18. Index.ANALYZED in lucene4 is deprecated
  19. 分布式理论,架构设计
  20. 问题 J: Frosting on the Cake

热门文章

  1. c 服务器通信和文件传输,服务器、终端和文件传输方法
  2. oracle全文索引的简单配置
  3. with as 用法
  4. mxnet与pytorch比较 bn层
  5. RGBD-SLAM 深度摄像机资料介绍
  6. insightface mxnet训练 旧版
  7. 轻量级检测网络Pelee和DenseNet
  8. A-Light-and-Fast-Face-Detector-for-Edge-Devices
  9. Run-Time Check Failure #2
  10. Corrupt JPEG data: 2 extraneous bytes before marker 0x