JavaScript 闭包风格的拖拽

🌙
手机阅读
本文目录结构

1

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>闭包的拖拽</title>
    <style>
        div{width: 100px;height: 100px;position: absolute;left: 0;top: 0;background: red;cursor: move;}
        #div2{left: 200px;top:20px;background: darkblue;}
        #div3{left: 350px;top:30px;background: olive;}
    </style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
</body>
</html>
<script src="event.js"></script>
<script>
    var oDivs=document.getElementsByTagName("div");
    for(var i= 0,len=oDivs.length;i<len;i++){
        (function(ele) {
            on(ele,"mousedown",down)
            var x=null,y=null,mx=null,my=null;
            function down(e){
                x=this.offsetLeft;
                y=this.offsetTop;
                mx= e.pageX;
                my= e.pageY;
                if(ele.setCapture){
                    ele.setCapture();
                    on(ele,"mousemove",move);
                    on(ele,"mouseup",up);
                }else{
                    on(document,"mousemove",move);
                    on(document,"mouseup",up);
                }
                e.preventDefault();
            }
            function move(e){
                ele.style.left=x+(e.pageX-mx)+"px";
                ele.style.top=y+(e.pageY-my)+"px";
            }
            function up(e){
                if(ele.releaseCapture){
                    ele.releaseCapture();
                    off(ele,"mousemove",move);
                    off(ele,"mouseup",up);
                }else{
                    off(document,"mousemove",move)
                    off(document,"mouseup",up)
                }
            }
        })(oDivs.item(i));
    }
</script>

优化

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>闭包的拖拽</title>
    <style>
        div{width: 100px;height: 100px;position: absolute;left: 0;top: 0;background: red;cursor: move;}
        #div2{left: 200px;top:20px;background: darkblue;}
        #div3{left: 350px;top:30px;background: olive;}
    </style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
</body>
</html>
<script src="event.js"></script>
<script>
    var oDivs=document.getElementsByTagName("div");
        var DRAG=(function() {
            /*下面的方法都是可以重用的;*/
            //*module model 模块模式 三种情况*/
            /*1、变量要保护,2、里面的一些函数或属性还要重复使用;3、变量共享(数据共享的)*/
            var x=null,y=null,mx=null,my=null,flag=0;
            function down(e){
                x=this.offsetLeft;
                y=this.offsetTop;
                mx= e.pageX;
                my= e.pageY;
                if(this.setCapture){
                    this.setCapture();
                    on(this,"mousemove",move);
                    on(this,"mouseup",up);
                }else{
                    this._move=bindThis(this,move);
                    this._up=bindThis(this,up);
                    on(document,"mousemove",this._move);
                    on(document,"mouseup",this._up);
                }
                e.preventDefault();
                flag++;
                this.innerHTML="一共拖了"+flag+"次";
            }
            function move(e){
                this.style.left=x+(e.pageX-mx)+"px";
                this.style.top=y+(e.pageY-my)+"px";
            }
            function up(e){
                if(this.releaseCapture){
                    this.releaseCapture();
                    off(this,"mousemove",move);
                    off(this,"mouseup",up);
                }else{
                    off(document,"mousemove",this._move);
                    off(document,"mouseup",this._up);
                }
            }
            return {down:down,move:move,up:up}

        })();

    for(var i= 0,len=oDivs.length;i<len;i++) {
        on(oDivs[i],"mousedown",DRAG.down)

    }

/*
    *//*属于模块化开发,单利模式*//*
    DRAG.down();
    DRAG.move();
    *//*module model 模块模式*/
</script>

面向对象的拖拽

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>闭包的拖拽</title>
    <style>
        div{width: 100px;height: 100px;position: absolute;left: 0;top: 0;background: red;cursor: move;}
        #div2{left: 200px;top:20px;background: darkblue;}
        #div3{left: 350px;top:30px;background: olive;}
    </style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
