第十周第三天笔记

1 知识点复习

  • 事件
    • 事件分为DOM0级事件和DOM2级事件
    • DOM0级事件:oDiv.onclick=函数名/匿名函数
      • 缺点:1)一个元素一种行为只能绑定一个方法,不能多次绑定;2)当事件触发时,在标准浏览器(包括IE9,10)下会默认向函数名或匿名函数中,传入一个事件对象实参,在IE8及其以下浏览器中,不会默认传入实参;
    • DOM2级事件:通过addEventListener()attachEvent()来绑定;
      • 缺点:在IE浏览器下用attachEvent绑定会出现三个问题:1)函数中this执行window,不是元素;2)同一个方法可以重复绑定上,然后执行多次;3)执行时不按绑定的顺序执行,为乱序执行;
    • 封装event事件库
      • 目的:解决IE浏览器向DOM2级绑定中attachEvent的问题
      • 封装结果:同一个元素,同一个行为可以绑定多个方法,并且保证函数执行中this指向元素,顺序执行,并且向函数中默认传事件对象实参,如果函数中需要使用事件对象,就设置形参e,如果不需要,则不设置;
  • bind知识点
    • bind方法:指的是在标准浏览器(包括IE9,10)下对函数的预处理,可改变函数的this指向,传实参,当与事件配合使用时,默认传事件对象实参;
      • 缺点:IE8及其以下浏览器不支持bind属性
    • 封装myBind方法库
      • 目的:达到与bind一样的效果,和相同使用方式;
      • 封装结果:与bind的使用方式相同,效果一样;即在所有浏览器下兼容;改变函数的this指向,传入实参,与事件配合使用时,默认传事件对象实参,事件对象已经兼容处理;
  • 事件赋值的不同情况
    • DOM0级事件:
      • 需求:存在一个函数名为add的函数,当点击事件发生时,执行add函数并改变其this指向和获取实参,包括事件对象
      • 两种方法:
        • 1)给事件添加匿名函数,匿名函数中用call来改变this指向,但是要注意的是事件对象的兼容处理,在标准浏览器下,事件触发,会默认给匿名函数传事件对象实参,但IE浏览器下不会传事件对象;
        • 代码:
        <script>
            var oDiv=document.getElementById("div1");
            function add(n,m,e){
                console.log(this);
                console.log(n+m);
                console.log(e.clientX,e.clientY);
                console.log(e.pageX,e.pageY);
            }
            oDiv.onclick=function (e) {
                if(!e){//当为IE浏览器时,不会传e,所以e此时为undefined,需要重新获取事件对象,然后做兼容处理,与标准浏览器相同;
                    e=window.event;
                    //IE浏览器下的兼容处理
                    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.preventDefault=function () {
                        e.returnValue=false;
                    };
                    //阻止冒泡
                    e.stopPropagation=function () {
                        e.cancelBubble=true;
                    };
                }
                add.call(this,2,3,e);//此时通过call来改变this指向,传参
            }
        </script>
        
        • 2)利用封装的myBind方法:所有浏览器都兼容,事件对象也兼容
        • 代码:
         <body>
         <div id="div1"></div>
         <script src="JS/01event.js"></script>
         <script src="JS/02myBind.js"></script>
         <script>
             var oDiv=document.getElementById("div1");
             function add(n,m,e){
                 console.log(this);
                 console.log(n+m);
                 console.log(e.clientX,e.clientY);
                 console.log(e.pageX,e.pageY);
             }
             //myBind方法,不用传e;
             oDiv.onclick=add.myBind(oDiv,2,3);
         </script>
         </body>
        
    • DOM2级事件:
      • 实质:使用封装的event库,来绑定事件,事件触发时,会给使函数中的this指向为该元素,默认传入一个事件对象实参;兼容所有浏览器;
      • 需求:存在一个函数名为add的函数,当点击事件发生时,执行add函数并改变其this指向和获取实参,包括事件对象
      • 注意:利用myBind绑定时,直接解绑函数名,无法解绑,需要新建一个变量,然后解绑变量,变量就代表一个匿名函数;
       <body>
       <div id="div1">111</div>
       <script src="JS/01event.js"></script>
       <script src="JS/02myBind.js"></script>
       <script>
           var oDiv=document.getElementById("div1");
           function add(n,m,e){
               console.log(this.innerHTML);
               console.log(n+m);
               console.log(e.clientX,e.clientY);
               console.log(e.pageX,e.pageY);
           }
           //1 利用匿名函数绑定
           on(oDiv,"click",function (e) {
               add.call(this,2,3,e);
           });
           //2 利用myBind函数
           var arr=add.myBind(oDiv,2,3);//将地址赋值变量,然后解绑变量;
           on(oDiv,"click",arr);
           off(oDiv,"click",arr);//解绑
       </script>
       </body>
      

