纯 JS 实现放大缩小拖拽功能(完整代码)


Posted in Javascript onNovember 25, 2019

前言

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

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

先看效果

纯 JS 实现放大缩小拖拽功能(完整代码) 

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

三个坑点

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

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

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

纯 JS 实现放大缩小拖拽功能(完整代码) 

这个效果是我们不想看到的,所以我们就需要区分开采宝上的 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('触发鼠标抬起')
}

然后我们得到的结果是:

纯 JS 实现放大缩小拖拽功能(完整代码) 

通过控制台的输出情况,我们就可以看到鼠标点击后的各个事件触发情况:首先执行的是 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;
 } 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";
}

效果如下

纯 JS 实现放大缩小拖拽功能(完整代码) 

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

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

纯 JS 实现放大缩小拖拽功能(完整代码) 

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

完整的代码

HTML:

<div class="box">
 <div class="move">
 <img
  onclick="moveBoxClick()"
  class="small-img"
  draggable="false"
  src="https://zcy-cdn.oss-cn-shanghai.aliyuncs.com/f2e-assets/103bbf76-6248-421c-a3d6-28a525c459db.png"
  alt=""
 />
 <img
  onclick="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";
}

总结

以上所述是小编给大家介绍的纯 JS 实现放大缩小拖拽功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
关于jquery ajax 调用带参数的webservice返回XML数据一个小细节
Jul 31 Javascript
关于innerHTML后丢失动态绑定的EVENT问题解决方法
May 19 Javascript
刷新页面的几种方法小结(JS,ASP.NET)
Jan 07 Javascript
innerText 使用示例
Jan 23 Javascript
javascript中call,apply,bind函数用法示例
Dec 19 Javascript
JavaScript组件开发之输入框加候选框
Mar 10 Javascript
vue检测对象和数组的变化分析
Jun 30 Javascript
JS实现图片拖拽交换效果
Nov 30 Javascript
详解React服务端渲染从入门到精通
Mar 28 Javascript
微信小程序返回上一页传参并刷新过程解析
Dec 13 Javascript
node.js 微信开发之定时获取access_token
Feb 07 Javascript
微信小程序实现搜索功能
Mar 10 Javascript
python实现迭代法求方程组的根过程解析
Nov 25 #Javascript
JS桶排序的简单理解与实现方法示例
Nov 25 #Javascript
JavaScript交换两个变量方法实例
Nov 25 #Javascript
three.js利用gpu选取物体并计算交点位置的方法示例
Nov 25 #Javascript
基于javascript实现贪吃蛇小游戏
Nov 25 #Javascript
JavaScript This指向问题详解
Nov 25 #Javascript
简单了解JavaScript sort方法
Nov 25 #Javascript
You might like
967 个函式
2006/10/09 PHP
yii2中LinkPager增加总页数和总记录数的实例
2017/08/28 PHP
浅析PHP类的反射来实现依赖注入过程
2018/02/06 PHP
php精度计算的问题解析
2019/06/21 PHP
thinkphp5修改view到根目录实例方法
2019/07/02 PHP
ext jquery 简单比较
2010/04/07 Javascript
jQuery类选择器用法实例
2014/12/23 Javascript
JS中获取函数调用链所有参数的方法
2015/05/07 Javascript
javaScript中push函数用法实例分析
2015/06/08 Javascript
SpringMVC restful 注解之@RequestBody进行json与object转换
2015/12/10 Javascript
使用jQuery UI库开发Web界面的简单入门指引
2016/04/22 Javascript
Jquery实现遮罩层的简单实例(就是弹出DIV周围都灰色不能操作)
2016/07/14 Javascript
原生JS和jQuery操作DOM对比总结
2017/01/19 Javascript
简单实现AngularJS轮播图效果
2020/04/10 Javascript
详解vue.js之绑定class和style的示例代码
2017/08/24 Javascript
javascript基于牛顿迭代法实现求浮点数的平方根【递归原理】
2017/09/28 Javascript
vue中实现滚动加载更多的示例
2017/11/08 Javascript
JavaScript实现鼠标经过表格某行时此行变色
2020/11/20 Javascript
收藏整理的一些Python常用方法和技巧
2015/05/18 Python
django开发教程之利用缓存文件进行页面缓存的方法
2017/11/10 Python
Window 64位下python3.6.2环境搭建图文教程
2018/09/19 Python
详解Numpy中的数组拼接、合并操作(concatenate, append, stack, hstack, vstack, r_, c_等)
2019/05/27 Python
使用Rasterio读取栅格数据的实例讲解
2019/11/26 Python
在python中logger setlevel没有生效的解决
2020/02/21 Python
基于Numba提高python运行效率过程解析
2020/03/02 Python
详解Python3中的 input() 函数
2020/03/18 Python
Python3爬虫中识别图形验证码的实例讲解
2020/07/30 Python
Python JSON常用编解码方法代码实例
2020/09/05 Python
详解HTML5将footer置于页面最底部的方法(CSS+JS)
2018/10/11 HTML / CSS
Vans荷兰官方网站:美国南加州的原创极限运动潮牌
2018/01/23 全球购物
Myholidays美国:在线旅游网站
2019/08/16 全球购物
车间主管岗位职责
2013/11/14 职场文书
2015欢度元旦标语口号
2014/12/09 职场文书
导游词之南昌滕王阁
2019/11/29 职场文书
PHP设计模式(观察者模式)
2021/07/07 PHP
使用Redis实现分布式锁的方法
2022/06/16 Redis