第十周第三天筆記

1 知識點復(fù)習(xí)

  • 事件
    • 事件分為DOM0級事件和DOM2級事件
    • DOM0級事件:oDiv.onclick=函數(shù)名/匿名函數(shù)
      • 缺點:1)一個元素一種行為只能綁定一個方法选酗,不能多次綁定径缅;2)當(dāng)事件觸發(fā)時,在標準瀏覽器(包括IE9,10)下會默認向函數(shù)名或匿名函數(shù)中撒犀,傳入一個事件對象實參伶丐,在IE8及其以下瀏覽器中,不會默認傳入實參潮峦;
    • DOM2級事件:通過addEventListener()attachEvent()來綁定倔毙;
      • 缺點:在IE瀏覽器下用attachEvent綁定會出現(xiàn)三個問題:1)函數(shù)中this執(zhí)行window,不是元素焚志;2)同一個方法可以重復(fù)綁定上录别,然后執(zhí)行多次;3)執(zhí)行時不按綁定的順序執(zhí)行拘悦,為亂序執(zhí)行齿兔;
    • 封裝event事件庫
      • 目的:解決IE瀏覽器向DOM2級綁定中attachEvent的問題
      • 封裝結(jié)果:同一個元素,同一個行為可以綁定多個方法础米,并且保證函數(shù)執(zhí)行中this指向元素分苇,順序執(zhí)行,并且向函數(shù)中默認傳事件對象實參屁桑,如果函數(shù)中需要使用事件對象医寿,就設(shè)置形參e,如果不需要蘑斧,則不設(shè)置靖秩;
  • bind知識點
    • bind方法:指的是在標準瀏覽器(包括IE9,10)下對函數(shù)的預(yù)處理,可改變函數(shù)的this指向竖瘾,傳實參沟突,當(dāng)與事件配合使用時,默認傳事件對象實參准浴;
      • 缺點:IE8及其以下瀏覽器不支持bind屬性
    • 封裝myBind方法庫
      • 目的:達到與bind一樣的效果事扭,和相同使用方式;
      • 封裝結(jié)果:與bind的使用方式相同乐横,效果一樣求橄;即在所有瀏覽器下兼容今野;改變函數(shù)的this指向,傳入實參罐农,與事件配合使用時条霜,默認傳事件對象實參,事件對象已經(jīng)兼容處理涵亏;
  • 事件賦值的不同情況
    • DOM0級事件:
      • 需求:存在一個函數(shù)名為add的函數(shù)宰睡,當(dāng)點擊事件發(fā)生時,執(zhí)行add函數(shù)并改變其this指向和獲取實參气筋,包括事件對象
      • 兩種方法:
        • 1)給事件添加匿名函數(shù)拆内,匿名函數(shù)中用call來改變this指向,但是要注意的是事件對象的兼容處理宠默,在標準瀏覽器下麸恍,事件觸發(fā),會默認給匿名函數(shù)傳事件對象實參搀矫,但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){//當(dāng)為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級事件:
      • 實質(zhì):使用封裝的event庫,來綁定事件蝴罪,事件觸發(fā)時,會給使函數(shù)中的this指向為該元素步清,默認傳入一個事件對象實參要门;兼容所有瀏覽器;
      • 需求:存在一個函數(shù)名為add的函數(shù)廓啊,當(dāng)點擊事件發(fā)生時欢搜,執(zhí)行add函數(shù)并改變其this指向和獲取實參,包括事件對象
      • 注意:利用myBind綁定時谴轮,直接解綁函數(shù)名炒瘟,無法解綁,需要新建一個變量第步,然后解綁變量疮装,變量就代表一個匿名函數(shù)缘琅;
       <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 利用匿名函數(shù)綁定
           on(oDiv,"click",function (e) {
               add.call(this,2,3,e);
           });
           //2 利用myBind函數(shù)
           var arr=add.myBind(oDiv,2,3);//將地址賦值變量,然后解綁變量廓推;
           on(oDiv,"click",arr);
           off(oDiv,"click",arr);//解綁
       </script>
       </body>
      

2 拖拽實例

2.1 es6版面向?qū)ο蟮募儍舭嫱献嵗?/h4>
  • 知識點:
    • processAge函數(shù)封裝:改變函數(shù)中的this指向刷袍,注意事件對象的傳參
     function processAge(fn,thisArg) {
         return function (e) {
             fn.call(thisArg,e);
         }
     }
    
    • 使用事件庫綁定事件時,綁定的函數(shù)為匿名函數(shù)樊展,不能解綁呻纹,所以需要新建一個變量來賦值匿名函數(shù),然后解綁變量专缠;
     this.MOVE=processAge(this.move,this);
     on(this.ele,"mousemove",this.MOVE);
     off(this.ele,"mousemove",this.MOVE);
    
  • 代碼:
    • 執(zhí)行代碼:
     <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){
             //通過元素位置計算出光標相對于元素內(nèi)的位置 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);//此時為一個匿名函數(shù);
             this.UP=processAge(this.up,this);
             //添加事件
             if(this.ele.setCapture){//IE瀏覽器設(shè)置焦點捕獲
                 this.ele.setCapture();
                 on(this.ele,"mousemove",this.MOVE);
                 on(this.ele,"mouseup",this.UP);
             }else{//標準瀏覽器下雷酪,給document設(shè)置事件,阻止默認事件
                 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;
             }
             //設(shè)置新位置
             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 彈性運動拖拽實例

  • 彈性運動的實現(xiàn)本質(zhì):
    • X軸方向:
      • 在移動事件move函數(shù)中,獲取每次運動的光標位置與上一次的光標位置之間的距離嘁圈,作為運動速度省骂,通過乘以小于1的參數(shù),來不斷的縮凶钭 钞澳;
      • 在鼠標抬起事件up的函數(shù)中,執(zhí)行一個函數(shù)dropX涨缚;
      • 在dropX函數(shù)中轧粟,設(shè)置物體的left值,使其加上移動的速度值脓魏;通過定時器來不斷的更新位置兰吟;直到運動速度值的絕對值小于一定值(一般為0.5)后,定時器不再運行茂翔;
      • 在drop函數(shù)中混蔼,要判斷l(xiāng)eft的邊界值,當(dāng)達到0或maxL時珊燎,設(shè)置運動速度承等-1惭嚣,這樣就會實現(xiàn)反彈的效果;
    • Y軸方向:
      • 在鼠標抬起事件up的函數(shù)中悔政,執(zhí)行一個函數(shù)dropY;
      • 在dropY函數(shù)中晚吞,設(shè)置下落的速度值,給其賦值初始值為9.8谋国,指的是重力加速度值槽地,可以賦值其他值;然后定時器開啟再次執(zhí)行dropY函數(shù)時,給速度值累加9.8捌蚊,然后給其承等小于1的參數(shù)集畅;然后設(shè)置top值
      • 通過邊界值的判斷,來使速度值乘等-1逢勾,達到反彈效果牡整;
      • 當(dāng)物體下落到最底部時,定時器還在運行溺拱,通過設(shè)置一個開關(guān)stemp來控制定時器的運行逃贝;
  • 知識點:
    • 設(shè)置this.speedX*=0.93;this.speedY*=0.98,指的就是讓speed的值不斷的減少迫摔,然后才會達到停下來的效果沐扳,如果給其乘等1,那么物體就不會停下來句占,一直反彈沪摄;
    • 邊界值判斷后,設(shè)置this.speedY*=-1,才能達到反彈的效果纱烘;
    • X軸方向dropX函數(shù)中定時器不再執(zhí)行的條件:判斷speedX的值小于一定值杨拐,然后使其不再執(zhí)行;
    • Y軸方向dropY函數(shù)中定時器不再執(zhí)行的條件:在邊界值判斷中擂啥,利用一個變量stemp來控制哄陶,當(dāng)物體開始下落過程中t的實時值會小于maxT值,當(dāng)t的值大于maxT值后哺壶,stemp賦值為1屋吨,然后會被反彈,t值小于maxT值山宾,stemp又會被賦值為0至扰;直到物體的值持續(xù)大于maxT值后,stemp一直累加资锰,大于2后敢课,定時器不再執(zhí)行;
    • 當(dāng)物體在經(jīng)過反彈后台妆,speedY值開始從負數(shù)累加定值翎猛,然后當(dāng)speedY值大于0時,達到最高點接剩,然后繼續(xù)累加,然后下降萨咳;
  • 注意點:
    • 在設(shè)置定時器后懊缺,每次在執(zhí)行dropX和dropY函數(shù)時,都需要關(guān)閉定時器
    • 在鼠標按下事件down函數(shù)中,要關(guān)閉定時器鹃两,這樣當(dāng)鼠標再次按下的時候遗座,定時器會被關(guān)閉;
    • X軸方向dropX函數(shù)中俊扳,判斷運動速度值時途蒋,要判斷絕對值;
    • 設(shè)置定時器時setTimeout(processAge(this.dropY,this),30),遞歸dropY函數(shù)時馋记,不能直接將this.dropY當(dāng)成參數(shù)傳入号坡,若當(dāng)成參數(shù)傳入,此時執(zhí)行的dropY函數(shù)中的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){
             //通過元素位置計算出光標相對于元素內(nèi)的位置 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);//此時為一個匿名函數(shù);
             this.UP=processAge(this.up,this);
             this.xtimer=this.ytimer=null;
             //添加事件
             if(this.ele.setCapture){//IE瀏覽器設(shè)置焦點捕獲
                 this.ele.setCapture();
                 on(this.ele,"mousemove",this.MOVE);
                 on(this.ele,"mouseup",this.UP);
             }else{//標準瀏覽器下畜隶,給document設(shè)置事件,阻止默認事件
                 on(document,"mousemove",this.MOVE);
                 on(document,"mouseup",this.UP);
                 e.preventDefault();//阻止默認事件号胚;防止選中文字籽慢;
             }
             //點擊按下時,關(guān)閉定時器猫胁;
             clearTimeout(this.xtimer);
             clearTimeout(this.ytimer);
         }
         move(e){
             //設(shè)置新位置
             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";
             //彈性運動的數(shù)據(jù)
             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后箱亿,定時器不再執(zhí)行;
             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開關(guān)來控制定時器的開啟驾茴,物體在底部彈跳時盼樟,stemp取值為0,1之間锈至;持續(xù)大于maxT值后晨缴,會自增;然后大于2峡捡;
             if(t>=this.maxT){
                 t=this.maxT;
                 this.speedY*=-1;
                 this.stemp++;//當(dāng)物體在最下面的時候击碗,持續(xù)大于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 訂閱式發(fā)布體驗

  • 訂閱式發(fā)布的本質(zhì):
    • 需求:一個元素身上創(chuàng)建一個自定義行為,這個行為包括很多方法砚婆,當(dāng)需要執(zhí)行該元素身上的這個自定義行為時械拍,讓所有的跟它有關(guān)的方法都執(zhí)行;
    • 實質(zhì):
      • 給元素ele身上這個自定義行為創(chuàng)建一個自定義屬性,這個屬性的屬性值為一個數(shù)組坷虑,然后將所有與該行為有關(guān)聯(lián)的方法甲馋,插入到這個數(shù)組中,避免重復(fù)插入迄损;
      • 創(chuàng)建一個函數(shù)fire定躏,函數(shù)中就是為了執(zhí)行該行為數(shù)組中的所有方法;需要使用該行為時芹敌,調(diào)用fire函數(shù)痊远;
  • 訂閱式發(fā)布的思路:
    • 創(chuàng)建on函數(shù):給一個元素創(chuàng)建一個自定義行為,然后給這個行為綁定多個不同的方法党窜,然后等待調(diào)用執(zhí)行拗引;
    • 創(chuàng)建fire函數(shù):函數(shù)內(nèi)部是執(zhí)行元素this身上的自定義行為,上綁定的多個方法幌衣;
  • 與事件庫的區(qū)別:
    • 事件庫:是通過給元素在系統(tǒng)事件池中的系統(tǒng)行為綁定一個run函數(shù)矾削,在改事件行為觸發(fā)時,執(zhí)行run函數(shù)豁护,run函數(shù)中同樣也是執(zhí)行該行為的所有方法哼凯;保證執(zhí)行函數(shù)中的this指向和傳入事件對象實參;
    • 訂閱式發(fā)布封裝的on函數(shù)和fire函數(shù)楚里,是給元素創(chuàng)建一個自定義行為断部,然后給這個自定義行為綁定多個不同方法,然后通過fire函數(shù)的調(diào)用班缎,來執(zhí)行這些方法蝴光;保證執(zhí)行函數(shù)中的this指向和傳入事件對象實參;
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>訂閱式發(fā)布體驗</title>
         <style>
             div{
                 width: 200px;
                 height: 200px;
                 background-color: red;
             }
         </style>
     </head>
     <body>
     <div id="div"></div>
     <script>
         var oDiv=document.getElementById("div");
         //封裝一個on函數(shù)达址,給元素的一個行為綁定不同的方法蔑祟;
         //on函數(shù)的目的:給元素身上創(chuàng)建一個自定義屬性,賦值為數(shù)組沉唠,作為元素自己的事件池疆虚,然后將每個方法push進事件池數(shù)組中;避免重復(fù)放入满葛;
         function on(ele,type,fn){
             //思路:元素創(chuàng)建一個自定義屬性径簿,作為自己的事件池,里面插入方法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);
         }
         //發(fā)射器fire函數(shù):目的是監(jiān)聽行為嘀韧,當(dāng)需要執(zhí)行type行為的時候篇亭,讓所有的方法執(zhí)行
         //fire函數(shù)的目的:待執(zhí)行,當(dāng)需要使用該元素身上的自定義行為時锄贷,就調(diào)用執(zhí)行函數(shù)暗赶,將自己事件池上的方法調(diào)用執(zhí)行鄙币,保證方法函數(shù)中的this指向為該元素和傳入實參e;
         //注意:調(diào)用時肃叶,必須保證函數(shù)中的this指向為該元素蹂随;
         function fire(type,e){
             //保證函數(shù)中的this為元素
             var a=this["on"+type];
             if(a.length){
                 for(var i=0; i<a.length; i++){
                     a[i].call(this,e);//保證了函數(shù)在執(zhí)行時,里面的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("相互關(guān)心愛護");
         }
         //需求:給oDiv添加點擊事件岳锁,當(dāng)點擊事件觸發(fā)時,執(zhí)行meihao行為上所有的方法蹦魔;
         oDiv.onclick=function (e) {
             e=e||window.event;
             fire.call(this,"meihao",e);
         }
     </script>
     </body>
     </html>
    

3 訂閱式發(fā)布版與繼承版彈性運動拖拽實例

  • 繼承與訂閱發(fā)布的區(qū)別:
    • 繼承:屬于開發(fā)人員自己進行版本升級激率,不同的功能創(chuàng)建不同的類函數(shù);
    • 訂閱發(fā)布:針對的是用戶勿决,即:給用戶留好升級的接口乒躺,如果用戶想要擴充功能,用戶自己添加低缩;
  • 訂閱發(fā)布函數(shù)EventEmitter
    • 目的:給實例對象this創(chuàng)建一個自定義屬性嘉冒,屬性值為一個數(shù)組,然后將自定義行為上的方法插入到數(shù)組中咆繁;再創(chuàng)建一個函數(shù)fire讳推,用于執(zhí)行數(shù)組中的方法;
    • 注意點:
      • on函數(shù)中的this都指的是實例對象玩般,添加返回值银觅,返回實例this,用于鏈式操作坏为;
      • fire函數(shù)中獲取數(shù)組時究驴,由于on函數(shù)在正常情況下,不會綁定匀伏,所以不會創(chuàng)建數(shù)組洒忧,則拿到的a為undefined,會報錯帘撰,所以為了防止報錯跑慕,必須設(shè)置當(dāng)數(shù)組不存在時,讓其為空數(shù)組摧找;
      • 在執(zhí)行數(shù)組內(nèi)綁定的方法時核行,要保證函數(shù)中的this為實例對象,和傳入事件對象實參蹬耘;
      • on和fire函數(shù)調(diào)用的時候芝雪,都是需要實例對象調(diào)用;
      • fire函數(shù)在每個需要接口的地方調(diào)用综苔,當(dāng)需要添加功能時惩系,就給指定行為用on綁定行為位岔;然后就會執(zhí)行;
      • on函數(shù)在使用時堡牡,用實例對象使用抒抬,并且可以進行鏈式操作;
    • 代碼:
     class EventEmitter{
         constructor(){}
         //on函數(shù)方法晤柄,給實例this創(chuàng)建一個自定義行為擦剑,給這個行為綁定多個方法
         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賦值為空數(shù)組纠屋;
             if(a.length){
                 for(var i=0; i<a.length; i++){
                     a[i].call(this,e);//保證函數(shù)中的this為實例對象;
                 }
             }
         }
     }
    
  • 訂閱式發(fā)布版拖拽實例
    • 本質(zhì):將實例drag繼承EventEmitter的屬性方法盾计;然后在想要擴展功能的地方調(diào)用fire函數(shù)售担,用做接口,當(dāng)需要擴展功能時闯估,用on添加方法灼舍,然后自動添加方法執(zhí)行,不會改變原來的函數(shù)類的代碼功能涨薪;
    • 不會改變原來的函數(shù)類骑素,只需重新建立另一個實例,就可調(diào)動刚夺;
    • 將純凈的拖拽轉(zhuǎn)變?yōu)閺椥赃\動功能的拖拽的思路:
      • 在down,move,up三個函數(shù)中献丑,添加接口this.fire("myMove",e),即給實例對象添加了一個myMove行為,相當(dāng)于發(fā)布上侠姑,如果沒有給myMove用on來綁定方法创橄,就不執(zhí)行,當(dāng)用on綁定方法后莽红,就會立刻執(zhí)行妥畏;可以給同一行為綁定多個方法;
      • 在使用時安吁,創(chuàng)建實例對象醉蚁,然后給實例對象用on給其myMove的行為綁定多個方法,然后到達彈性運動的效果鬼店;在用on綁定時网棍,可以用鏈式操作;
    • 代碼:
      • 執(zhí)行代碼:
       <script>
           var oDiv=document.getElementById("div1");
           var drag=new Drag({
               ele:oDiv
           });
           //想要進行鏈式操作妇智,必須保證on函數(shù)中返回的值為實例對象滥玷;
           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);//注意定時器中的函數(shù)調(diào)用時氏身,函數(shù)中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代碼:
       //訂閱式發(fā)布類函數(shù)
       class EventEmitter{
           constructor(){}
           //on函數(shù)方法惑畴,給實例this創(chuàng)建一個自定義行為蛋欣,給這個行為綁定多個方法
           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賦值為空數(shù)組;
               if(a.length){
                   for(var i=0; i<a.length; i++){
                       a[i].call(this,e);//保證函數(shù)中的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){
               //通過元素位置計算出光標相對于元素內(nèi)的位置 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);//此時為一個匿名函數(shù);
               this.UP=processAge(this.up,this);
               //添加事件
               if(this.ele.setCapture){//IE瀏覽器設(shè)置焦點捕獲
                   this.ele.setCapture();
                   on(this.ele,"mousemove",this.MOVE);
                   on(this.ele,"mouseup",this.UP);
               }else{//標準瀏覽器下霞掺,給document設(shè)置事件,阻止默認事件
                   on(document,"mousemove",this.MOVE);
                   on(document,"mouseup",this.UP);
                   e.preventDefault();//阻止默認事件讹躯;防止選中文字菩彬;
               }
               //關(guān)閉定時器
               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;
               }
               //設(shè)置新位置
               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);
               }
               //執(zhí)行兩個函數(shù)
               this.fire("myUp",e);
           }
       }
      
  • 繼承版拖拽實例
    • 思路:繼承純凈版類函數(shù)Drag,然后在新的類函數(shù)中潮梯,創(chuàng)建函數(shù)骗灶,綁定事件,然后執(zhí)行函數(shù)秉馏;
    • 使用時:兩個類獨立開來耙旦,使用哪個功能,創(chuàng)建哪個類的實例對象萝究;
    • 注意點:給事件行為mousemove綁定方法時免都,需要根據(jù)瀏覽器種類,給不同的元素綁定事件行為的方法帆竹;
    • 代碼:
      • 執(zhí)行代碼:
       <script>
           var oDiv=document.getElementById("div1");
           //1 純凈版拖拽绕娘,創(chuàng)建Drag實例對象;
           var drag=new Drag({
               ele:oDiv
           });
           //2 彈性運動版拖拽栽连,創(chuàng)建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){
               //通過元素位置計算出光標相對于元素內(nèi)的位置 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);//此時為一個匿名函數(shù);
               this.UP=processAge(this.up,this);
               //添加事件
               if(this.ele.setCapture){//IE瀏覽器設(shè)置焦點捕獲
                   this.ele.setCapture();
                   on(this.ele,"mousemove",this.MOVE);
                   on(this.ele,"mouseup",this.UP);
               }else{//標準瀏覽器下险领,給document設(shè)置事件,阻止默認事件
                   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;
               }
               //設(shè)置新位置
               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瀏覽器設(shè)置焦點捕獲
                   on(this.ele,"mousemove",this.MOVE2);
                   on(this.ele,"mouseup",this.UP2);
               }else{//標準瀏覽器下,給document設(shè)置事件噩茄,阻止默認事件
                   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瀏覽器設(shè)置焦點捕獲
                   off(this.ele,"mousemove",this.MOVE2);
                   off(this.ele,"mouseup",this.UP2);
               }else{//標準瀏覽器下下面,給document設(shè)置事件,阻止默認事件
                   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后绩聘,定時器不再執(zhí)行沥割;
               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開關(guān)來控制定時器的開啟帜讲,物體在底部彈跳時,stemp取值為0椒拗,1之間似将;持續(xù)大于maxT值后,會自增蚀苛;然后大于2在验;
               if(t>=this.maxT){
                   t=this.maxT;
                   this.speedY*=-1;
                   this.stemp++;//當(dāng)物體在最下面的時候,持續(xù)大于maxT值后堵未,會自增腋舌;然后大于2;
               }else{
                   this.stemp=0;
               }
               this.ele.style.top=t+"px";
               if(this.stemp<2){
                   this.ytimer=setTimeout(processAge(this.dropY,this),30);
               }
           }
       }
      
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渗蟹,一起剝皮案震驚了整個濱河市块饺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌雌芽,老刑警劉巖授艰,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異世落,居然都是意外死亡淮腾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門岛心,熙熙樓的掌柜王于貴愁眉苦臉地迎上來来破,“玉大人,你說我怎么就攤上這事忘古∨墙” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵髓堪,是天一觀的道長送朱。 經(jīng)常有香客問我,道長干旁,這世上最難降的妖魔是什么驶沼? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮争群,結(jié)果婚禮上回怜,老公的妹妹穿的比我還像新娘。我一直安慰自己换薄,他們只是感情好玉雾,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布翔试。 她就那樣靜靜地躺著,像睡著了一般复旬。 火紅的嫁衣襯著肌膚如雪垦缅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天驹碍,我揣著相機與錄音壁涎,去河邊找鬼。 笑死志秃,一個胖子當(dāng)著我的面吹牛怔球,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播洽损,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼庞溜,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了碑定?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤又官,失蹤者是張志新(化名)和其女友劉穎延刘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體六敬,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡碘赖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了外构。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片普泡。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖审编,靈堂內(nèi)的尸體忽然破棺而出撼班,到底是詐尸還是另有隱情,我是刑警寧澤垒酬,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布砰嘁,位于F島的核電站,受9級特大地震影響勘究,放射性物質(zhì)發(fā)生泄漏矮湘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一口糕、第九天 我趴在偏房一處隱蔽的房頂上張望缅阳。 院中可真熱鬧,春花似錦景描、人聲如沸十办。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽橘洞。三九已至捌袜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間炸枣,已是汗流浹背虏等。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留适肠,地道東北人霍衫。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像侯养,于是被迫代替她去往敵國和親敦跌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

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

  • 1 放大鏡實例 頁面結(jié)構(gòu):創(chuàng)建左右兩個容器逛揩,左邊容器中添加一張圖片背景圖柠傍,右邊容器中添加一張與背景圖成比例放大的圖...
    果木山閱讀 298評論 0 0
  • 1 標準瀏覽器中的DOM2級事件綁定 addEventListener綁定代碼:oDiv.addEventList...
    果木山閱讀 99評論 0 0
  • 事件 1 事件流 冒泡:從該元素開始由里向外觸發(fā)一類事件,事件對象中的事件源為該元素辩稽,觸發(fā)同一類事件惧笛,在子級父級祖...
    果木山閱讀 197評論 0 0
  • ES6之Promise類 1 Promise類基礎(chǔ)知識解讀 promise類的靜態(tài)屬性方法分類:resolve()...
    果木山閱讀 159評論 0 0
  • 1 事件 1.1 事件分類 事件分為DOM0級事件和DOM2級事件DOM0級事件:1)在元素的私有屬性上;2)同一...
    果木山閱讀 140評論 0 0