第十周第三天笔记

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浏览器下不会传事件对象;
        • 代码:
        
        
        • 2)利用封装的myBind方法:所有浏览器都兼容,事件对象也兼容
        • 代码:
         
         
    • DOM2级事件:
      • 实质:使用封装的event库,来绑定事件,事件触发时,会给使函数中的this指向为该元素,默认传入一个事件对象实参;兼容所有浏览器;
      • 需求:存在一个函数名为add的函数,当点击事件发生时,执行add函数并改变其this指向和获取实参,包括事件对象
      • 注意:利用myBind绑定时,直接解绑函数名,无法解绑,需要新建一个变量,然后解绑变量,变量就代表一个匿名函数;
       
       
      111

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);
    
  • 代码:
    • 执行代码:
     
    
    • 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指向和传入事件对象实参;
     
     
     
         
         订阅式发布体验
         
     
     
     

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
  • 订阅式发布版拖拽实例
    • 本质:将实例drag继承EventEmitter的属性方法;然后在想要扩展功能的地方调用fire函数,用做接口,当需要扩展功能时,用on添加方法,然后自动添加方法执行,不会改变原来的函数类的代码功能;
    • 不会改变原来的函数类,只需重新建立另一个实例,就可调动;
    • 将纯净的拖拽转变为弹性运动功能的拖拽的思路:
      • 在down,move,up三个函数中,添加接口this.fire("myMove",e),即给实例对象添加了一个myMove行为,相当于发布上,如果没有给myMove用on来绑定方法,就不执行,当用on绑定方法后,就会立刻执行;可以给同一行为绑定多个方法;
      • 在使用时,创建实例对象,然后给实例对象用on给其myMove的行为绑定多个方法,然后到达弹性运动的效果;在用on绑定时,可以用链式操作;
    • 代码:
      • 执行代码:
       
      
      • 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=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绑定方法时,需要根据浏览器种类,给不同的元素绑定事件行为的方法;
    • 代码:
      • 执行代码:
       
      
      • 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);
               }
           }
       }
      

你可能感兴趣的:(第十周第三天笔记)