纯 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;
 } 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">
 <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 实现放大缩小拖拽功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • vue.js+Echarts开发图表放大缩小功能实例

    最近使用echarts来开发某系统的图表功能,先申明我以前用的ext.js,ext.js对图表有自己的一套组件,用起来也很方便.但是由于ext.js过于臃肿,公司决定使用echarts来开发图表功能.当我们使用的时候才悲催的发现,echart绘制之后,不能随着容器div的大小而变化.而我们所开发的图表是需要有放大缩小功能,于是在网上找了很久,也没有找到合适的答案,大部分是通过监听窗口大小改变事件来设置,然而并不是我们所需要的.于是自己用了一点点时间,了解了为何echarts不能重新渲染,原来是在

  • JS实现图片局部放大或缩小的方法

    本文实例讲述了JS实现图片局部放大或缩小的方法.分享给大家供大家参考,具体如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <he

  • JavaScript实现点击按钮字体放大、缩小

    本文给大家分享js实现点击按钮字体放大缩小实例代码,代码简单易懂,需要的朋友参考下吧 具体代码如下所示: <style> .bb{color:red;} .cc{color:green;} .chapter {font-size: 1.5em;} .normal{font-size:12px;} .hidden{display:none;} </style> <script> $(document).ready(function() { $('div.aa').addC

  • JS简单的图片放大缩小的两种方法

    以左上角为定点,放大缩小,该点位置不变. 方法一: Html代码 复制代码 代码如下: <script type="text/javascript">         //兼容IE和火狐   缩小放大.缩放         function ImageSuofang(args) {             var oImg = document.getElementById("oImg");             if (args) {        

  • JS实现图片放大缩小的方法

    本文实例讲述了JS实现图片放大缩小的方法.分享给大家供大家参考,具体如下: 最近经常看见有人问怎样放大和缩小图片,我之前也做过一次,下面就把我的方法共享出来.我有2个种方法实现:第一种方法可以兼容IE和火狐(其他的浏览器我没有测试):第二种方法只能兼容IE. 第一种方法很简单,代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Image.aspx.cs" Inhe

  • js实现图片旋转 js滚动鼠标中间对图片放大缩小

    从开通博客园到今天,有两个多月了.我发现之前没有开通博客记录自己所做的东西,真是后悔啊. 现在一点一点把自己所做的功能以博客的形式记录下来,一方面可以给大家分享,大家一起学习,同时自己也从新回顾一下. 这个图片放大,缩小和旋转,我采用canvas画布这个来做的,核心点就在js中去控制鼠标状态及事件. 我先给大家展示一下效果图. 鼠标移到画布范围内就会出现下方的操作栏,每次以90度选择. 1.在引入js的时候一定要注意了,由于在使用画布canvas时,需要等图片加载完成后才可以执行画布里的内容.j

  • JS实现放大、缩小及拖拽图片的方法【可兼容IE、火狐】

    本文实例讲述了JS实现放大.缩小及拖拽图片的方法.分享给大家供大家参考,具体如下: var divId; var v_left; var v_top; window.onload=function(){ divId = document.getElementById("block1"); var height1 = images1.height;//图片的高度 var width1 = images1.width;//图片的宽度 v_left=(document.body.client

  • js实现图片放大缩小功能后进行复杂排序的方法

    这是一个基于jquery的图片效果,它的作用是:当图片点击变大(变小)时,其它图片按照一定的规则进行排序运动. 首先来看下最终的效果图: 有人可能看到这个会觉得,这有什么难的,这么简单的事,楼主是不是太小提大作了?当你真正去尝试时,你才知道到底有什么难点. 首先,我们来讲下需求: 1.图片分为大小和小图,大图占四个小图的位置, 2.点击图片放大缩小, 重新排列顺序, 3. 当点击偶数列(2,4)时,它前面的那项将向提出来向后面排列 4.第一个项不能动,点第二个是将占第三四的位置,从后面取第一个小

  • js实现图片缓慢放大缩小效果

    本文实例为大家分享了图片缓慢放大缩小js实现代码,供大家参考,具体内容如下 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>图片的缓慢缩放</title> </head> <script> window.onload = function(){ var btn_big = docu

  • JavaScript实现图片的放大缩小及拖拽功能示例

    本文实例讲述了JavaScript实现图片的放大缩小及拖拽功能.分享给大家供大家参考,具体如下: 实现效果如下: 实现代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> div{width:400px;height:400px;overflow:

随机推荐