2 拖拽实例

2.1 es6版面向对象的纯净版拖拽实例

  • 知识点:
    • processAge函数封装:改变函数中的this指向,注意事件对象的传参
     function processAge(fn,thisArg) {
         return function (e) {
             fn.call(thisArg,e);
         }
     }
    
    • 使用事件库绑定事件时,绑定的函数为匿名函数,不能解绑,所以需要新建一个变量来赋值匿名函数,然后解绑变量;
     this.MOVE=processAge(this.move,this);
     on(this.ele,"mousemove",this.MOVE);
     off(this.ele,"mousemove",this.MOVE);
    
  • 代码:
    • 执行代码:
     <script>
         var oDiv=document.getElementById("div1");
         var drag=new Drag({
             ele:oDiv
         })
     </script>
    
    • JS封装代码:
     class Drag{
         constructor(opt){
             opt=opt||{};
             if(!opt.ele) return;
             this.ele=opt.ele;
             this.disX=null;
             this.disY=null;
             this.maxL=null;
             this.maxT=null;
             this.DOWN=null;
             this.MOVE=null;
             this.UP=null;
             this.init();
         }
         init(){
             this.DOWN=processAge(this.down,this);
             on(this.ele,"mousedown",this.DOWN);
         }
         down(e){
             //通过元素位置计算出光标相对于元素内的位置 disX disY
             var l=this.ele.offsetLeft;
             var t=this.ele.offsetTop;
             var x=e.clientX;
             var y=e.clientY;
             this.disX=x-l;
             this.disY=y-t;
             this.MOVE=processAge(this.move,this);//此时为一个匿名函数;
             this.UP=processAge(this.up,this);
             //添加事件
             if(this.ele.setCapture){//IE浏览器设置焦点捕获
                 this.ele.setCapture();
                 on(this.ele,"mousemove",this.MOVE);
                 on(this.ele,"mouseup",this.UP);
             }else{//标准浏览器下,给document设置事件,阻止默认事件
                 on(document,"mousemove",this.MOVE);
                 on(document,"mouseup",this.UP);
                 e.preventDefault();//阻止默认事件;防止选中文字;
             }
         }
         move(e){
             //边界值判断
             var l=e.clientX-this.disX;
             var t=e.clientY-this.disY;
             this.maxL=(document.documentElement.clientWidth || document.body.clientWidth)-this.ele.offsetWidth;
             this.maxT=(document.documentElement.clientHeight || document.body.clientHeight)-this.ele.offsetHeight;
             if(l<=0){
                 l=0;
             }else if(l>=this.maxL){
                 l=this.maxL;
             }
             if(t<=0){
                 t=0;
             }else if(t>=this.maxT){
                 t=this.maxT;
             }
             //设置新位置
             this.ele.style.left=l+"px";
             this.ele.style.top=t+"px";
         }
         up(){
             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);
             }
         }
     }
    