</body>
</html>
<script src="event.js"></script>
<script>
    /*每一个单独行为,是依赖一个主体/上下文
    * 每一个主体是一个类的实例
    * 类是一种统筹管理的方式
    * */
    function Drag(ele){
        //初始化的,构造函数;
        this.x=null;
        this.y=null;
        this.mx=null;
        this.my=null;
        this.ele=ele;
        /*下面是强制指向当前的实例*/
        this._down=bindThis(this,this.down);
        this._move=bindThis(this,this.move);
        this._up=bindThis(this,this.up);
        on(this.ele,"mousedown",this._down)
    }
    Drag.prototype.down=function(e){
        /*下面的this是Drag的实例*/
        this.x=this.ele.offsetLeft;
        this.y=this.ele.offsetTop;
        this.mx= e.pageX;
        this.my= e.pageY;
        if(this.ele.setCapture){
            this.ele.setCapture();
            on(this.ele,"mousemove",this._move);
            on(this.ele,"mouseup",this._up);
        }else{
            on(document,"mousemove",this._move);
            on(document,"mouseup",this._up);
        }
    };
    Drag.prototype.move=function(e){
        this.ele.style.left=this.x+(e.pageX-this.mx)+'px';
        this.ele.style.top=this.y+(e.pageY-this.my)+'px';
    };
    Drag.prototype.up=function(e){
        if(this.ele.releaseCapture){
            this.ele.releaseCapture();
            off(this.ele,"mousemove",this._move);
            off(this.ele,"mouseup",this._up);
        }else{
            off(document,"mousemove",this._move);
            off(document,"mouseup",this._up);
        }
    };

    var obj1=new Drag(div1);
    var obj1=new Drag(div2);
    var obj1=new Drag(div3);
</script>

面向对象+设计模式+继承

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>闭包的拖拽</title>
    <style>
        div{width: 100px;height: 100px;position: absolute;left: 0;top: 0;background: red;cursor: move;}
        #div2{left: 200px;top:20px;background: darkblue;}
        #div3{left: 350px;top:30px;background: olive;}
    </style>
