工作需要写一个移动端的滑动效果,参考了swipe,和iscroll之后自己写了一个xwipe。在写的过程中学习了很多,分享一下心得。
首先html和css部分和swipe一样。
html部分
xwipe结构 <div id=‘xwipe‘ class=‘xwipe‘> <div class=‘xwipe-wrap‘> <div></div> <div></div> <div></div> <div></div> .. </div> </div> tab结构 <ul id="tab" class="tab clearfix"> <li>0</li> <li>1</li> <li>2</li> <li>3</li> ... </ul>
css部分
.xwipe { overflow: hidden; visibility: hidden; } .xwipe-wrap { overflow: hidden; } .xwipe-wrap > div { float:left; position: relative; }
主要说下js部分的心得和坑
1.第一个心得是在写绑定touch事件的时候,开始这么绑定
el.addEventListener("touchmove", function(){}, false);
这样就有一个问题,必须在外面定义一个_this = this把Xwipe对象的this给存到一个变量里,不然监听事件函数里的this就指向了el本身,就不能继续调用Xwipe的方法。
之前遇到定时器或者绑定事件也都是这么解决的。但是这次由于各个方法之间相互调用把this定义变量当参数的方式会让整个逻辑非常乱。查看了swipe和iscroll才发现构造函数的
handleEvent方法,这个可以方法可以在事件触发的时候被调用,这样就统一了this指向的问题。具体实现方法是
el.addEventListener(type, this, false); handleEvent : function(e){ switch (e.type){ case ‘touchstart‘: this.touchStart(e); break; ... }
这样就可以在el被touchstart的时候触发Xwipe的touchStart方法了。但是这里同时就有一个问题了,如果这里有两个dom对象绑定事件的type都是一样的话,这里该怎么处理?
我觉得这种情况出现的不多,毕竟是一个类,dom对象也是基于一个div整体,如果真出现两个type一样的话,可以把这两个事件委托给这个div整体,或者其他有什么好的处理方法?
2.在写touchMove的时候为了阻止浏览器默认的滚动事件,在方法开始就写了
e.preventDefault();来阻止浏览器默认动作,这个在单个测试的时候没有测出什么问题,但是放到具体页面的时候,特别是实例出来多个对象的时候,明显感觉是不科学的,因为这部分无法上下滑动了。
所以在touchMove开始触发的时候加上了先判断是否是上下滑动,如果是,则不preventDefault且不进行xwipe动画。
3.setTimeout(function(){},0)的作用。
先说下Xwipe的滑动逻辑,如果滑到最后一个,再向右滑的话,第一个出现。这里没有添加是否形成这个循环的配置。所以目前Xwipe只能循环滑动。
从当前index跳到goTo指定的to页面的时候,是判断index到to的顺时针和逆时针的距离,优先想近的方向滑,具体代码实现
getDirection : function (to) { return (to - this.options.curIndex)*(Math.abs(to - this.options.curIndex) > this.sLength/2 ? -1 : 1) > 0 ? 1 : -1; //1向右滑动,-1向左滑动 },
比如现状当前页是1,goto页是7,点击tab7的时候是向左滑动到7。说完背景,说到这里遇到的问题。
还是这个例子,当点击的时候判断出7应该出现在左边的时候,先给7的
translate x设置为-width,然后到正确位置之后,在让7的x过渡到0,1的x过渡到width,这样才能形成一个滑动的效果。具体代码简单例子是这样
slide[7].style.webkitTransform = ‘translate3d(‘ + -width+ ‘px, 0, 0)‘; slide[7].translate(0); slide[1].translate(width);
实际运行的时候浏览器好像不按逻辑来的,仿佛直接忽略了第一步而是直接让7从原来的位置滑动到1,如果7原来的位置是width的话,什么鬼这是?
接着各种查,然后发现很多人都遇到过这个问题,比如creat一个input标签之后直接绑定事件不生效,在好好读一下swipe发现一个不太明白的地方
var offloadFn = function(fn) { setTimeout(fn || noop, 0) };
这个正常理解就是立即执行啊,遂谷粉搜搜一下,发现这个还真有大作用,具体可以查看这个http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful
有了这个大杀器之后,这里就好办了,可以这么改一下上面的代码
slide[7].style.webkitTransform = ‘translate3d(‘ + -width+ ‘px, 0, 0)‘; setTimeout(function(){ slide[7].translate(0); slide[1].translate(width); },0)
这样就可以把两个translate滑动方法移动队列最后去执行,这样就能保证7的位置已经移动到了-width位置。
这个Xwipe还是有很多不足之处的,有很多地方写的都不很满意,
比如处理当slide长度为1或者2的时候做的特殊处理,在逻辑的-1和+1位置都加了一个判断slide.length !=1,一共加了6个。处理长度为2的时候是把这两个slide再append后面去变成4个的情况同时把realLength存储起来,这样就可以循环滑动了。
再比如循环滚动这个配置,本来是想加的,后来发现又要改逻辑部分再加上自己主观认为不循环是反人类的体验,所以就默认了循环滑动了。
写完这个感觉自己学了很多,之前太依赖jq了,现在发现写完之后,h5提供了很多api都非常好用,主要操作dom的jq方法,对应的新增api也不错,而且是做移动端,根本就不需要兼容ie。在测试页面里实例化了10个左右,也没有之前的滑动卡顿的情况。
Xwipe整体借鉴了swipe的思路,新增了一个tab导航。提供了4个简单常用方法
prev() 滑动到上一个slide next() 滑动到下一个slide goTo(index) 滑动到指定index索引 option.curIndex 获取当前索引