您好,登錄后才能下訂單哦!
前言
最近團隊需要做一個智能客服懸浮窗功能,需要支持拖動、放大縮小等功能,因為這個是全局插件,為了兼容性考慮全部使用原生 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 實現放大縮小拖拽功能,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。