</head>
<body>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
</body>
</html>
<script src="event.js"></script>
<script>
    /*每一个单独行为,是依赖一个主体/上下文
    * 每一个主体是一个类的实例
    * 类是一种统筹管理的方式
    * */
    function EventEimter(){}
    EventEimter.prototype.on=function(type,fn){
        if(!this[type]){
            this[type]=[];
        }
        for(var i=0;i<this[type].length;i++){
            if(this[type][i]==fn)return;
        }
        this[type].push(fn)
    };
    EventEimter.prototype.fire=function(type,obj,e){
        var ary=this[type];
        if(ary){
            for(var i=0;i<ary.length;){
                if(typeof ary[i]=="function"){
                    ary[i].call(obj,e);
                    i++;
                }else{
                    ary.splice(i,1);
                }
            }
        }
    };
    EventEimter.prototype.off=function(type,fn){
        var ary=this[type];
        if(ary){
            for(var i=0;i<ary.length;i++){
                if(ary[i]==fn){
                    ary[i]=null;
                    return;
                }
            }
        }
    };

     function Drag(ele){
        //初始化的,构造函数;
        this.x=null;
        this.y=null;
        this.mx=null;
        this.my=null;
        this.ele=ele;
        /*下面是强制指向当前的实例*/
        this._down=bindThis(this,this.down);
        this._move=bindThis(this,this.move);
        this._up=bindThis(this,this.up);
        on(this.ele,"mousedown",this._down)
    }
    Drag.prototype=new EventEimter();
    Drag.prototype.down=function(e){
        /*下面的this是Drag的实例*/
        this.x=this.ele.offsetLeft;
        this.y=this.ele.offsetTop;
        this.mx= e.pageX;
        this.my= e.pageY;
        if(this.ele.setCapture){
            this.ele.setCapture();
            on(this.ele,"mousemove",this._move);
            on(this.ele,"mouseup",this._up);
        }else{
            on(document,"mousemove",this._move);
            on(document,"mouseup",this._up);
        }
        this.fire("dragStar",this.ele,e)
    };
    Drag.prototype.move=function(e){
        this.ele.style.left=this.x+(e.pageX-this.mx)+'px';
        this.ele.style.top=this.y+(e.pageY-this.my)+'px';
        this.fire("dragMove",this.ele,e)

    };
    Drag.prototype.up=function(e){
        if(this.ele.releaseCapture){
            this.ele.releaseCapture();
            off(this.ele,"mousemove",this._move);
            off(this.ele,"mouseup",this._up);
        }else{
            off(document,"mousemove",this._move);
            off(document,"mouseup",this._up);
        }
        this.fire("dragEnd",this.ele,e)
    };

    var obj1=new Drag(div1);
    obj1.on("dragStar",zIndexplus);
    obj1.on("dragStar",clearEffect);
    obj1.on("dragMove",getSpeed);
    obj1.on("dragEnd",drop);
    obj1.on("dragEnd",fly);
    var obj2=new Drag(div2);
    var obj3=new Drag(div3);



    /*左右反弹*/
    function fly(){
        clearTimeout(this.flytimer);
        var maxRight=(document.documentElement.clientWidth||document.body.clientWidth)-this.offsetWidth;
        this.speed*=0.93;
        if(this.offsetLeft+this.speed>=maxRight){
            this.style.left=maxRight+"px";
            this.speed*=-1;
        }else if(this.offsetLeft+this.speed<=0){
            this.style.left=0;
            this.speed*=-1;
        }else{
            this.style.left=this.offsetLeft+this.speed+"px";
        }
        if(Math.abs(this.speed)>=0.5){/*定时器停止的条件*/
            this.flytimer=window.setTimeout(bindThis(this,arguments.callee),30);
        }
    }
    /*自由落体*/
    var g=9.8;
    var flag=0;
    function drop(){
        clearTimeout(this.droptimer);
        var maxBottom=(document.documentElement.clientHeight||document.body.clientHeight)-this.offsetHeight;
        if(!this.dropSpeed){
            this.dropSpeed=g;
        }else{
            this.dropSpeed+=g;
        }
        /*下面是摩擦系数*/
        this.dropSpeed*=0.93;
        /*下面是边界判断*/
        if(this.offsetTop+this.dropSpeed>=maxBottom){
            this.style.top=maxBottom+"px";
            this.dropSpeed*=-1;
            flag++;
        }else{
            this.style.top=this.offsetTop+this.dropSpeed+"px";
            flag=0;
        }
        if(flag<2){
            this.droptimer=window.setTimeout(bindThis(this,arguments.callee),30);
        }
    }

    var zIndex=1;
    function zIndexplus(){
        this.style.zIndex=++zIndex;//解决图片覆盖的;
    }
    function clearEffect(){
        clearTimeout(this.flytimer);
        clearTimeout(this.droptimer);
    }
    function getSpeed(e){
        /*加动画效果的代码*/
        if(!this.prevX){
            //记录第一次/上一次move事件触发的时候鼠标X轴位置;
            this.prevX= e.clientX;
        }else{
            this.speed= e.clientX-this.prevX;//速度=这次位置-上次位置;
            this.prevX= e.clientX;//改变this.prevX;记录本次的位置
            //两次move事件之间的事件间大体是一样的;
            // 速度=单位时间的距离;
        }
        /*动画代码结束*/
    }

</script>

event.js

