开发该插件的初衷是,在作一个项目时发现如今实现移动端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)