使用JavaScript實(shí)現(xiàn)一個(gè)拖拽縮放效果
前言
在寫一個(gè)簡單的H5頁面時(shí),需要實(shí)現(xiàn)如下的一個(gè)拖拽效果,找了半天未能找到符合要求的,含淚手寫
先來看一下我們要是實(shí)現(xiàn)一個(gè)怎樣的效果
基本思路
- 鼠標(biāo)摁下,記錄元素的初始位置以及寬高
- 監(jiān)聽鼠標(biāo)的移動(dòng),根據(jù)鼠標(biāo)的移動(dòng)不斷改變自己的位置/寬高
- 是否存在邊界限制
拖拽實(shí)現(xiàn)
我們常見的改變元素位置的方式有
- 定位
- 使用translate進(jìn)行偏移
那我們現(xiàn)在用那種方式那實(shí)現(xiàn)拖拽呢?
從功能實(shí)現(xiàn)上來看,這兩個(gè)方式都能很好的實(shí)現(xiàn)我們的需求
從性能上來看,translate天生就是用來制作動(dòng)畫效果的,所以translate的性能以及流暢度都是要優(yōu)于定位的。
開始操作!!
<style> .box{ margin: 50px; width: 500px; height: 300px; border: 1px solid black; position: relative; } .drag{ height: 100px; width: 100px; background-color: #cbd; } </style> <div class="box"> <div class="drag"></div> </div> <script> let dragEl = document.querySelector(".drag") let container = document.querySelector(".box") let width, height, maxWidth, maxHeight, tx, ty, startX, startY // 初始化 function init() { // 為目標(biāo)元素設(shè)置初始的偏移,避免在第一次獲取偏移時(shí)為空的問題 dragEl.style.transform = "translate(0px,0px)" // 獲取父元素寬高 maxWidth = container.clientWidth maxHeight = container.clientHeight } function getInfo(e) { // 獲取元素當(dāng)前的寬高 width = dragEl.clientWidth height = dragEl.clientHeight // 獲取元素當(dāng)前的偏移量 let translateStr = dragEl.style.transform const reg = /\d+/g let translateArr = translateStr.match(reg) tx = Number(translateArr[0]) ty = Number(translateArr[1]) // 記錄鼠標(biāo)的起始位置 startX = e.clientX startY = e.clientY } function drag() { dragEl.addEventListener("mousedown", (e) => { getInfo(e) document.onmousemove = (e) => { let distanceX = tx + e.clientX - startX let distanceY = ty + e.clientY - startY dragEl.style.transform = `translate(${distanceX}px, ${distanceY}px)` } document.onmouseup = () => { document.onmousemove = null } }) } init() drag() </script>
通過上述代碼,我們已經(jīng)完成了元素的拖動(dòng),接下來需要考慮的就是,如果有邊界限制,我們又該如何實(shí)現(xiàn)
從上訴例子中,我們可以觀察出,元素偏移的最小值為0,最大值為父元素的寬高 - 目標(biāo)元素的寬高
所以在有邊界限制的情形下偏移量的計(jì)算方式為
let distanceX = Math.max(0, Math.min(tx + e.clientX - startX, maxWidth - width)) let distanceY = Math.max(0, Math.min(ty + e.clientY - startY, maxHeight - height))
縮放實(shí)現(xiàn)
這里我們以向左縮放為例
- 首先我們需要為目標(biāo)元素添加一個(gè)邊框,用來進(jìn)行縮放的操作
<style> .box{ margin: 50px; width: 500px; height: 300px; border: 1px solid black; position: relative; } .drag{ height: 100px; width: 100px; background-color: #cbd; } .left{ width: 10px; height: calc(100% - 14px); margin: 7px 0px; position: absolute; background-color: #000; cursor: col-resize; top: 0; left: -5px; } </style> <script> function addLeft() { left = document.createElement("div") left.className = "left" dragEl.append(left) } init() drag() addLeft() </script>
2.為左側(cè)的邊框添加縮放功能,因?yàn)槭亲髠?cè)的縮放,所以在寬度變化的同時(shí),需要不斷調(diào)整元素的位置,令其符合視覺效果
function leftZoom() { left.addEventListener("mousedown", (e) => { e.stopPropagation() getInfo(e) document.onmousemove = (e) => { // 注意這里是? newWidth = width - (e.clientX - startX) let distanceX = tx + (e.clientX - startX) dragEl.style.width = `${newWidth}px` dragEl.style.transform = `translate(${distanceX}px, ${ty}px)` } document.onmouseup = () => { document.onmousemove = null } }) } init() drag() addLeft() leftZoom()
- 限制元素縮放的最小值
let minWidth = 30 newWidth = Math.max(minWidth, width - (e.clientX - startX))
- 現(xiàn)在我們已經(jīng)完成了縮放,但是當(dāng)元素向右縮小到最小值時(shí),元素會(huì)向右移動(dòng),顯然這是不符合邏輯的,所以我們需要對偏移進(jìn)行限制
// 最大偏移為已經(jīng)偏移的距離 + 目標(biāo)元素的寬度 - 最小寬度 let distanceX = Math.min(tx + width - minWidth, tx + (e.clientX - startX))
4.如果縮放的尺寸需要限制在父元素內(nèi),我們需要繼續(xù)完善代碼
// 最大寬度為元素當(dāng)前偏移量 + 最初的寬度,最小寬度為minWidth newWidth = Math.min(tx + width, Math.max(minWidth, width - (e.clientX - startX))) // 最大偏移為已經(jīng)偏移的距離 + 目標(biāo)元素的寬度 - 最小寬度,最小偏移為0 let distanceX = Math.max(0,Math.min(tx + width - minWidth, tx + (e.clientX - startX)))
其他是三邊以及四個(gè)角的實(shí)現(xiàn)方式基本相同,就不在這里一一贅述了
最后
雖然我們完成了元素的拖拽與縮放,但是我們在使用時(shí),還是存在許多的限制,比如
- 目標(biāo)元素不能被定位,如果使用定位對元素進(jìn)行了偏移,我們所做的限制就會(huì)不生效
- 同理目標(biāo)元素也不能存在邊距
雖然存在限制,但是我們可以根據(jù)自己的實(shí)際需求進(jìn)行調(diào)整
到此這篇關(guān)于如何使用JS實(shí)現(xiàn)一個(gè)這樣的拖拽縮放效果的文章就介紹到這了,更多相關(guān)js拖拽縮放內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript 函數(shù)用法詳解【函數(shù)定義、參數(shù)、綁定、作用域、閉包等】
這篇文章主要介紹了JavaScript 函數(shù)用法,結(jié)合實(shí)例形式分析了JavaScript函數(shù)定義、參數(shù)、綁定、作用域、閉包、回調(diào)函數(shù)、柯理化函數(shù)等相關(guān)概念、原理與操作注意事項(xiàng),需要的朋友可以參考下2020-05-05使用TypeScript實(shí)現(xiàn)高效的異步隊(duì)列任務(wù)管理
在javaScript項(xiàng)目開發(fā)中,異步編程是不可或缺的一部分,從網(wǎng)絡(luò)請求到延時(shí)操作,異步操作使得我們能夠在等待某個(gè)任務(wù)完成時(shí)繼續(xù)執(zhí)行其他任務(wù),提高應(yīng)用的響應(yīng)性和性能,本文使用JavaScript實(shí)現(xiàn)一個(gè)異步隊(duì)列來優(yōu)雅地管理復(fù)雜的異步任務(wù)流,需要的朋友可以參考下2024-03-03基于substring()和substr()的使用以及區(qū)別(實(shí)例講解)
下面小編就為大家分享一篇基于substring()和substr()的使用以及區(qū)別實(shí)例講解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2017-12-12詳解JavaScript到TypeScript的轉(zhuǎn)換過程
JavaScript是一門強(qiáng)大而靈活的編程語言,TypeScript作為JavaScript的超集,為開發(fā)人員提供了靜態(tài)類型檢查、更好的協(xié)作能力和面向?qū)ο缶幊痰闹С郑疚膶⒃敿?xì)講解如何將JavaScript代碼轉(zhuǎn)換為TypeScript,并寫一些代碼示例參考2023-06-06原生javascript實(shí)現(xiàn)無間縫滾動(dòng)示例
原生javascript無間縫滾動(dòng)目前支持的是豎向與橫向滾動(dòng),下面有個(gè)不錯(cuò)的示例,大家可以參考下2014-01-01JS數(shù)組的遍歷方式for循環(huán)與for...in
本節(jié)主要介紹了JS數(shù)組的遍歷方式for循環(huán)與for...in,需要的朋友可以參考下2014-07-07