原生js實(shí)現(xiàn)拖拽功能

在前端技術(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é)痴荐,文中如有不正之處血柳,還望指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末生兆,一起剝皮案震驚了整個濱河市难捌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸦难,老刑警劉巖根吁,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異合蔽,居然都是意外死亡击敌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門拴事,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沃斤,“玉大人,你說我怎么就攤上這事刃宵『馄浚” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵牲证,是天一觀的道長哮针。 經(jīng)常有香客問我,道長坦袍,這世上最難降的妖魔是什么十厢? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮键闺,結(jié)果婚禮上寿烟,老公的妹妹穿的比我還像新娘澈驼。我一直安慰自己辛燥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挎塌,像睡著了一般徘六。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上榴都,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天待锈,我揣著相機(jī)與錄音,去河邊找鬼嘴高。 笑死竿音,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拴驮。 我是一名探鬼主播春瞬,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼套啤!你這毒婦竟也來了宽气?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤潜沦,失蹤者是張志新(化名)和其女友劉穎萄涯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體唆鸡,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涝影,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了争占。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片袄琳。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖燃乍,靈堂內(nèi)的尸體忽然破棺而出唆樊,到底是詐尸還是另有隱情,我是刑警寧澤刻蟹,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布逗旁,位于F島的核電站,受9級特大地震影響舆瘪,放射性物質(zhì)發(fā)生泄漏片效。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一英古、第九天 我趴在偏房一處隱蔽的房頂上張望淀衣。 院中可真熱鬧,春花似錦召调、人聲如沸膨桥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽只嚣。三九已至沮稚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間册舞,已是汗流浹背蕴掏。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留调鲸,地道東北人盛杰。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像藐石,于是被迫代替她去往敵國和親饶唤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,515評論 25 707
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案贯钩? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,728評論 1 92
  • 1 剛畢業(yè)那會兒角雷,我其實(shí)是一個性子比較急躁的人祸穷,說話語速也快,跟跟打機(jī)關(guān)槍似得勺三。 曾經(jīng)還吃過一個虧:部門的一次工作...
    白小白女性生涯規(guī)劃閱讀 273評論 0 0
  • 今天真是忙了一天雷滚,本來說把牌子掛完,把我那個路弄起來吗坚,結(jié)果牌子掛了一天祈远。 我的方向感不太強(qiáng),還有表格不好弄...
    小不點(diǎn)Fight閱讀 120評論 0 0
  • 今天40分鐘色彩搭配課程商源,依然自己學(xué)習(xí)色彩資料庫里的內(nèi)容车份,今天了解到160種色彩搭配風(fēng)格,看的時候很多不懂牡彻,姑且當(dāng)...
    貴妃Matilda閱讀 1,717評論 0 3