写点什么

纯 JS 实现放大缩小拖拽采坑之旅

2021 年 3 月 06 日

纯 JS 实现放大缩小拖拽采坑之旅

前言


最近团队需要做一个智能客服悬浮窗功能,需要支持拖动、放大缩小等功能,因为这个是全局插件,为了兼容性考虑全部使用原生 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; // 每次鼠标按下时,重置isMove  document.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; } elseif (x > document.documentElement.clientWidth - moveBox.offsetWidth) { x = document.documentElement.clientWidth - moveBox.offsetWidth; }
if (y < 0) { y = 0; } elseif (y > document.documentElement.clientHeight - moveBox.offsetHeight) { y = document.documentElement.clientHeight - moveBox.offsetHeight; }
moveBox.style.left = x + "px"; moveBox.style.top = y + "px";}
复制代码


效果如下



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


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



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


总结


通过开发一个很小的功能点,引出了很多需要处理的细节问题,提高用户体验。



头图:Unsplash

作者:微笑

原文:https://mp.weixin.qq.com/s/3MkahoyNl7-jJQXzk1sjpw

原文:纯 JS 实现放大缩小拖拽采坑之旅

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021 年 3 月 06 日 23:53968

评论

发布
暂无评论
发现更多内容

数据库SQL:视图

正向成长

第八周作业

TheSRE

极客大学架构师训练营

架构师训练营第 1 期 -Week8 - 课后练习

鲁小鲁

极客大学架构师训练营

Week4 作业

evildracula

学习 架构

架构师训练营第 8 周课后练习

叶纪想

架构师训练营第八周作业

Shunyi

极客大学架构师训练营

已经 2020 年了,真的要继续 RoR 么?

escray

面经 面试经历 101次面试

架构师训练营 第四周 作业

xiaomao

第八周作业

熊桂平

极客大学架构师训练营

系统架构 - 学习总结笔记

Xuenqlve

极客时间架构 1 期:第8周 性能优化(二) - 命题作业

Null

架构师训练营第 1 期 -Week8 - 性能优化二学习总结

鲁小鲁

极客大学架构师训练营

一个典型的大型互联网应用系统使用的技术方案

落朽

架构第八周总结

Geek_Gu

极客大学架构师训练营

极客时间架构 1 期:第8周 性能优化(二) - 学习总结

Null

第 4 周 系统架构作业

心在那片海

架构师训练营第八周命题作业

一马行千里

极客大学架构师训练营 命题作业

架构师训练营第八周总结

月殇

极客大学架构师训练营

架构师训练营 1 期第 8 周:性能优化(二)- 总结

piercebn

极客大学架构师训练营

第八 周 性能优化(二)作业

钟杰

极客大学架构师训练营

[架构师训练营第 1 期] 第八周学习总结

猫切切切切切

极客大学架构师训练营

架构师训练营第 1 期 - 第 8 周 - 命题作业

wgl

极客大学架构师训练营

架构师1期week08作业

FG佳

架构师一期

【架构师训练营第 1 期 08 周】 学习总结

Bear

极客大学架构师训练营

第八周作业

橘子皮嚼着不脆

判断两个单向链表是否合并

Jacky.Chen

架构师week08总结

FG佳

架构师训练营 2 期 - 第四周总结

Geek_no_one

极客大学架构师训练营

架构师训练营 - 作业 - 第八周

Max2012

第八周作业

Geek_ac4080

第八周总结

Geek_ac4080

2021年全国大学生计算机系统能力大赛操作系统设计赛 技术报告会

2021年全国大学生计算机系统能力大赛操作系统设计赛 技术报告会

纯 JS 实现放大缩小拖拽采坑之旅-InfoQ