原生js实现拖拽功能

在前端技术日新月异,飞速发展的当下,涌现出了很多优秀的开源框架以及优秀的开源组件,这些都是优秀的前端开发者的技术成果。当然,虽然有这么多现成的开源技术成果供我们再开发中使用,但我们不能仅抱着拿来主义的态度,只顾拿来用就行,更多的是要以学习的心态来对待开源技术,对待前端世界,要多汲取他(她)人的经验,且又不忘根本(基础知识),站在巨人的肩膀上继续前行!

js拖拽功能

拖拽功能是前端很多业务场景都能使用到的一种普遍的技术,比如弹窗视口的拖拽等,前端世界的很多优秀框架组件都拥有封装完美的拖拽功能,别人封装的优秀源码,健壮,稳定,同时又优雅美观,自然是值得我们去学习的。这篇文章主要讲解下如何用原生js实现一个简单的拖拽功能。

拖拽原理

对于PC端来说,拖拽功能无非就是鼠标点击,鼠标开始移动,鼠标松开三个步骤,当然这三个步骤里面有拆分了很多的细节,越是健壮,稳定的代码,对细节的处理就越严谨(我们主要讲实现一个简单的拖拽功能,可能有些细节处理得不妥当,待以后再次进行封装)。

  • 鼠标点击:当鼠标按下点击的时候,需要拖拽的容器就已应该准备就绪 进去拖拽状态了

  • 鼠标移动:需要拖拽的容器根据鼠标的移动位置而移动,这里面就包含的很多对细节的处理,如:当容器某一端的位置到达了浏览器边缘的时候,是不应该超出浏览器范围区域的,不然会撑出滚动条或者被遮挡,这不是一个很好的效果。那么要如何把容器的位置控制在浏览器规定的范围区域呢?这里就要分别处理上下左右四个方向的位置变化。
    1.top端:当容器的顶部到达了浏览器顶部边缘的时候,鼠标移动的方向继续是向上移动,那么就要设置容器顶端的top值始终为0,保证容器位置不会溢出浏览器范围。
    2.bottom端:当容器的底部到达了浏览器底部边缘的时候,鼠标移动的方向继续是向下移动,那么就要设置容器底端的bottom值始终为浏览器可视区域的高度值减去容器的高度值,保证容器位置不会溢出浏览器范围。
    3.left端:当容器的左端到达了浏览器左端边缘的时候,鼠标移动的方向继续是向左移动,那么就要设置容器顶端的left值始终为0,保证容器位置不会溢出浏览器范围。
    3.right端:当容器的右端到达了浏览器右端边缘的时候,鼠标移动的方向继续是向右移动,那么就要设置容器顶端的right值始终为浏览器可视区域的宽度值减去容器的宽度值,保证容器位置不会溢出浏览器范围。

  • 鼠标松开:代表着容器拖拽结束,停止改变容器的位置。这里也有个小细节需要注意下,就是,鼠标点击容器准备拖拽时候的位置要和鼠标松开后鼠标在容器中的位置是一致的,意思就是在拖拽的过程当中不管鼠标的位置在什么地方,停止拖拽后都要回到原点。

接口参数暴露

完美的封装往往都是需要搭配强大的配置参数来维护它的健壮,稳定的。

对于拖拽功能,需要配置的参数通常需要有以下内容:

  • 需要拖拽的容器(必选)

  • 需要拖拽的区域(可选)

  • 是否可拖拽,拖拽开关(可选)

  • 是否需要虚拟拖拽容器(可选)

  • 控制虚拟拖拽容器的样式(可选)

配置上这些参数,一个简单的拖拽功能就可以使用了。

拖拽示例核心代码

调用方式

new DragView({
    "dragWrap": document.getElementById("drag_wrap"),    //容器,必选
    "dragableArea": document.getElementById("drag_area"),  //可拖拽区域,可选
    "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哈!**

原创文章,站在前辈们的经验上的总结,文中如有不正之处,还望指正!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,537评论 25 707
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,624评论 1 92
  • 1 刚毕业那会儿,我其实是一个性子比较急躁的人,说话语速也快,跟跟打机关枪似得。 曾经还吃过一个亏:部门的一次工作...
    白小白女性生涯规划阅读 257评论 0 0
  • 今天真是忙了一天,本来说把牌子挂完,把我那个路弄起来,结果牌子挂了一天。 我的方向感不太强,还有表格不好弄...
    小不点Fight阅读 110评论 0 0
  • 今天40分钟色彩搭配课程,依然自己学习色彩资料库里的内容,今天了解到160种色彩搭配风格,看的时候很多不懂,姑且当...
    贵妃Matilda阅读 1,688评论 0 3