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)兼容處理涵亏;
- bind方法:指的是在標準瀏覽器(包括IE9,10)下對函數(shù)的預(yù)處理,可改變函數(shù)的this指向竖瘾,傳實參沟突,當(dā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>
- DOM0級事件:
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);
}
}
}
- 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);
}
}
}
- 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);
}
}
}
- 需求:一個元素身上創(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ù)痊远;
- 創(chuàng)建on函數(shù):給一個元素創(chuàng)建一個自定義行為,然后給這個行為綁定多個不同的方法党窜,然后等待調(diào)用執(zhí)行拗引;
- 創(chuàng)建fire函數(shù):函數(shù)內(nèi)部是執(zhí)行元素this身上的自定義行為,上綁定的多個方法幌衣;
- 事件庫:是通過給元素在系統(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>
- 繼承:屬于開發(fā)人員自己進行版本升級激率,不同的功能創(chuàng)建不同的類函數(shù);
- 訂閱發(fā)布:針對的是用戶勿决,即:給用戶留好升級的接口乒躺,如果用戶想要擴充功能,用戶自己添加低缩;
- 目的:給實例對象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為實例對象;
}
}
}
}
- 本質(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綁定時网棍,可以用鏈式操作;
- 在down,move,up三個函數(shù)中献丑,添加接口
- 代碼:
- 執(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); } } }