0.o自造插件路上的牛刀小試
前幾天總結了IScroll的一些用法,總覺得IScroll有時候還是太龐大了些,有時候用它僅僅是為了自定義一個滾動條樣式……,於是我決定自己動手,豐衣足食。
這是我封裝的第一個插件,當然是先做一下總結啦!我認為這個插件有主要兩個難點:
一、滑動處理動畫(目前還沒有做出來)
二、自定義時間的處理。
第一條還沒有思路,我就針對第二條談一下自己的心得吧!
自定義事件其實很簡單,本質就是將自己定義事件看作是一個事件對象裏面的一個存放方法的數組,那麽我們在插件中需要觸發我們自定義的事件的時候,去遍歷這個數組裏面的方法並且全部執行就可以了。
我封裝的這個插件中只定義了事件綁定器和觸發器。這裏總結下這兩個東西,所謂觸發器,就是在this.events對象中建立一個自定義事件的數組(如果沒有的話),然後去遍歷裏面的方法,並且依次執行;所謂事件綁定器,就是往事件對象裏的自定義事件數組中push方法,這個方法是實例化代碼的時候從外面傳進來的。
然後,就像上面所說,在程序的核心代碼合適的地方(自定義的方法含義),我們只要去調用事件觸發器並且傳入自定義的名稱就可以了。
對於這個的理解,一篇文章和IScroll源碼對我的啟發很大!給出改片文章的鏈接:https://www.cnblogs.com/xcmylrl/p/5405797.html。
另外一個收獲就是原型和對this的理解了,說白了,插件就是一個類,我們通過構造函數定義這個類具有的屬性,然後通過定義構造函數的原型,去定義它應該有的功能。this困擾本猿很久,現在終於終於明白,this始終屬於自己所處的對象,也最終始終指向自己所處的對象。構造函數本身就是一個未實例化的對象,那麽它的this就指向實例化後的對象(是這樣的吧?哈哈哈哈);在對象中的方法內,那麽這個this也是指向這個對象;在普通方法中,由於普通方法不是對象,但它屬於window,那麽這個this就會指向window(對吧?哈哈哈哈)。所以,this的本質就是指向它本身所屬的對象!哈哈,終於不用再看到this就迷路了!
獻上我的插件源碼和測試代碼:
插件源碼:
/*** * 用於前端列表不能動的插件,可以自定義滾動條 * 著手開發於2017-12-11 * author:一只神秘的猿 */ (function (win,doc,Math) { function AScroll(el,opations) { this.height = 0;//裏面框的高度 this.boxHeight = 0;//容器的高度 this.element = null; this.children = null; this.style = null; this.scrollBox = null;//滾動條框 this.scrollItem = null;//滾動條 this.opations = opations;//參數 this.overHeight = 0;//為顯示的內容高度 this.bottomHeight = 0;//底部未顯示的高度 this.events = {}; this.startY = 0; this.y = 0; if (typeof el === "string") { this.element = doc.querySelector(el); } else { throw "獲取不到正確的dom。"; } if (this.element) { var child = this.element.children[0]; this.children = child; } else { throw "無法獲取列表父級盒子。" } this._init(); this._eventHandle(); } AScroll.prototype = { _init: function () { if (this.children) { this.height = this.children.scrollHeight; this.boxHeight = this.element.offsetHeight; this.overHeight = this.height - this.boxHeight; this.style = this.children.style; } if (this.height > this.element.offsetHeight) { this.scrollBox = doc.createElement("div"); this.scrollItem = doc.createElement("div"); this.scrollBox.appendChild(this.scrollItem); this.element.appendChild(this.scrollBox); //設置滾動條類名 if (this.opations && typeof this.opations.barName === "string") { this.scrollBox.className = "clipScrollBox " + this.opations.barName; } else { this.scrollBox.className = "clipScrollBox"; } this.scrollItem.className = "clipScrollItem"; if (this.scrollBox.className === "clipScrollBox") { this.scrollBox.setAttribute( "style","position:absolute; width: 5px; height:100%; top: 0; right: 0; border: 1px solid #fff; background: rgba(255,255,255,.7); border-radius: 4px; overflow: hidden; z-index: 1000"); this.scrollItem.setAttribute("style","width: 100%; height: " + this.boxHeight * 100 / this.height + "%; background: #999; border-radius: 4px;") } else { this.scrollBox.setAttribute("style","position: absolute; height:100%; top: 0; right: 0; overflow: hidden; z-index: 1000"); this.scrollItem.setAttribute("style","width: 100%; height: " + this.boxHeight * 100 / this.height + "%;") } } }, transform: function () { this.children.style.transform = "translate3d(0," + this.y + "px,0)"; }, changePosition: function () { this.scrollItem.style.transform = "translate3d(0," + Math.abs(this.y) * (this.boxHeight - this.boxHeight * this.boxHeight / this.height) / (this.height - this.boxHeight) + "px,0)"; }, //事件控制器 _eventHandle: function (e) { var self = this; this.element.addEventListener("touchstart",function (e) { e.preventDefault(); e.stopPropagation(); self.startY = e.touches[0].pageY; self.transform(); },false); this.element.addEventListener("touchmove",function (e) { e.preventDefault(); e.stopPropagation(); if (self.y > 0) { self.diffY = e.touches[0].pageY - self.startY; self.startY = e.touches[0].pageY; self.y += self.diffY * .3; } else if (self.y <= self.element.offsetHeight - self.height) { self.diffY = e.touches[0].pageY - self.startY; self.startY = e.touches[0].pageY; self.y += self.diffY * .3; } else { self.diffY = e.touches[0].pageY - self.startY; self.startY = e.touches[0].pageY; self.y += self.diffY; self.changePosition(); } self.bottomHeight = self.overHeight + self.y; self._sendEvent("scrolling"); self.transform(); },false); this.element.addEventListener("touchend",function (e) { e.preventDefault(); e.stopPropagation(); self.endTime = Date.now(); self.endY = e.changedTouches[0].pageY; if (self.y > 0) { self.y = 0; self.changePosition(); self.transform(); } else if (self.y <= self.element.offsetHeight - self.height) { self.y = self.element.offsetHeight - self.height; self.changePosition(); self.transform(); } },false); }, //刷新列表 refresh: function () { if (this.children) { this.height = this.children.scrollHeight; this.boxHeight = this.element.offsetHeight; this.overHeight = this.height - this.boxHeight; this.style = this.children.style; } if (this.scrollBox.className === "clipScrollBox") { this.scrollBox.setAttribute( "style","position:absolute; width: 5px; height:100%; top: 0; right: 0; border: 1px solid #fff; background: rgba(255,255,255,.7); border-radius: 4px; overflow: hidden; z-index: 1000"); this.scrollItem.setAttribute("style","width: 100%; height: " + this.boxHeight * 100 / this.height + "%; background: #999; border-radius: 4px;") } else { this.scrollBox.setAttribute("style","position: absolute; height:100%; top: 0; right: 0; overflow: hidden; z-index: 1000"); this.scrollItem.setAttribute("style","width: 100%; height: " + this.boxHeight * 100 / this.height + "%;") } this.changePosition(); }, //事件綁定,實質就是自定義一個事件名稱,將需要執行的方法存放在這個數組中,在代碼需要的時候遍歷這個事件數組,去執行裏面的方法。 on: function (type,fn) { if (!this.events[type]) { this.events[type] = []; } this.events[type].push(fn); }, //事件觸發器,在代碼合適的地方調用該方法,這個方法會遍歷events中的對應的事件名下的所有方法,並且依次執行。這裏,我們的方法都是實例化改對象時候使用者寫入的方法。 _sendEvent: function (type) { if (!this.events[type]) { this.events[type] = []; } var l = this.events[type].length,i = 0; for ( ; i < l; i++) { this.events[type][i].apply(this,arguments); } }, }; if (typeof module != "undefined" && module.exports) { module.exports = AScroll; } else if ( typeof define == ‘function‘ && define.amd ) { define( function () { return AScroll; } ); } else { window.AScroll = AScroll; } })(window,document,Math);
以下是測試源碼:
<!DOCTYPE html> <html lang="zh_CN"> <head> <title>clip插件測試</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/> <meta name="apple-mobile-web-app-status-bar-style" content="black"/> <meta name="apple-mobile-web-app-title" content=""/> <meta name="apple-touch-fullscreen" content="YES" /> <meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="format-detection" content="telephone=no" /> <meta name="HandheldFriendly" content="true" /> <meta http-equiv="x-rim-auto-match" content="none" /> <meta name="format-detection" content="telephone=no" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <style> #myBox { width: 90%; height: 90%; position: absolute; left: 0; right: 0; top: 0; bottom: 0; margin: auto; overflow: hidden; } #myBox p { margin: 5px auto; line-height: 60px; text-align: center; background: #ddd; } </style> </head> <body> <div id="myBox"> <div> <p>1</p> <p>2</p> <p>3</p> <p>4</p> <p>5</p> <p>6</p> <p>7</p> <p>8</p> <p>9</p> <p>10</p> <p>11</p> <p>12</p> <p>13</p> <p>14</p> <p>15</p> <p>16</p> <p>17</p> <p>18</p> <p>19</p> <p>20</p> </div> </div> <script type="text/javascript" src="clip.js"></script> <script type="text/javascript"> var box = document.querySelector("#myBox>div"); var loaded = false; //模擬ajax添加條目 function createNewItem() { var i = 0, l = 10; for ( ; i < l; i++) { var myDom = document.createElement("p"); myDom.innerText = "我是添加的條目" + (i + 1); box.appendChild(myDom); } // console.log("created!"); }; var myTest = new AScroll("#myBox"); myTest.on("scrolling",function () { if (this.bottomHeight < 100 && !loaded) { loaded = true; createNewItem(); this.refresh(); } }); </script> </body> </html>
用法說明:
支持分段加載(refresh)、自定義滾動條(opations.barName = "string");
0.o自造插件路上的牛刀小試