2.2 弹性运动拖拽实例

  • 弹性运动的实现本质:
    • X轴方向:
      • 在移动事件move函数中,获取每次运动的光标位置与上一次的光标位置之间的距离,作为运动速度,通过乘以小于1的参数,来不断的缩小;
      • 在鼠标抬起事件up的函数中,执行一个函数dropX;
      • 在dropX函数中,设置物体的left值,使其加上移动的速度值;通过定时器来不断的更新位置;直到运动速度值的绝对值小于一定值(一般为0.5)后,定时器不再运行;
      • 在drop函数中,要判断left的边界值,当达到0或maxL时,设置运动速度承等-1,这样就会实现反弹的效果;
    • Y轴方向:
      • 在鼠标抬起事件up的函数中,执行一个函数dropY;
      • 在dropY函数中,设置下落的速度值,给其赋值初始值为9.8,指的是重力加速度值,可以赋值其他值;然后定时器开启再次执行dropY函数时,给速度值累加9.8,然后给其承等小于1的参数;然后设置top值
      • 通过边界值的判断,来使速度值乘等-1,达到反弹效果;
      • 当物体下落到最底部时,定时器还在运行,通过设置一个开关stemp来控制定时器的运行;
  • 知识点:
    • 设置this.speedX*=0.93;this.speedY*=0.98,指的就是让speed的值不断的减少,然后才会达到停下来的效果,如果给其乘等1,那么物体就不会停下来,一直反弹;
    • 边界值判断后,设置this.speedY*=-1,才能达到反弹的效果;
    • X轴方向dropX函数中定时器不再执行的条件:判断speedX的值小于一定值,然后使其不再执行;
    • Y轴方向dropY函数中定时器不再执行的条件:在边界值判断中,利用一个变量stemp来控制,当物体开始下落过程中t的实时值会小于maxT值,当t的值大于maxT值后,stemp赋值为1,然后会被反弹,t值小于maxT值,stemp又会被赋值为0;直到物体的值持续大于maxT值后,stemp一直累加,大于2后,定时器不再执行;
    • 当物体在经过反弹后,speedY值开始从负数累加定值,然后当speedY值大于0时,达到最高点,然后继续累加,然后下降;
  • 注意点:
    • 在设置定时器后,每次在执行dropX和dropY函数时,都需要关闭定时器
    • 在鼠标按下事件down函数中,要关闭定时器,这样当鼠标再次按下的时候,定时器会被关闭;
    • X轴方向dropX函数中,判断运动速度值时,要判断绝对值;
    • 设置定时器时setTimeout(processAge(this.dropY,this),30),递归dropY函数时,不能直接将this.dropY当成参数传入,若当成参数传入,此时执行的dropY函数中的this就不再是实例this,而是window,所以需要改变this,使其为实例;
  • 代码:
    • 封装JS代码:
     class Drag{
         constructor(opt){
             opt=opt||{};
             if(!opt.ele) return;
             this.ele=opt.ele;
             this.disX=null;
             this.disY=null;
             this.DOWN=null;
             this.MOVE=null;
             this.UP=null;
             this.maxL=(document.documentElement.clientWidth || document.body.clientWidth)-this.ele.offsetWidth;
             this.maxT=(document.documentElement.clientHeight || document.body.clientHeight)-this.ele.offsetHeight;
             this.init();
         }
         init(){
             this.DOWN=processAge(this.down,this);
             on(this.ele,"mousedown",this.DOWN);
         }
         down(e){
             //通过元素位置计算出光标相对于元素内的位置 disX disY
             var l=this.ele.offsetLeft;
             var t=this.ele.offsetTop;
             var x=e.clientX;
             var y=e.clientY;
             this.disX=x-l;
             this.disY=y-t;
             this.MOVE=processAge(this.move,this);//此时为一个匿名函数;
             this.UP=processAge(this.up,this);
             this.xtimer=this.ytimer=null;
             //添加事件
             if(this.ele.setCapture){//IE浏览器设置焦点捕获
                 this.ele.setCapture();
                 on(this.ele,"mousemove",this.MOVE);
                 on(this.ele,"mouseup",this.UP);
             }else{//标准浏览器下,给document设置事件,阻止默认事件
                 on(document,"mousemove",this.MOVE);
                 on(document,"mouseup",this.UP);
                 e.preventDefault();//阻止默认事件;防止选中文字;
             }
             //点击按下时,关闭定时器;
             clearTimeout(this.xtimer);
             clearTimeout(this.ytimer);
         }
         move(e){
             //设置新位置
             var l=e.clientX-this.disX;
             var t=e.clientY-this.disY;
             if(l<=0){
                 l=0;
             }else if(l>=this.maxL){
                 l=this.maxL;
             }
             if(t<=0){
                 t=0;
             }else if(t>=this.maxT){
                 t=this.maxT;
             }
             this.ele.style.left=l+"px";
             this.ele.style.top=t+"px";
             //弹性运动的数据
             if(!this.prevX){
                 this.prevX=e.clientX;
             }else{
                 this.speedX=e.clientX-this.prevX;
                 this.prevX=e.clientX;
             }
         }
         up(){
             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.dropX();
             this.dropY();
         }
         dropX(){
             clearTimeout(this.xtimer);
             this.speedX*=0.93;
             var l=this.ele.offsetLeft+this.speedX;
             if(l<=0){
                 l=0;
                 this.speedX*=-1;
             }else if(l>=this.maxL){
                 l=this.maxL;
                 this.speedX*=-1;
             }
             this.ele.style.left=l+"px";
             //判断this.speedX值的绝对值小于0.5后,定时器不再执行;
             if(Math.abs(this.speedX)>0.5){
                 this.xtimer=setTimeout(processAge(this.dropX,this),30);
             }
         }
         dropY(){
             clearTimeout(this.ytimer);
             if(!this.speedY){
                 this.speedY=9.8;
             }else{
                 this.speedY+=9;
             }
             this.speedY*=0.98;//控制弹跳的频率;值越大,弹跳的频率越多;
             var t=this.ele.offsetTop+this.speedY;
             //边界值判断,然后通过stemp开关来控制定时器的开启,物体在底部弹跳时,stemp取值为0,1之间;持续大于maxT值后,会自增;然后大于2;
             if(t>=this.maxT){
                 t=this.maxT;
                 this.speedY*=-1;
                 this.stemp++;//当物体在最下面的时候,持续大于maxT值后,会自增;然后大于2;
             }else{
                 this.stemp=0;
             }
             this.ele.style.top=t+"px";
             if(this.stemp<2){
                 this.ytimer=setTimeout(processAge(this.dropY,this),30);
             }
         }
     }
    

