最近因?yàn)闃I(yè)務(wù)需求绞幌,需要對(duì)element的el-dialog組件實(shí)現(xiàn)拖拽功能疾掰,在網(wǎng)上搜索了下,資料很多听皿,不過這其中也有一些小坑咕别,這里記錄下。主要參考以下兩篇文章 element-ui dialog組件添加可拖拽位置 可拖拽寬高和JavaScript限定范圍拖拽及自定義滾動(dòng)條應(yīng)用(3)写穴。
首先還是要明確幾個(gè)概念惰拱,這里通過修改css并截圖給大家介紹下,理解了這幾個(gè)概念啊送,代碼寫起來會(huì)得心應(yīng)手許多偿短。
- clientWidth,clientHeight
- scrollWidth馋没,scrollHeight
- offsetWidth昔逗,offsetHeight
- clientLeft,clientTop
- scrollLeft篷朵,scrollTop
- offsetLeft勾怒,offsetTop
1. clientWidth,clientHeight
clientWidth声旺,clientHeight表示對(duì)象內(nèi)容的可視區(qū)的寬度笔链,不包括滾動(dòng)條和邊框,會(huì)隨對(duì)象顯示大小的變化而改變腮猖。
上圖中鉴扫,我給el-scrollbar__view這個(gè)div增加了10px的紅色邊框,整個(gè)div的實(shí)際高度澈缺,寬度變成了3452px坪创,1920px,觀察clientWidth姐赡,clientHeight的值是3432px莱预,1900px,并沒有包含邊框和滾動(dòng)條的寬度项滑。
2. scrollWidth依沮,scrollHeight
scrollWidth,scrollHeight表示對(duì)象的實(shí)際內(nèi)容的寬度,不包括邊框(但是包括滾動(dòng)條)悉抵,會(huì)隨對(duì)象中內(nèi)容超過可視區(qū)后而變大肩狂。
上圖中摘完,我還是給el-scrollbar__view這個(gè)div增加了10px的紅色邊框姥饰,整個(gè)div的實(shí)際高度,寬度變成了3452px孝治,1920px列粪,觀察scrollWidth,scrollHeight的值是3432px谈飒,1900px岂座,并沒有包含邊框的寬度。
3. offsetWidth杭措,offsetHeight
offsetWidth费什,offsetHeight表示對(duì)象整體的實(shí)際寬度,包括滾動(dòng)條和邊框手素,會(huì)隨對(duì)象顯示大小的變化而改變鸳址。
上圖中,我還是給el-scrollbar__view這個(gè)div增加了10px的紅色邊框泉懦,整個(gè)div的實(shí)際高度稿黍,寬度變成了3452px,1920px崩哩,觀察scrollWidth巡球,scrollHeight的值仍然是3452px,1920px邓嘹,包含滾動(dòng)條和邊框酣栈。
4. clientLeft,clientTop
clientLeft汹押,clientTop表示對(duì)象邊框的寬度钉嘹。
上圖中,我還是給el-scrollbar__view這個(gè)div增加了10px的紅色邊框鲸阻,觀察clientLeft跋涣, clientTop均為10px。
5. scrollLeft鸟悴,scrollTop
scrollLeft,scrollTop表示對(duì)象的顯示(可見)的內(nèi)容與該對(duì)象實(shí)際的內(nèi)容的距離陈辱。
上圖中,我們給content這個(gè)section設(shè)置了寬高均是300px细诸,并設(shè)置顯示滾動(dòng)條沛贪,將滾動(dòng)條拖動(dòng)一部分,觀察scrollLeft,scrollTop的值分別是164px利赋,204px水评,這對(duì)應(yīng)著當(dāng)前內(nèi)容距離原來實(shí)際內(nèi)容的距離(假設(shè)藍(lán)色框是實(shí)際內(nèi)容的距離)。
6. offsetLeft媚送,offsetTop
offsetLeft中燥,offsetTop表示對(duì)象邊框的外邊緣距離與已定位的父容器(offsetparent)的內(nèi)邊距離(不包括元素的邊框和父容器的邊框)。
上圖中塘偎,el-dialog這個(gè)div距離已定位的父容器(這里是el-dialog__wrapper)的寬高分別是672px疗涉,61px,與offsetLeft吟秩,offsetTop值一致咱扣。
我們給el-dialog__wrapper加上一個(gè)20px的黃色邊框,給el-dialog加上一個(gè)10px的藍(lán)色邊框涵防,再來觀察offsetLeft闹伪,offsetTop的值,發(fā)現(xiàn)在計(jì)算時(shí)壮池,是從黃色邊框的內(nèi)邊距到藍(lán)色邊框的外邊距偏瓤,不包括邊框。
7. coding
明確上述概念之后火窒,我們來著手寫代碼硼补,先說下我的業(yè)務(wù)場(chǎng)景。
有一個(gè)dialog彈框熏矿,彈框的背景并不是全屏的已骇,只在除header,sidebar的地方顯示票编,要求拖拽dialog彈框不能超過背景褪储。
我們?cè)趍ain.js同級(jí)目錄創(chuàng)建directives.js,具體代碼如下慧域。
// directives.js
import Vue from 'vue';
// v-dialogDrag: 彈窗拖拽
Vue.directive('dialogDrag', {
bind(el, binding, vnode, oldVnode) {
// 獲取拖拽內(nèi)容頭部
const dialogHeaderEl = el.querySelector('.el-dialog__header');
// 獲取拖拽內(nèi)容整體 這個(gè)rrc-dialog是我自己封裝的組件 如果使用element的組件應(yīng)寫成.el-dialog
const dragDom = el.querySelector('.rrc-dialog');
dialogHeaderEl.style.cursor = 'move';
// 獲取原有屬性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const sty = dragDom.currentStyle || window.getComputedStyle(dragDom, null);
// 鼠標(biāo)按下事件
dialogHeaderEl.onmousedown = (e) => {
// 鼠標(biāo)按下鲤竹,計(jì)算當(dāng)前元素距離可視區(qū)的距離 (鼠標(biāo)點(diǎn)擊位置距離可視窗口的距離)
const disX = e.clientX - dialogHeaderEl.offsetLeft;
const disY = e.clientY - dialogHeaderEl.offsetTop;
// 獲取到的值帶px 正則匹配替換
let styL, styT;
// 注意在ie中 第一次獲取到的值為組件自帶50% 移動(dòng)之后賦值為px
if (sty.left.includes('%')) {
styL = +document.body.clientWidth * (+sty.left.replace(/\%/g, '') / 100);
styT = +document.body.clientHeight * (+sty.top.replace(/\%/g, '') / 100);
} else {
styL = +sty.left.replace(/\px/g, '');
styT = +sty.top.replace(/\px/g, '');
};
// 鼠標(biāo)拖拽事件
document.onmousemove = function (e) {
// 通過事件委托,計(jì)算移動(dòng)的距離 (開始拖拽至結(jié)束拖拽的距離)
const l = e.clientX - disX;
const t = e.clientY - disY;
let finallyL = l + styL
let finallyT = t + styT
// 邊界值判定 注意clientWidth scrollWidth區(qū)別 要減去之前的top left值
// dragDom.offsetParent表示彈窗陰影部分
if (finallyL < 0) {
finallyL = 0
} else if (finallyL > dragDom.offsetParent.clientWidth - dragDom.clientWidth - dragDom.offsetParent.offsetLeft) {
finallyL = dragDom.offsetParent.clientWidth - dragDom.clientWidth - dragDom.offsetParent.offsetLeft
}
if (finallyT < 0) {
finallyT = 0
} else if (finallyT > dragDom.offsetParent.clientHeight - dragDom.clientHeight - dragDom.offsetParent.offsetLeft) (
finallyT = dragDom.offsetParent.clientHeight - dragDom.clientHeight - dragDom.offsetParent.offsetLeft
)
// 移動(dòng)當(dāng)前元素
dragDom.style.left = `${finallyL}px`;
dragDom.style.top = `${finallyT}px`;
//將此時(shí)的位置傳出去
//binding.value({x:e.pageX,y:e.pageY})
};
document.onmouseup = function (e) {
document.onmousemove = null;
document.onmouseup = null;
};
}
}
})
然后在main.js去引入我們剛才創(chuàng)建的directives.js文件昔榴。
// main.js
import './directives'
最后找到我們需要拖拽的組件辛藻,在其標(biāo)簽上加上v-dialogDrag即可(自定義指令)。
// call.center.detail.vue
<rrc-dialog v-dialogDrag title="呼出結(jié)果" :visible.sync="dialogOutVisible">
...
</rrc-dialog>
以上是我對(duì)簡(jiǎn)單實(shí)現(xiàn)el-dialog拖拽功能的一些看法互订,感謝上面提到的兩篇文章的作者吱肌,碼字不易,請(qǐng)尊重作者版權(quán)仰禽,轉(zhuǎn)載注明出處氮墨。七夕表白被拒纺蛆,特地寫下此文。
By BeLLESS 2018.8.18 12:08