開發該插件的初衷是,在作一個項目時發現如今實現移動端tab滑動的插件大多基於swiper,swiper的功能太強大而我只要一個小小的tab滑動功能,就要引入200+k的js這未免太過浪費。並且swiper是沒有下拉刷新功能的,要用swiper實現下拉刷新還得改造一番。在實現功能的同時產生了很多bug。要是在引入一個下拉刷新的插件又不免多了幾十kb的js。並且這些插件對dom結構又是有必定要求的,一不當心就有bug。修復bug的時間均可以在擼一個插件出來了。javascript
此次開發的這個插件只依賴手勢庫touch.js。使用原生實現功能。大小隻有6kb。兼容性也算不錯。html
其實對touch.js的依賴並不嚴重,只是用了其兩個手勢事件,花點時間徹底能夠本身實現的。java
<font color="red">插件我只是粗略的測試了一番,如有什麼bug請你們提出。有寫的不清楚的也請提出。以爲不錯的能夠給我一個星星</font>ios
- 該插件基於百度手勢庫touch.js該改手勢庫的大小也只有13k不到。官方文檔連接是找不到了因此引用別人寫的吧:<a href="http://blog.csdn.net/wangjiaohome/article/details/49364177" target="_blank">API文檔</a>
主要體如今微信和ios瀏覽器對下拉時會有彈簧效果:git
這個是瀏覽器的默認效果,是能夠經過「e.preventDefault()」取消默認效果的。不過這就會產生容器不能滾動了。 因此就不能直接e.preventDefault()取消默認效果了。只能在特定的條件下才能取消默認事件。那條件是什麼呢? 第一個條件就是滑動方向是向下&&是在容器頂部時候 第二個條件就是滑動方向向下&&在容器底部 在這裏touch.js能夠輕易的獲取滑動的方向,滾動條所在的位置也很容易算出。我已開始也覺得很簡單的,結果卻發現touch.js獲取滾動方向是有必定延時的,這就形成第一時間捕獲的位置是上一次的,因此出現偶爾能夠偶爾不可,有時乾脆滾動不了。因此使用touch.js獲取方向的方式是不可取的。 只能本身採集觸摸屏幕時的座標,在對比滑動時的座標取得方向。ok這個bug就這樣輕鬆解決了。這都是在微信上運行的結構,後來拉到uc的時候居然發現uc連左右滑動都有默認效果(喪盡天良)。 這就只能用老辦法解決了,增長兩組條件,左右滑動。根據採集的初始點,對比滑動過程的座標,判斷上下滾動仍是左右滑動。在取消默認效果。es6
dom結構:github
<div id="box"> <!-- 主容器 --> <div class="pullDownHtml"> <!-- 下拉刷新的顯示內容 --> <div class="pullDownshow1">下拉刷新</div> <div class="pullDownshow2">正在刷新</div> </div> <div class="pullUpHtml"> <!-- 上拉加載的顯示內容 --> <div class="pullUpHtmlshow1">上拉加載</div> <div class="pullUpHtmlshow2">正在加載</div> </div> <div class="box"> <div class="tab-container"> <div class="s-pull"> // 頁面一內容 </div> </div> <div class="tab-container"> <div class="s-pull"> // 頁面二內容 </div> </div> <div class="tab-container"> <div class="s-pull"> // 頁面三內容 </div> </div> </div> </div>
一、初始化瀏覽器
var swiper = new TabSwiper(ele, options) // ele:容器 // options: 參數(Object)
二、options參數微信
{ speed: 300, // 動畫速度 threshold: 100, // 上下拉觸發的閥值(px) xThreshold: 0.3, // 左右滑動觸發的閥值(0~1)默認爲:‘0.25’ closeInertia: false, // 是否關閉慣性滑動, 默認開啓 isPullDown: true, // 是否開啓下拉刷新 isPullUp: true, // 是否開啓上拉加載 defaultPage: 0, // 默認顯示的頁數 initCb: function(){}, // 初始化回調 onEnd: function(page){}, // 切換頁數時回調(返回當前頁數) onRefreshStart: function(page){}, // 觸發下拉刷新時回調(返回當前頁數) onLoadStart: function(page){}, // 觸發上拉加載時回調(返回當前頁數) onTouchmove: function(page, e){} // 正在頁面上滑動回調(返回當前頁數和滑動信息。可經過滑動的信息獲得當前滑動的方向速度滑動的距離,進行功能擴展) }
三、pullEnd(cb)方法:dom
swiper.pullEnd(function (page) { // 返回當前頁數 console.log(page) })
四、changePage(page)方法:
swiper.changePage(page) // 切換頁面page目標頁面從0開始
五、nowIndex屬性:
var nowIndex = swiper.nowIndex // 獲取當前所在頁數(只讀)
若要查看es5的版本請移步(<a href="https://github.com/xiaosu95/tab-swiper" target="_blank">查看代碼</a>)
;(function (window, document) { // 更改transform function changeTransform (ele, left, top) { ele.style.transform = `translate(${left}px, ${top}px)` ele.style.WebkitTransform = `translate(${left}px, ${top}px)` } class TabSwiper { get nowIndex () { return this._nowIndex } set nowIndex (val) { if (val === this._nowIndex) return this._nowIndex = val this.options.onEnd && this.options.onEnd(val) } constructor (ele, options) { this._nowIndex = 0 this.ele = ele this.width = ele.clientWidth // 容器寬度 this.height = ele.clientHeight // 容器高度 this.totalWidth = 0 // 總寬度 this.box = ele.querySelector('.box') this.containers = ele.querySelectorAll('.tab-container') // 容器 this.direction = '' this.scrollTop = 0 this.options = options // 配置參數 this.prohibitPull = false // 禁止上下拉動操做標記 this.startY = 0 // 起始y座標 this.startX = 0 // 起始x座標 this.isBottom = false // 是否在底部 this.disX = 0 // 滑動X差值 this.disY = 0 // 滑動Y差值 this.pullDownHtml = ele.querySelector('.pullDownHtml') this.pullUpHtml = ele.querySelector('.pullUpHtml') this.pullDownHtmlHeight = 0 // 下拉的html高度 this.pullUpHtmlHeight = 0 // 上拉的html高度 this.left = 0 // 向左偏移量 // 初始化 this.init() } // 初始化 init () { this.options.xThreshold = this.options.xThreshold || 0.25 // 設置樣式 this.ele.style.overflow = 'hidden' this.ele.style.position = 'relative' this.box.style.height = '100%' this.box.style.width = this.containers.length * 100 + 'vw' this.box.style.float = 'left' this.box.style.transition = 'all ' + this.options.speed / 1000 + 's' this.box.style.position = 'relative' this.box.style.zIndex = 2 this.totalWidth = this.width * this.containers.length;; [].forEach.call(this.containers, (ele) => { ele.style.float = 'left' ele.style.width = '100vw' ele.style.height = '100%' ele.style.overflow = 'auto' ele.style.WebkitOverflowScrolling = 'touch' ele.addEventListener('touchstart', (e) => { this.startY = e.touches[0].clientY // 設置起始y座標 this.startX = e.touches[0].clientX // 設置起始y座標 }, false) ele.addEventListener('touchmove', (e) => { this.scrollTop = this.containers[this.nowIndex].scrollTop this.isBottom = this.containers[this.nowIndex].querySelector('.s-pull').clientHeight <= this.scrollTop + this.height // 判斷滑動方向是否爲上下 const disY = e.touches[0].clientY - this.startY const disX = e.touches[0].clientX - this.startX // 設置事件(當爲頂部或底部是取消默認事件) if ((disY > 0 && ele.scrollTop == 0) || (disY < 0 && this.isBottom)) { e.preventDefault() } // 若爲左右滑動時取消默認事件 if (Math.abs(disY) < Math.abs(disX)) e.preventDefault() }, false) }) // 上下拉 if (this.options.isPullDown) { this.pullDownHtml.style.position = 'absolute' this.pullDownHtml.style.width = '100%' this.pullDownHtmlHeight = this.pullDownHtml.clientHeight } if (this.options.isPullUp) { this.pullUpHtml.style.position = 'absolute' this.pullUpHtml.style.width = '100%' this.pullUpHtml.style.bottom = '0' this.pullUpHtmlHeight = this.pullUpHtml.clientHeight } // 添加事件 // 拖拽 touch.on(this.box, 'drag', (e) => { this.direction = e.direction this.touchmove(e) this.options.onTouchmove && this.options.onTouchmove(this.nowIndex, e) // 事件輸出 }) // 滑動 !this.options.closeInertia && touch.on(this.box, 'swipe', (e) => { this.swipe(e) }) // 手指離開屏幕 touch.on(this.box, 'touchend', (e) => { this.touchend(e) }) // 移動至默認頁面 this.changePage(this.options.defaultPage || 0) this.options.initCb && this.options.initCb() } // 拖拽方法 touchmove (e) { this.box.style.transition = 'none' // 取消動畫 if ((e.direction === 'left' || e.direction === 'right') && !this.disY) { // 左右滑動 this.disX = e.distanceX changeTransform(this.box, (this.left + this.disX), this.disY) } else if (!this.disX && !this.prohibitPull) { // 上下滑動 if (e.direction === 'down' && !this.options.isPullDown) return if (e.direction === 'up' && !this.options.isPullUp) return if ((this.scrollTop <= 0 && this.direction === 'down') || (this.isBottom && this.direction === 'up')) { // 上下拉動容器 this.disY = e.distanceY changeTransform(this.box, (this.left + this.disX), this.disY) } } } // 手指離開屏幕 touchend (e) { this.box.style.transition = 'all ' + this.options.speed / 1000 + 's' // 開啓動畫 if (!this.prohibitPull) { if (Math.abs(this.disY) < this.options.threshold) { // 上下拉小於閥值自動復原 this.disY = 0 changeTransform(this.box, (this.left + this.disX), this.disY) } // 下拉刷新觸發 if (this.scrollTop <= 0 && this.direction === 'down' && this.disY >= this.options.threshold) { this.disY = this.pullDownHtmlHeight this.prohibitPull = true // 顯示加載中 this.pullDownHtml.style.visibility = 'visible' this.options.onRefreshStart && this.options.onRefreshStart(this.nowIndex) // 輸出下拉刷新事件 } // 上拉加載觸發 else if (this.isBottom && this.direction === 'up' && Math.abs(this.disY) > this.options.threshold) { this.disY = -this.pullUpHtmlHeight this.prohibitPull = true // 顯示加載中 this.pullUpHtml.style.visibility = 'visible' this.options.onLoadStart && this.options.onLoadStart(this.nowIndex) // 輸出上拉事件 } } // 左右滑動 if (Math.abs(this.disX) < this.width * this.options.xThreshold) { changeTransform(this.box, this.left, this.disY) this.disX = 0 } else { this.left += this.disX / Math.abs(this.disX) * this.width if (this.left > 0) this.left = 0 if (this.left <= -this.totalWidth) this.left = -(this.totalWidth - this.width) changeTransform(this.box, this.left, this.disY) } this.direction = '' // 重置方向 this.nowIndex = Math.abs(this.left) / this.width // 計算頁數 } // 快速滑動 swipe (e) { if (e.factor < 1 && !this.disX && !this.disY) { if (e.direction === 'left') { this.left -= this.width } else if (e.direction === 'right') { this.left += this.width } if (this.left > 0) this.left = 0 if (this.left <= -this.totalWidth) this.left = -(this.totalWidth - this.width) changeTransform(this.box, this.left, this.disY) } this.disX = 0 this.nowIndex = Math.abs(this.left) / this.width // 計算頁數 } // 關閉上下拉 pullEnd (cb) { cb && cb(this.nowIndex) changeTransform(this.box, this.left, 0) this.disY = 0 this.prohibitPull = false } // 切換頁數 changePage (page) { if (this.prohibitPull) return this.left = -page * this.width changeTransform(this.box, this.left, this.disY) this.nowIndex = page } } window.TabSwiper = TabSwiper })(window, document)