2.3 订阅式发布体验

  • 订阅式发布的本质:
    • 需求:一个元素身上创建一个自定义行为,这个行为包括很多方法,当需要执行该元素身上的这个自定义行为时,让所有的跟它有关的方法都执行;
    • 实质:
      • 给元素ele身上这个自定义行为创建一个自定义属性,这个属性的属性值为一个数组,然后将所有与该行为有关联的方法,插入到这个数组中,避免重复插入;
      • 创建一个函数fire,函数中就是为了执行该行为数组中的所有方法;需要使用该行为时,调用fire函数;
  • 订阅式发布的思路:
    • 创建on函数:给一个元素创建一个自定义行为,然后给这个行为绑定多个不同的方法,然后等待调用执行;
    • 创建fire函数:函数内部是执行元素this身上的自定义行为,上绑定的多个方法;
  • 与事件库的区别:
    • 事件库:是通过给元素在系统事件池中的系统行为绑定一个run函数,在改事件行为触发时,执行run函数,run函数中同样也是执行该行为的所有方法;保证执行函数中的this指向和传入事件对象实参;
    • 订阅式发布封装的on函数和fire函数,是给元素创建一个自定义行为,然后给这个自定义行为绑定多个不同方法,然后通过fire函数的调用,来执行这些方法;保证执行函数中的this指向和传入事件对象实参;
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>订阅式发布体验</title>
         <style>
             div{
                 width: 200px;
                 height: 200px;
                 background-color: red;
             }
         </style>
     </head>
     <body>
     <div id="div"></div>
     <script>
         var oDiv=document.getElementById("div");
         //封装一个on函数,给元素的一个行为绑定不同的方法;
         //on函数的目的:给元素身上创建一个自定义属性,赋值为数组,作为元素自己的事件池,然后将每个方法push进事件池数组中;避免重复放入;
         function on(ele,type,fn){
             //思路:元素创建一个自定义属性,作为自己的事件池,里面插入方法fn
             if(!ele["on"+type]){
                 ele["on"+type]=[];
             }
             var a=ele["on"+type];
             if(a.length){
                 //去重
                 for(var i=0; i<a.length; i++){
                     if(a[i]===fn) return;
                 }
             }
             //把fn都放在事件池中
             a.push(fn);
         }
         //发射器fire函数:目的是监听行为,当需要执行type行为的时候,让所有的方法执行
         //fire函数的目的:待执行,当需要使用该元素身上的自定义行为时,就调用执行函数,将自己事件池上的方法调用执行,保证方法函数中的this指向为该元素和传入实参e;
         //注意:调用时,必须保证函数中的this指向为该元素;
         function fire(type,e){
             //保证函数中的this为元素
             var a=this["on"+type];
             if(a.length){
                 for(var i=0; i<a.length; i++){
                     a[i].call(this,e);//保证了函数在执行时,里面的this指向为元素,并传入实参e;
                 }
             }
         }
         on(oDiv,"meihao",fn1);
         on(oDiv,"meihao",fn2);
         on(oDiv,"meihao",fn3);
         on(oDiv,"meihao",fn4);
         function fn1() {
             console.log("喜欢");
         }
         function fn2() {
             console.log("相濡以沫");
         }
         function fn3(e) {
             console.log("共同努力");
             console.log(e.type)
         }
         function fn4() {
             console.log(this);
             console.log("相互关心爱护");
         }
         //需求:给oDiv添加点击事件,当点击事件触发时,执行meihao行为上所有的方法;
         oDiv.onclick=function (e) {
             e=e||window.event;
             fire.call(this,"meihao",e);
         }
     </script>
     </body>
     </html>
    

