在前端技術(shù)日新月異报强,飛速發(fā)展的當(dāng)下,涌現(xiàn)出了很多優(yōu)秀的開源框架以及優(yōu)秀的開源組件拱燃,這些都是優(yōu)秀的前端開發(fā)者的技術(shù)成果秉溉。當(dāng)然,雖然有這么多現(xiàn)成的開源技術(shù)成果供我們再開發(fā)中使用碗誉,但我們不能僅抱著拿來主義的態(tài)度召嘶,只顧拿來用就行,更多的是要以學(xué)習(xí)的心態(tài)來對待開源技術(shù)哮缺,對待前端世界弄跌,要多汲取他(她)人的經(jīng)驗(yàn),且又不忘根本(基礎(chǔ)知識)尝苇,站在巨人的肩膀上繼續(xù)前行铛只!
js拖拽功能
拖拽功能是前端很多業(yè)務(wù)場景都能使用到的一種普遍的技術(shù),比如彈窗視口的拖拽等糠溜,前端世界的很多優(yōu)秀框架組件都擁有封裝完美的拖拽功能淳玩,別人封裝的優(yōu)秀源碼,健壯诵冒,穩(wěn)定凯肋,同時又優(yōu)雅美觀谊惭,自然是值得我們?nèi)W(xué)習(xí)的汽馋。這篇文章主要講解下如何用原生js實(shí)現(xiàn)一個簡單的拖拽功能。
拖拽原理
對于PC端來說圈盔,拖拽功能無非就是鼠標(biāo)點(diǎn)擊豹芯,鼠標(biāo)開始移動,鼠標(biāo)松開三個步驟驱敲,當(dāng)然這三個步驟里面有拆分了很多的細(xì)節(jié)铁蹈,越是健壯,穩(wěn)定的代碼众眨,對細(xì)節(jié)的處理就越嚴(yán)謹(jǐn)(我們主要講實(shí)現(xiàn)一個簡單的拖拽功能握牧,可能有些細(xì)節(jié)處理得不妥當(dāng)容诬,待以后再次進(jìn)行封裝)。
鼠標(biāo)點(diǎn)擊:當(dāng)鼠標(biāo)按下點(diǎn)擊的時候沿腰,需要拖拽的容器就已應(yīng)該準(zhǔn)備就緒 進(jìn)去拖拽狀態(tài)了
鼠標(biāo)移動:需要拖拽的容器根據(jù)鼠標(biāo)的移動位置而移動览徒,這里面就包含的很多對細(xì)節(jié)的處理,如:當(dāng)容器某一端的位置到達(dá)了瀏覽器邊緣的時候颂龙,是不應(yīng)該超出瀏覽器范圍區(qū)域的习蓬,不然會撐出滾動條或者被遮擋,這不是一個很好的效果措嵌。那么要如何把容器的位置控制在瀏覽器規(guī)定的范圍區(qū)域呢躲叼?這里就要分別處理上下左右四個方向的位置變化。
1.top端:當(dāng)容器的頂部到達(dá)了瀏覽器頂部邊緣的時候企巢,鼠標(biāo)移動的方向繼續(xù)是向上移動枫慷,那么就要設(shè)置容器頂端的top值始終為0,保證容器位置不會溢出瀏覽器范圍浪规。
2.bottom端:當(dāng)容器的底部到達(dá)了瀏覽器底部邊緣的時候流礁,鼠標(biāo)移動的方向繼續(xù)是向下移動,那么就要設(shè)置容器底端的bottom值始終為瀏覽器可視區(qū)域的高度值減去容器的高度值罗丰,保證容器位置不會溢出瀏覽器范圍神帅。
3.left端:當(dāng)容器的左端到達(dá)了瀏覽器左端邊緣的時候,鼠標(biāo)移動的方向繼續(xù)是向左移動萌抵,那么就要設(shè)置容器頂端的left值始終為0找御,保證容器位置不會溢出瀏覽器范圍。
3.right端:當(dāng)容器的右端到達(dá)了瀏覽器右端邊緣的時候绍填,鼠標(biāo)移動的方向繼續(xù)是向右移動霎桅,那么就要設(shè)置容器頂端的right值始終為瀏覽器可視區(qū)域的寬度值減去容器的寬度值,保證容器位置不會溢出瀏覽器范圍讨永。鼠標(biāo)松開:代表著容器拖拽結(jié)束滔驶,停止改變?nèi)萜鞯奈恢谩_@里也有個小細(xì)節(jié)需要注意下卿闹,就是揭糕,鼠標(biāo)點(diǎn)擊容器準(zhǔn)備拖拽時候的位置要和鼠標(biāo)松開后鼠標(biāo)在容器中的位置是一致的,意思就是在拖拽的過程當(dāng)中不管鼠標(biāo)的位置在什么地方锻霎,停止拖拽后都要回到原點(diǎn)著角。
接口參數(shù)暴露
完美的封裝往往都是需要搭配強(qiáng)大的配置參數(shù)來維護(hù)它的健壯,穩(wěn)定的旋恼。
對于拖拽功能吏口,需要配置的參數(shù)通常需要有以下內(nèi)容:
需要拖拽的容器(必選)
需要拖拽的區(qū)域(可選)
是否可拖拽,拖拽開關(guān)(可選)
是否需要虛擬拖拽容器(可選)
控制虛擬拖拽容器的樣式(可選)
配置上這些參數(shù),一個簡單的拖拽功能就可以使用了产徊。
拖拽示例核心代碼
調(diào)用方式
new DragView({
"dragWrap": document.getElementById("drag_wrap"), //容器昂勒,必選
"dragableArea": document.getElementById("drag_area"), //可拖拽區(qū)域,可選
"isDrag": true, //是否可拖拽舟铜,可選
"isDummyWrap": true, //是否需要虛擬拖拽框叁怪,可選
"dummyWrapClass": "dragview_dummy_wrap2" //控制虛擬拖拽框的樣式,可選
})
封裝的部分代碼
DragView.prototype = {
"constructor": DragView,
"init": function(){
var that = this;
var dragMove = document.createElement("div");
dragMove.setAttribute("class","dragview_move");
document.body.appendChild(dragMove);
that.options.isDrag && (that.options.dragableArea.style.cursor = "move");
that.options.dragableArea.onmousedown = function(e){
if(!that.options.dragStatus){
that.throttle(function(){
that.options.isDrag && that.mouseDown(e);
},null,0);
}
}.bind(that);
},
"getEvent": function(e){
return e ? e : window.event;
},
"extend": function(setting,option){
for(var attr in setting){
if(typeof option[attr] != "undefined"){
setting[attr] = option[attr];
}
}
return setting;
},
"throttle": function(fn,context,delay){
clearTimeout(fn.timeoutId);
fn.timeoutId = setTimeout(function(){
fn.call(context);
},delay);
},
"mouseDown": function(e){
var that = this;
var dragMoveArea = "";
var dragWrap = that.options.dragWrap;
var events = that.getEvent(e);
var disX = events.clientX - dragWrap.offsetLeft;
var disY = events.clientY - dragWrap.offsetTop;
document.getElementsByClassName("dragview_move")[0].style.display = "block";
dragWrap.style.position = "absolute";
dragWrap.style.zIndex = "99999";
that.options.tempDragWrap = that.options.dragWrap;
if(that.options.isDummyWrap){
dragMoveArea = document.createElement("div");
dragMoveArea.setAttribute("class",that.options.dummyWrapClass);
dragMoveArea.style.width = dragWrap.clientWidth + "px";
dragMoveArea.style.height = dragWrap.clientHeight + "px";
dragMoveArea.style.position = "absolute";
dragMoveArea.style.zIndex = "99999";
dragMoveArea.style.top = dragWrap.style.top;
dragMoveArea.style.left = dragWrap.style.left;
document.body.appendChild(dragMoveArea);
that.options.tempDragWrap = dragMoveArea;
}
that.options.dragStatus = true;
document.onmousemove = function(e){
that.throttle(function(){
var _events = that.getEvent(e);
that.mouseMove(_events,disX,disY,dragMoveArea);
},null,0);
}
document.onmouseup = function(){
that.options.dragStatus && that.mouseUp(dragMoveArea);
}
},
"mouseMove": function(_events,disX,disY,dragMoveArea){
if(this.options.dragStatus){
var _x = _events.clientX - disX;
var _y = _events.clientY - disY;
var _winW = document.documentElement.clientWidth || document.body.clientWidth;
var _winH=document.documentElement.clientHeight || document.body.clientHeight;
var option = {
"x": _x,
"y": _y,
"winX": _winW,
"winY": _winH,
"dragW": this.options.tempDragWrap.offsetWidth,
"dragH": this.options.tempDragWrap.offsetHeight
};
this.limiteRange(option);
}
},
"mouseUp": function(dragMoveArea){
this.options.dragWrap.style.left = this.options.tempDragWrap.style.left;
this.options.dragWrap.style.top = this.options.tempDragWrap.style.top;
this.options.dragStatus = false;
dragMoveArea!="" && document.body.removeChild(dragMoveArea);
document.getElementsByClassName("dragview_move")[0].style.display = "none";
},
"limiteRange": function(option){
if(option.x <= 0 || option.dragW >= option.winX){
this.options.tempDragWrap.style.left = "0px";
}else if((option.x + option.dragW) >= option.winX){
this.options.tempDragWrap.style.left = (option.winX - option.dragW) + "px";
}else{
this.options.tempDragWrap.style.left = option.x + "px";
}
if(option.y <= 0 || option.dragH >= option.winY){
this.options.tempDragWrap.style.top = "0px";
}
else if((option.y + option.dragH) >= option.winY){
this.options.tempDragWrap.style.top = (option.winY - option.dragH) + "px";
}
else{
this.options.tempDragWrap.style.top = option.y + "px";
}
}
};
顯示效果如下鏈接:https://webproblem.github.io/hello-world/drag-View/drag.html
具體示例及源碼見我的 github , ** 如果喜歡深滚,請記得Star哈奕谭!**
原創(chuàng)文章,站在前輩們的經(jīng)驗(yàn)上的總結(jié)痴荐,文中如有不正之處血柳,還望指正!