/*on:是负责往数组里安排一个队列的,程序池*/
function on(ele,type,fn){
    /*约定type如果以self开头的字符串,就是自定义事件,如果不是的,就是系统的事件*/
    if(/^self/.test(type)){//判断是否是自定义事件,这里是自定义事件;
        if(!ele[type]){
            ele[type]=[];
        }
        var aryEvent=ele[type];
        for(var i=0;i<aryEvent.length;i++){//防止同一个方法被同事件绑定;
            if(aryEvent[i]==fn)return;
        }
        aryEvent.push(fn);
    }else{
        if(ele.addEventListener){
            ele.addEventListener(type,fn);
        }else{
            if(!ele["aEvent"+type]){
                ele["aEvent"+type]=[];
                //bind3(ele,type,run);//只会执行一次,防止重复绑定;上一次封装的事件库是用bind解决的
                ele.attachEvent("on"+type,function(){run.call(ele)})//放在这里,因为是在if里,所以是有条件执行的,而且这里是只执行一次;防止重复绑定;
            }
            var aryEvent=ele["aEvent"+type];
            for(var i=0;i<aryEvent.length;i++){//防止同一个方法被同事件绑定;
                if(aryEvent[i]==fn)return;
            }
            aryEvent.push(fn);
        }
    }
}

/*run:负责具体的执行,在这里把IE兼容性全部解决掉;run方法是由系统的事件来触发的,真正绑定的是run方法*/
function run(){//run方法只用在IE678,解决系统事件的兼容性问题;;
    var e=window.event;
    e.target= e.srcElement;
    e.pageX=(document.documentElement.scrollLeft||document.body.scrollLeft)+ e.clientX;
    e.pageY=(document.documentElement.scrollTop||document.body.scrollTop)+ e.clientY;
    e.stopPropagation=function(){e.cancelBubble=true;}//阻止事件传播;
    e.preventDefault=function(){e.returnValue=false;}//阻止事件默认行为;
    var a=this["aEvent"+e.type];
    for(var i=0;i< a.length;){
        /*下面是防止数组塌陷的*/
        if(typeof a[i]=="function"){//这个是和off呼应的,off里有为null的值;
            a[i].call(this,e);//以后给元素写的方法,约定好都要写一个e的参数,这样就不需要再解决IE兼容性了;
            i++;
        }else{
            a.splice(i,1);
        }
    }
}
/*fire是写在某一个方法最后,任何浏览器任何自定义事件都可以用*/
function fire(selfType,e){//第一个参数是自定义事件的类型;第二个事件是系统事件对象;
    var ary=this[selfType];
    if(ary){
        for(var i=0;i<ary.length;i++){
            if(typeof  ary[i]=="function"){
                ary[i].call(this,e);
                i++
            }else{
                ary.splice(i,1);
            }

        }
    }
}

function off(ele,type,fn){
    if(/^self/.test(type)){//自定义事件
        var ary=ele[type];
        if(ary){
            for (var i=0;i<ary.length;i++){
                if(ary[i]==fn){
                    ary[i]==null;
                    return;
                }
            }
        }
    }else{
        if(ele.removeEventListener){
            ele.removeEventListener(type,fn);
        }else {
            var aryEvent=ele["aEvent"+type];
            if(aryEvent&& aryEvent.length){
                for(var i=0;i<aryEvent.length;i++){
                    if(aryEvent[i]==fn){
                        aryEvent[i]=null;
                        return;
                    }
                }
            }
        }
    }

}

function bindThis(obj,fn){
    return function(e){fn.call(obj,e)}
}

AXIHE / 精选资源

浏览全部教程

面试题

学习网站

前端培训
自己甄别

前端书籍

关于朱安邦

我叫 朱安邦,阿西河的站长,在杭州。

以前是一名平面设计师,后来开始接接触前端开发,主要研究前端技术中的JS方向。

业余时间我喜欢分享和交流自己的技术,欢迎大家关注我的 Bilibili

关注我: Github / 知乎

于2021年离开前端领域,目前重心放在研究区块链上面了

我叫朱安邦,阿西河的站长

目前在杭州从事区块链周边的开发工作,机械专业,以前从事平面设计工作。

2014年底脱产在老家自学6个月的前端技术,自学期间几乎从未出过家门,最终找到了满意的前端工作。更多>

于2021年离开前端领域,目前从事区块链方面工作了