基于javascript的拖拽類封裝詳解
效果圖如下

github地址如下: github地址
使用方法
引入js和對(duì)應(yīng)的css
import Drag from '../../static/dragger.js' import './assets/css/dragger.css'
之后,實(shí)例化
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'
})
具體實(shí)現(xiàn)(封裝細(xì)節(jié))
功能細(xì)節(jié)整理:
- 旋轉(zhuǎn)
- 縮放
- 平移
技術(shù)難點(diǎn):
- 旋轉(zhuǎn)時(shí)要注意盒子每一個(gè)點(diǎn)的位置發(fā)生了變化
- 針對(duì)拖拽后的盒子的left和top都有變化,計(jì)算其left和top時(shí)需將其按照中心軸旋轉(zhuǎn)擺正,再進(jìn)行計(jì)算
- 當(dāng)且僅有一個(gè)盒子是被選中的盒子,點(diǎn)擊哪個(gè)選中哪個(gè)。(當(dāng)前頁面多個(gè)實(shí)例化Drag對(duì)象時(shí),如何保證操作互不影響)
- 實(shí)現(xiàn)的兩種不同方式:
可以選中某元素,直接給該元素內(nèi)部加上操作的點(diǎn)
有一個(gè)pannel,選中某元素時(shí),將這個(gè)pannel定位到該元素的位置上
這兩種方式都實(shí)現(xiàn)過一次,第一種比較簡(jiǎn)單,但是第一種,不好控制選中某個(gè)元素才讓操作點(diǎn)展示。
如何封裝:
考慮如何讓用戶快速上手使用,可參考的點(diǎn):
- 用戶需要傳入什么必須的參數(shù)
- 暴露給用戶什么可設(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對(duì)象的位置:記錄其:
- left平距左
- top
- width
- height
- angle
- rightBottomPoint 目標(biāo)dom對(duì)象右下坐標(biāo)
- rightTopPoint 目標(biāo)dom對(duì)象右上坐標(biāo)
- leftTopPoint 目標(biāo)dom對(duì)象左上坐標(biāo)
- leftBottomPoint 目標(biāo)dom對(duì)象左下坐標(biāo)
- leftMiddlePoint 目標(biāo)dom對(duì)象左中坐標(biāo)
- rightMiddlePoint 目標(biāo)dom對(duì)象右中坐標(biāo)
- topMiddlePoint 目標(biāo)dom對(duì)象上中坐標(biāo)
- bottomMiddlePoint 目標(biāo)dom對(duì)象下中坐標(biāo)
- centerPos 目標(biāo)dom對(duì)象中心點(diǎn)坐標(biāo)
初始化pannel結(jié)構(gòu)
當(dāng)前的父容器中只有一個(gè)pannel結(jié)構(gòu),每次實(shí)例化對(duì)象時(shí),會(huì)判斷一下如果當(dāng)前這個(gè)父容器里已經(jīng)存在id為pannel的結(jié)構(gòu),就將其子節(jié)點(diǎn)清空,按照當(dāng)前實(shí)例化對(duì)象傳進(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事件,是在所有實(shí)例化結(jié)束后,panneldom被展示在最后一個(gè)實(shí)例化對(duì)象上,鼠標(biāo)按下它時(shí),觸發(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()
// 在點(diǎn)擊其他未被選中元素時(shí),pannel定位到該元素上,重寫pannelDom事件,因?yàn)榇藭r(shí)的this.pannelDom已經(jīng)根據(jù)新的目標(biāo)元素被重寫
this.pannelDom.onmousedown= e => {
this.moveInit(9, e, this.targetObj)
}
}
}
}
dom操作
旋轉(zhuǎn)操作
鼠標(biāo)按下時(shí),記錄當(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)移動(dòng)時(shí),記錄再次計(jì)算鼠標(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,求出移動(dòng)的弧度
this.tranformRadian = this.curRadian - this.preRadian
求出最后box的旋轉(zhuǎn)角度,this.getRotate(target)是js中獲取某dom元素的旋轉(zhuǎn)角度的方法(粘貼過來的,親測(cè)好使)
this.angle = this.getRotate(target) + Math.round(this.tranformRadian * 180 / Math.PI) this.preRadian = this.curRadian //鼠標(biāo)移動(dòng)的每一下都計(jì)算這個(gè)角度,所以每一下移動(dòng)前的弧度值都上一次移動(dòng)后的弧度值
計(jì)算旋轉(zhuǎn)后box每個(gè)點(diǎn)的坐標(biāo),根據(jù)余弦公式,傳入:旋轉(zhuǎn)前每點(diǎ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)
沿著一個(gè)方向拉升操作。
沿著一個(gè)角縮放操作。 這兩個(gè)操作,主要參考了一個(gè)大佬的拖拽思想實(shí)現(xiàn)的 github wiki地址
優(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))
以上所述是小編給大家介紹的基于javascript的拖拽類封裝詳解整合,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- 關(guān)于js拖拽上傳 [一個(gè)拖拽上傳修改頭像的流程]
- 一個(gè)js拖拽的效果類和dom-drag.js淺析
- js 表格排序(編輯+拖拽+縮放)
- JQuery Dialog(JS 模態(tài)窗口,可拖拽的DIV)
- js實(shí)現(xiàn)拖拽效果
- js完美的div拖拽實(shí)例代碼
- JS類的封裝及實(shí)現(xiàn)代碼
- js 對(duì)聯(lián)廣告、漂浮廣告封裝類(IE,FF,Opera,Safari,Chrome
- javascript 通過封裝div方式彈出div窗體
- js數(shù)據(jù)驗(yàn)證集合、js email驗(yàn)證、js url驗(yàn)證、js長(zhǎng)度驗(yàn)證、js數(shù)字驗(yàn)證等簡(jiǎn)單封裝
相關(guān)文章
Vue項(xiàng)目中最新用到的一些實(shí)用小技巧
這篇文章主要給大家介紹了關(guān)于Vue項(xiàng)目中最新用到的一些實(shí)用小技巧,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用vue具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11
解決vue+ element ui 表單驗(yàn)證有值但驗(yàn)證失敗問題
這篇文章主要介紹了vue+ element ui 表單驗(yàn)證有值但驗(yàn)證失敗,本文通過實(shí)例代碼給大家分享解決方案,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01
基于Vue3+TypeScript實(shí)現(xiàn)圖片預(yù)覽組件
在現(xiàn)代的 Web 應(yīng)用中,圖片預(yù)覽是一個(gè)常見的需求,本文將介紹如何使用 Vue3 和 TypeScript 開發(fā)一個(gè)圖片預(yù)覽組件,支持展示單張或多張圖片,并提供了豐富的配置選項(xiàng),需要的朋友可以參考下2024-04-04
vue3中$attrs的變化與inheritAttrs的使用詳解
$attrs現(xiàn)在包括class和style屬性。?也就是說在vue3中$listeners不存在了,vue2中$listeners是單獨(dú)存在的,在vue3?$attrs包括class和style屬性,?vue2中?$attrs?不包含class和style屬性,這篇文章主要介紹了vue3中$attrs的變化與inheritAttrs的使用?,需要的朋友可以參考下2022-10-10
vue實(shí)現(xiàn)桌面向網(wǎng)頁拖動(dòng)文件的示例代碼(可顯示圖片/音頻/視頻)
這篇文章主要介紹了vue實(shí)現(xiàn)桌面向網(wǎng)頁拖動(dòng)文件的示例代碼(可顯示圖片/音頻/視頻),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
vue?+?element-plus自定義表單驗(yàn)證(修改密碼業(yè)務(wù))的示例
這篇文章主要介紹了vue?+?element-plus自定義表單驗(yàn)證(修改密碼業(yè)務(wù)),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2025-04-04
vue中實(shí)現(xiàn)多頁面應(yīng)用方式
這篇文章主要介紹了vue中實(shí)現(xiàn)多頁面應(yīng)用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Nuxt如何實(shí)現(xiàn)將服務(wù)測(cè)數(shù)據(jù)存儲(chǔ)到Vuex中
這篇文章主要介紹了Nuxt如何實(shí)現(xiàn)將服務(wù)測(cè)數(shù)據(jù)存儲(chǔ)到Vuex中的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
vue-resource請(qǐng)求實(shí)現(xiàn)http登錄攔截或者路由攔截的方法
這篇文章主要介紹了vue-resource請(qǐng)求實(shí)現(xiàn)http登錄攔截或者路由攔截的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07