3 订阅式发布版与继承版弹性运动拖拽实例

  • 继承与订阅发布的区别:
    • 继承:属于开发人员自己进行版本升级,不同的功能创建不同的类函数;
    • 订阅发布:针对的是用户,即:给用户留好升级的接口,如果用户想要扩充功能,用户自己添加;
  • 订阅发布函数EventEmitter
    • 目的:给实例对象this创建一个自定义属性,属性值为一个数组,然后将自定义行为上的方法插入到数组中;再创建一个函数fire,用于执行数组中的方法;
    • 注意点:
      • on函数中的this都指的是实例对象,添加返回值,返回实例this,用于链式操作;
      • fire函数中获取数组时,由于on函数在正常情况下,不会绑定,所以不会创建数组,则拿到的a为undefined,会报错,所以为了防止报错,必须设置当数组不存在时,让其为空数组;
      • 在执行数组内绑定的方法时,要保证函数中的this为实例对象,和传入事件对象实参;
      • on和fire函数调用的时候,都是需要实例对象调用;
      • fire函数在每个需要接口的地方调用,当需要添加功能时,就给指定行为用on绑定行为;然后就会执行;
      • on函数在使用时,用实例对象使用,并且可以进行链式操作;
    • 代码:
     class EventEmitter{
         constructor(){}
         //on函数方法,给实例this创建一个自定义行为,给这个行为绑定多个方法
         on(type,fn){
             if(!this[type]){
                 this[type]=[];
             }
             var a=this[type];
             if(a.length){
                 for(var i=0; i<a.length; i++){
                     if(a[i]===fn) return;
                 }
             }
             a.push(fn);
             return this;//用于链式操作;
         }
         fire(type,e){
             var a=this[type] || [];//需注意,如果不绑定的话,a不存在,会报错,所以给a赋值为空数组;
             if(a.length){
                 for(var i=0; i<a.length; i++){
                     a[i].call(this,e);//保证函数中的this为实例对象;
                 }
             }
         }
     }
    
  • 订阅式发布版拖拽实例
    • 本质:将实例drag继承EventEmitter的属性方法;然后在想要扩展功能的地方调用fire函数,用做接口,当需要扩展功能时,用on添加方法,然后自动添加方法执行,不会改变原来的函数类的代码功能;
    • 不会改变原来的函数类,只需重新建立另一个实例,就可调动;
    • 将纯净的拖拽转变为弹性运动功能的拖拽的思路:
      • 在down,move,up三个函数中,添加接口this.fire("myMove",e),即给实例对象添加了一个myMove行为,相当于发布上,如果没有给myMove用on来绑定方法,就不执行,当用on绑定方法后,就会立刻执行;可以给同一行为绑定多个方法;
      • 在使用时,创建实例对象,然后给实例对象用on给其myMove的行为绑定多个方法,然后到达弹性运动的效果;在用on绑定时,可以用链式操作;
    • 代码:
      • 执行代码:
       <script>
           var oDiv=document.getElementById("div1");
           var drag=new Drag({
               ele:oDiv
           });
           //想要进行链式操作,必须保证on函数中返回的值为实例对象;
           drag.on("myDown",closeTimer).on("myMove",fn1).on("myUp",dropX).on("myUp",dropY);
           function closeTimer() {
               clearTimeout(this.xtimer);
               clearTimeout(this.ytimer);
           }
           function fn1(e) {
               //获取X轴方向上的speedX
               if(!this.prevX){
                   this.prevX=e.clientX;
               }else{
                   this.speedX=e.clientX-this.prevX;
                   this.prevX=e.clientX;
               }
           }
           function dropX(e) {
               clearTimeout(this.xtimer);
               this.speedX*=0.93;
               var l=this.ele.offsetLeft+this.speedX;
               if(l<=0){
                   l=0;
                   this.speedX*=-1;
               }else if(l>=this.maxL){
                   l=this.maxL;
                   this.speedX*=-1;
               }
               this.ele.style.left=l+"px";
               if(Math.abs(this.speedX)>0.5){
                   this.xtimer=setTimeout(processAge(arguments.callee,this),30);//注意定时器中的函数调用时,函数中this为window;
               }
           }
           function dropY(e) {
               clearTimeout(this.ytimer);
               if(!this.speedY){
                   this.speedY=9.8
               }else{
                   this.speedY+=9.8;
               }
               this.speedY*=0.98;
               var t=this.ele.offsetTop+this.speedY;
               if(t>=this.maxT){
                   t=this.maxT;
                   this.speedY*=-1;
                   this.stmp++;
               }else{
                   this.stmp=0;
               }
               this.ele.style.top=t+"px";
               if(this.stmp<2){
                   this.ytimer=setTimeout(processAge(arguments.callee,this),30);
               }
           }
       </script>
      
      • JS代码:
       //订阅式发布类函数
       class EventEmitter{
           constructor(){}
           //on函数方法,给实例this创建一个自定义行为,给这个行为绑定多个方法
           on(type,fn){
               if(!this[type]){
                   this[type]=[];
               }
               var a=this[type];
               if(a.length){
                   for(var i=0; i<a.length; i++){
                       if(a[i]===fn) return;
                   }
               }
               a.push(fn);
               return this;//用于链式操作;
           }
           fire(type,e){
               var a=this[type] || [];//需注意,如果不绑定的话,a不存在,会报错,所以给a赋值为空数组;
               if(a.length){
                   for(var i=0; i<a.length; i++){
                       a[i].call(this,e);//保证函数中的this为实例对象;
                   }
               }
           }
       }
       
       class Drag extends EventEmitter{
           constructor(opt){
               super();
               opt=opt||{};
               if(!opt.ele) return;
               this.ele=opt.ele;
               this.disX=null;
               this.disY=null;
               this.maxL=null;
               this.maxT=null;
               this.DOWN=null;
               this.MOVE=null;
               this.UP=null;
               this.init();
           }
           init(){
               this.DOWN=processAge(this.down,this);
               on(this.ele,"mousedown",this.DOWN);
           }
           down(e){
               //通过元素位置计算出光标相对于元素内的位置 disX disY
               var l=this.ele.offsetLeft;
               var t=this.ele.offsetTop;
               var x=e.clientX;
               var y=e.clientY;
               this.disX=x-l;
               this.disY=y-t;
               this.MOVE=processAge(this.move,this);//此时为一个匿名函数;
               this.UP=processAge(this.up,this);
               //添加事件
               if(this.ele.setCapture){//IE浏览器设置焦点捕获
                   this.ele.setCapture();
                   on(this.ele,"mousemove",this.MOVE);
                   on(this.ele,"mouseup",this.UP);
               }else{//标准浏览器下,给document设置事件,阻止默认事件
                   on(document,"mousemove",this.MOVE);
                   on(document,"mouseup",this.UP);
                   e.preventDefault();//阻止默认事件;防止选中文字;
               }
               //关闭定时器
               this.fire("myDown",e);
           }
           move(e){
               //边界值判断
               var l=e.clientX-this.disX;
               var t=e.clientY-this.disY;
               this.maxL=(document.documentElement.clientWidth || document.body.clientWidth)-this.ele.offsetWidth;
               this.maxT=(document.documentElement.clientHeight || document.body.clientHeight)-this.ele.offsetHeight;
               if(l<=0){
                   l=0;
               }else if(l>=this.maxL){
                   l=this.maxL;
               }
               if(t<=0){
                   t=0;
               }else if(t>=this.maxT){
                   t=this.maxT;
               }
               //设置新位置
               this.ele.style.left=l+"px";
               this.ele.style.top=t+"px";
               //获取X轴方向上的运动速度
               this.fire("myMove",e);
           }
           up(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("myUp",e);
           }
       }
      
  • 继承版拖拽实例
    • 思路:继承纯净版类函数Drag,然后在新的类函数中,创建函数,绑定事件,然后执行函数;
    • 使用时:两个类独立开来,使用哪个功能,创建哪个类的实例对象;
    • 注意点:给事件行为mousemove绑定方法时,需要根据浏览器种类,给不同的元素绑定事件行为的方法;
    • 代码:
      • 执行代码:
       <script>
           var oDiv=document.getElementById("div1");
           //1 纯净版拖拽,创建Drag实例对象;
           var drag=new Drag({
               ele:oDiv
           });
           //2 弹性运动版拖拽,创建Elastic实例对象
           var drag1=new Elastic({
               ele:oDiv
           })
       </script>
      
      • JS代码:
       class Drag{
           constructor(opt){
               opt=opt||{};
               if(!opt.ele) return;
               this.ele=opt.ele;
               this.disX=null;
               this.disY=null;
               this.maxL=null;
               this.maxT=null;
               this.DOWN=null;
               this.MOVE=null;
               this.UP=null;
               this.init();
           }
           init(){
               this.DOWN=processAge(this.down,this);
               on(this.ele,"mousedown",this.DOWN);
           }
           down(e){
               //通过元素位置计算出光标相对于元素内的位置 disX disY
               var l=this.ele.offsetLeft;
               var t=this.ele.offsetTop;
               var x=e.clientX;
               var y=e.clientY;
               this.disX=x-l;
               this.disY=y-t;
               this.MOVE=processAge(this.move,this);//此时为一个匿名函数;
               this.UP=processAge(this.up,this);
               //添加事件
               if(this.ele.setCapture){//IE浏览器设置焦点捕获
                   this.ele.setCapture();
                   on(this.ele,"mousemove",this.MOVE);
                   on(this.ele,"mouseup",this.UP);
               }else{//标准浏览器下,给document设置事件,阻止默认事件
                   on(document,"mousemove",this.MOVE);
                   on(document,"mouseup",this.UP);
                   e.preventDefault();//阻止默认事件;防止选中文字;
               }
           }
           move(e){
               //边界值判断
               var l=e.clientX-this.disX;
               var t=e.clientY-this.disY;
               this.maxL=(document.documentElement.clientWidth || document.body.clientWidth)-this.ele.offsetWidth;
               this.maxT=(document.documentElement.clientHeight || document.body.clientHeight)-this.ele.offsetHeight;
               if(l<=0){
                   l=0;
               }else if(l>=this.maxL){
                   l=this.maxL;
               }
               if(t<=0){
                   t=0;
               }else if(t>=this.maxT){
                   t=this.maxT;
               }
               //设置新位置
               this.ele.style.left=l+"px";
               this.ele.style.top=t+"px";
           }
           up(){
               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);
               }
           }
       }
       class Elastic extends Drag{
           constructor(opt){
               super(opt);
               this.speedX=null;
               this.speedY=null;
               this.stemp=null;
               this.xtimer=this.ytimer=null;
               this.DOWN2=processAge(this.down2,this);
               this.MOVE2=processAge(this.move2,this);
               this.UP2=processAge(this.up2,this);
               this.init2();
           }
           init2(){
               on(this.ele,"mousedown",this.DOWN2);
           }
           down2(){
               if(this.ele.setCapture){//IE浏览器设置焦点捕获
                   on(this.ele,"mousemove",this.MOVE2);
                   on(this.ele,"mouseup",this.UP2);
               }else{//标准浏览器下,给document设置事件,阻止默认事件
                   on(document,"mousemove",this.MOVE2);
                   on(document,"mouseup",this.UP2);
               }
               clearTimeout(this.xtimer);
               clearTimeout(this.ytimer);
           }
           move2(e){
               if(!this.prevX){
                   this.prevX=e.clientX;
               }else{
                   this.speedX=e.clientX-this.prevX;
                   this.prevX=e.clientX;
               }
           }
           up2(){
               this.dropX();
               this.dropY();
               if(this.ele.setCapture){//IE浏览器设置焦点捕获
                   off(this.ele,"mousemove",this.MOVE2);
                   off(this.ele,"mouseup",this.UP2);
               }else{//标准浏览器下,给document设置事件,阻止默认事件
                   off(document,"mousemove",this.MOVE2);
                   off(document,"mouseup",this.UP2);
               }
           }
           dropX(){
               clearTimeout(this.xtimer);
               this.speedX*=0.93;
               var l=this.ele.offsetLeft+this.speedX;
               if(l<=0){
                   l=0;
                   this.speedX*=-1;
               }else if(l>=this.maxL){
                   l=this.maxL;
                   this.speedX*=-1;
               }
               this.ele.style.left=l+"px";
               //判断this.speedX值的绝对值小于0.5后,定时器不再执行;
               if(Math.abs(this.speedX)>0.5){
                   this.xtimer=setTimeout(processAge(this.dropX,this),30);
               }
           }
           dropY(){
               clearTimeout(this.ytimer);
               if(!this.speedY){
                   this.speedY=9.8;
               }else{
                   this.speedY+=9.8;
               }
               this.speedY*=0.93;//控制弹跳的频率;值越大,弹跳的频率越多;
               var t=this.ele.offsetTop+this.speedY;
               //边界值判断,然后通过stemp开关来控制定时器的开启,物体在底部弹跳时,stemp取值为0,1之间;持续大于maxT值后,会自增;然后大于2;
               if(t>=this.maxT){
                   t=this.maxT;
                   this.speedY*=-1;
                   this.stemp++;//当物体在最下面的时候,持续大于maxT值后,会自增;然后大于2;
               }else{
                   this.stemp=0;
               }
               this.ele.style.top=t+"px";
               if(this.stemp<2){
                   this.ytimer=setTimeout(processAge(this.dropY,this),30);
               }
           }
       }
      
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 1 放大镜实例 页面结构:创建左右两个容器,左边容器中添加一张图片背景图,右边容器中添加一张与背景图成比例放大的图...
    果木山阅读 292评论 0 0
  • 1 标准浏览器中的DOM2级事件绑定 addEventListener绑定代码:oDiv.addEventList...
    果木山阅读 95评论 0 0
  • 事件 1 事件流 冒泡:从该元素开始由里向外触发一类事件,事件对象中的事件源为该元素,触发同一类事件,在子级父级祖...
    果木山阅读 192评论 0 0
  • ES6之Promise类 1 Promise类基础知识解读 promise类的静态属性方法分类:resolve()...
    果木山阅读 154评论 0 0
  • 1 事件 1.1 事件分类 事件分为DOM0级事件和DOM2级事件DOM0级事件:1)在元素的私有属性上;2)同一...
    果木山阅读 136评论 0 0