摘要:參考了很多別人寫的代碼,最后終于弄明白了其中的原理,自己也寫了一個。效果圖如下地址如下拖拽類封裝代碼使用方法引入和對應(yīng)的。如果沒有為的結(jié)構(gòu),就創(chuàng)建。鼠標(biāo)移動時,記錄再次計算鼠標(biāo)位置距離中心位置的的反正切函數(shù)。
在公司做一個h5編輯平臺,中間需要對元素進(jìn)行拖拽、放大縮小、旋轉(zhuǎn)等操作,且需要對文本、圖片、音樂組件等不同元素都可以具備這些功能。參考了很多別人寫的代碼,最后終于弄明白了其中的原理,自己也寫了一個。
效果圖如下:
github地址如下:拖拽類封裝代碼
使用方法引入js和對應(yīng)的css。真的有需要的小伙伴只要把我對應(yīng)文件夾的dragger.js和dragger.css拷進(jìn)自己的項目里就可以用啦,不限制前端框架啊,vue、react還是html文件里都可以使用哦
import Drag from "../../static/dragger.js" import "./assets/css/dragger.css"
之后,實例化
new Drag({ id: "box-dragger", showAngle: true, isScale: false, showBorder: false }) new Drag({ id: "box-dragger2", canZoom: false, canRotate: false }) new Drag({ id: "img-box", showAngle: true, showPosition: true }) new Drag({ id: "test" })具體實現(xiàn)(封裝細(xì)節(jié)) 功能細(xì)節(jié)整理:
旋轉(zhuǎn)
縮放
平移
技術(shù)難點:旋轉(zhuǎn)時要注意盒子每一個點的位置發(fā)生了變化
針對拖拽后的盒子的left和top都有變化,計算其left和top時需將其按照中心軸旋轉(zhuǎn)擺正,再進(jìn)行計算
當(dāng)且僅有一個盒子是被選中的盒子,點擊哪個選中哪個。(當(dāng)前頁面多個實例化Drag對象時,如何保證操作互不影響)
實現(xiàn)的兩種不同方式:
可以選中某元素,直接給該元素內(nèi)部加上操作的點
有一個pannel,選中某元素時,將這個pannel定位到該元素的位置上
這兩種方式都實現(xiàn)過一次,第一種比較簡單,但是第一種,不好控制選中某個元素才讓操作點展示。
如何封裝:考慮如何讓用戶快速上手使用,可參考的點:
用戶需要傳入什么必須的參數(shù)
暴露給用戶什么可設(shè)置的參數(shù)和方法
實現(xiàn)過程: 可配置參數(shù)字段 | 說明 | 是否必填 | 默認(rèn)值 |
---|---|---|---|
id | 目標(biāo)元素id | 是 | 無 |
container | 父容器id | 否 | body |
canRotate | 是否可以旋轉(zhuǎn) | 否 | true |
canZoom | 是否可以縮放 | 否 | true |
canPull | 是否可以拉升 | 否 | true |
canMove | 是否可以平移 | 否 | true |
showAngle | 展示角度 | 否 | false |
showPosition | 展示位置 | 否 | false |
isScale | 是否等比例縮放 | 否 | true |
showBorder | 是否展示pannel的border | 否 | false |
canRotate
canZoom
canPull
canMove
showAngle
isScale
id
container
targetObj
pannelDom 操作divdom
...
具體看圖:
初始化參數(shù)
初始化目標(biāo)dom對象的位置:記錄其:
left平距左
top
width
height
angle
rightBottomPoint 目標(biāo)dom對象右下坐標(biāo)
rightTopPoint 目標(biāo)dom對象右上坐標(biāo)
leftTopPoint 目標(biāo)dom對象左上坐標(biāo)
leftBottomPoint 目標(biāo)dom對象左下坐標(biāo)
leftMiddlePoint 目標(biāo)dom對象左中坐標(biāo)
rightMiddlePoint 目標(biāo)dom對象右中坐標(biāo)
topMiddlePoint 目標(biāo)dom對象上中坐標(biāo)
bottomMiddlePoint 目標(biāo)dom對象下中坐標(biāo)
centerPos 目標(biāo)dom對象中心點坐標(biāo)
初始化pannel結(jié)構(gòu)
當(dāng)前的父容器中只有一個pannel結(jié)構(gòu),每次實例化對象時,會判斷一下如果當(dāng)前這個父容器里已經(jīng)存在id為pannel的結(jié)構(gòu),就將其子節(jié)點清空,按照當(dāng)前實例化對象傳進(jìn)來的屬性重新渲染pannel子結(jié)構(gòu)。如果沒有id為pannel的結(jié)構(gòu),就創(chuàng)建。
初始化事件
給pannelDom和targetObj綁定mousedown事件
給document綁定mousemove和mouseup事件
initEvent () { document.addEventListener("mousemove", e => { e.preventDefault && e.preventDefault() this.moveChange(e, this.targetObj) }) document.addEventListener("mouseup", e => { this.moveLeave(this.targetObj) }) if (this.canMove) { // 外層給this.pannelDom添加mousedown事件,是在所有實例化結(jié)束后,panneldom被展示在最后一個實例化對象上,鼠標(biāo)按下它時,觸發(fā)moveInit事件 this.pannelDom.onmousedown = e => { e.stopPropagation() this.moveInit(9, e, this.targetObj) } this.targetObj.onmousedown = e => { e.stopPropagation() this.moveInit(9, e, this.targetObj) this.initPannel() // 在點擊其他未被選中元素時,pannel定位到該元素上,重寫pannelDom事件,因為此時的this.pannelDom已經(jīng)根據(jù)新的目標(biāo)元素被重寫 this.pannelDom.onmousedown= e => { this.moveInit(9, e, this.targetObj) } } } }
dom操作
旋轉(zhuǎn)操作
鼠標(biāo)按下時,記錄當(dāng)前鼠標(biāo)位置距離box中心位置的y/x的反正切函數(shù)A1。
this.mouseInit = { x: Math.floor(e.clientX), y: Math.floor(e.clientY) } this.preRadian = Math.atan2(this.mouseInit.y - this.centerPos.y, this.mouseInit.x - this.centerPos.x)
鼠標(biāo)移動時,記錄再次計算鼠標(biāo)位置距離box中心位置的y/x的反正切函數(shù)A2。
this.rotateCurrent = { x: Math.floor(e.clientX), y: Math.floor(e.clientY) } this.curRadian = Math.atan2(this.rotateCurrent.y - this.centerPos.y, this.rotateCurrent.x - this.centerPos.x)
求A2-A1,求出移動的弧度
this.tranformRadian = this.curRadian - this.preRadian
求出最后box的旋轉(zhuǎn)角度,this.getRotate(target)是js中獲取某dom元素的旋轉(zhuǎn)角度的方法(粘貼過來的,親測好使)
this.angle = this.getRotate(target) + Math.round(this.tranformRadian * 180 / Math.PI) this.preRadian = this.curRadian //鼠標(biāo)移動的每一下都計算這個角度,所以每一下移動前的弧度值都上一次移動后的弧度值
計算旋轉(zhuǎn)后box每個點的坐標(biāo),根據(jù)余弦公式,傳入:旋轉(zhuǎn)前每點坐標(biāo),旋轉(zhuǎn)中心坐標(biāo)和旋轉(zhuǎn)角度
let disAngle = this.angle - this.initAngle this.rightBottomPoint = this.getRotatedPoint(this.initRightBottomPoint, this.centerPos, disAngle) this.rightTopPoint = this.getRotatedPoint(this.initRightTopPoint, this.centerPos, disAngle) this.leftTopPoint = this.getRotatedPoint(this.initLeftTopPoint, this.centerPos, disAngle) this.leftBottomPoint = this.getRotatedPoint(this.initLeftBottomPoint, this.centerPos, disAngle) this.leftMiddlePoint = this.getRotatedPoint(this.initLeftMiddlePoint, this.centerPos, disAngle) this.rightMiddlePoint = this.getRotatedPoint(this.initRightMiddlePoint, this.centerPos, disAngle) this.topMiddlePoint = this.getRotatedPoint(this.initTopMiddlePoint, this.centerPos, disAngle) this.bottomMiddlePoint = this.getRotatedPoint(this.initBottomMiddlePoint, this.centerPos, disAngle)
沿著一個方向拉升操作。
沿著一個角縮放操作。
優(yōu)化,mousemove事件添加節(jié)流函數(shù)
function throttle(fn, interval) { let canRun = true; return function () { if (!canRun) return; canRun = false; setTimeout(() => { fn.apply(this, arguments); canRun = true; }, interval); }; } let that = this document.addEventListener("mousemove", throttle(function (e) { e.preventDefault && e.preventDefault() that.moveChange(e, that.targetObj) }, 10))
中間的沿著一個方向的拉升操作和沿著一個角的縮放操作主要是參考了一個大佬的拖拽思想實現(xiàn)的 github wiki地址
補充節(jié)流函數(shù)和防抖函數(shù)的區(qū)別:
節(jié)流函數(shù):在一定規(guī)定時間段觸發(fā)過的事件就不再觸發(fā)
防抖函數(shù):觸發(fā)了一次事件,如果在一定時間段內(nèi)又觸發(fā)一次,那么取消上一次觸發(fā),執(zhí)行這次觸發(fā)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/109615.html
前言:前段時間負(fù)責(zé)公司的運營管理后臺項目,通過運營后臺的PC端拖拽配置布局,達(dá)到App首頁模板的動態(tài)UI界面配置,生成頁面。趁著周末,整理一下當(dāng)時所了解到的拖拽。文章會根據(jù)大家的反饋或者自己學(xué)習(xí)經(jīng)驗的累積成長不定期更新豐富。如果你想了解更多PC端的拖拽開發(fā),歡迎點贊關(guān)注或者收藏一波[鞠躬]。 之前在掘金一篇文章里看到這段話: UI 開發(fā)的三種模式 1.手寫標(biāo)簽和樣式代碼,生成頁面 2.可視化拖拽 ...
摘要:前面幾篇文章,我跟大家分享了的一些基礎(chǔ)知識,這篇文章,將會進(jìn)入第一個實戰(zhàn)環(huán)節(jié)利用前面幾章的所涉及到的知識,封裝一個拖拽對象。不封裝對象直接實現(xiàn)利用原生封裝拖拽對象通過擴(kuò)展來實現(xiàn)拖拽對象。 showImg(https://segmentfault.com/img/remote/1460000008699587); 前面幾篇文章,我跟大家分享了JavaScript的一些基礎(chǔ)知識,這篇文章,...
摘要:原生實現(xiàn)對元素的拖拽一背景介紹此處為鋪墊內(nèi)容,可跳過隨著前端的不斷發(fā)展,各種各樣的前端規(guī)范和新知識新技術(shù)層出不窮,極大地拓展了開發(fā)者的操作空間,也大大地提升了用戶體驗。 原生 JS 實現(xiàn)對 html 元素的拖拽 一、背景介紹 【此處為鋪墊內(nèi)容,可跳過】 隨著 Web 前端的不斷發(fā)展,各種各樣的前端規(guī)范和新知識、新技術(shù)層出不窮,極大地拓展了開發(fā)者的操作空間,也大大地提升了用戶體驗。而隨著...
摘要:原生實現(xiàn)對元素的拖拽一背景介紹此處為鋪墊內(nèi)容,可跳過隨著前端的不斷發(fā)展,各種各樣的前端規(guī)范和新知識新技術(shù)層出不窮,極大地拓展了開發(fā)者的操作空間,也大大地提升了用戶體驗。 原生 JS 實現(xiàn)對 html 元素的拖拽 一、背景介紹 【此處為鋪墊內(nèi)容,可跳過】 隨著 Web 前端的不斷發(fā)展,各種各樣的前端規(guī)范和新知識、新技術(shù)層出不窮,極大地拓展了開發(fā)者的操作空間,也大大地提升了用戶體驗。而隨著...
摘要:拖拽排序組件地址因為使用了技術(shù)棧,所以封裝優(yōu)先考慮輸入和輸出?;跀?shù)據(jù)驅(qū)動去渲染頁面控制拖拽元素的順序。例如原生的事件,在里應(yīng)使用事件。 拖拽排序組件Github地址:https://github.com/VicEcho/VD... 因為使用了react.js技術(shù)棧,所以封裝優(yōu)先考慮輸入和輸出?;跀?shù)據(jù)驅(qū)動去渲染頁面、控制拖拽元素的順序。 由于我不考慮兼容IE8等舊版本瀏覽器,拖拽的效...
閱讀 2835·2023-04-25 17:59
閱讀 676·2023-04-25 15:05
閱讀 669·2021-11-25 09:43
閱讀 3026·2021-10-12 10:13
閱讀 3532·2021-09-27 13:59
閱讀 3577·2021-09-23 11:21
閱讀 3872·2021-09-08 09:35
閱讀 561·2019-08-29 17:12