chrome模拟器
真机
http://v.youku.com/v_show/id_XMTM2MjExNTM5Ng==.htmljavascript
<div id='tab'> <ul> <li>TAB 1</li> ... </ul> <div id='cursor'></div> </div>
@page_num:3;//页面数 @page_width:360px;//每一页的宽度 #tab{ position: relative; ul{ display: flex; align-items: flex-start; flex-flow: row wrap; justify-content: space-around; } li{ display: block; width:(100-8*@page_num)/@page_num*1%; text-align: center; color: #333; padding:10px 4%; font-size: 1.5em; } .cur{ color:#bc232c; } #cursor{ bottom:0; width:1/@page_num*100%; position: absolute; border-bottom:5px solid #bc232c; color: #bc232c; } }
切换卡用flex-box,每一个切换卡的宽度用百分比,(100-页面数*2*切换卡padding)/页面数*100%
.css
游标#cursor
用absolute,相对于整个tab定位,left也用百分比表示,后面滑动时动态改变left.其宽度=(100/页面数)%
.html
<div id='content'> <ul style="transform: translate(0px, 0px);"> <li>page 1</li> ... </ul> </div>
#content{ width: @page_width*@page_num; li{ font-size: 30px; vertical-align: top; width: 1/@page_num*100%; display: inline-block; } }
#content ul
相对于'遮罩',经过改变它的transform
调整后面内容的可见部分。java
<div id='wrap' ms-controller="slide_switch"> <div id='tab'> <ul> <li ms-class-cur='cur==$index' ms-repeat='heights'>TAB {{$index+1}}</li> </ul> <div id='cursor' ms-css-left='{{cursor_pos}}%'></div> </div> <div id='content'> <ul ms-css-transform='translate({{-offset}}px, 0)'> <li ms-attr-id='page{{$index+1}}' ms-repeat-height="heights" ms-css-height="height" ms-on-touchstart='start($event)' ms-on-touchmove='move($event)' ms-on-touchend='end($event)'> page {{$index+1}} </li> </ul> </div> </div>
var start,offset,page_width=320,page_num=3,cursor_step=1/page_num*100; var slide_switch=avalon.define({ $id:'slide_switch', cur:0,//当前页 heights:[], offset:0,//页面偏移 cursor_pos:0,//tab游标偏移 ... });
须要对每一页设定高度:当前页的高度是它本身自己的高度,其它页的高度不能大于当前页的高度,防止滚动条与当前页不对应。
好比当前页是第一页(最左边),高度为[500,700,800],即高度都是它们自己的高度。git
这时,滚动条是和最高的页(第三页)对应的。
事实上,天猫的h5商品详情页面就是这样作的。
这里由于切换时没有异步加载数据,本屌就没在切换后从新设定高度。github
触摸事件在这里的做用是演示滑动可能产生的效果,最终决定哪一页是当前页的是滑动事件。chrome
<li ms-attr-id='page{{$index+1}}' ms-repeat-height="heights" ms-css-height="height" ms-on-touchstart='start($event)'>page {{$index+1}}</li>
... start:function(e){ start=e.touches[0].clientX; } ...
start保存触摸的初始点。segmentfault
<li ms-attr-id='page{{$index+1}}' ms-repeat-height="heights" ms-css-height="height" ms-on-touchstart='start($event)' ms-on-touchmove='move($event)'>page {{$index+1}}</li>
... move:function(e){ offset=e.touches[0].clientX-start;//当前触摸的位置到初始点的位移 slide_switch.offset=page_width*slide_switch.cur-offset;//更新页面可见区域 //更新tab游标位置 slide_switch.cursor_pos=cursor_step*slide_switch.cur-offset/(page_width*page_num)*100; } ...
<li ms-attr-id='page{{$index+1}}' ms-repeat-height="heights" ms-css-height="height" ms-on-touchstart='start($event)' ms-on-touchmove='move($event)' ms-on-touchend='end($event)'> page {{$index+1}}</li>
... end:function(e){ //页面当前状态是第一页的左边或最后一页的右边或左右相邻页未露出一半 if(slide_switch.offset<0||slide_switch.offset>page_width*(page_num-1)|| Math.abs(offset)<page_width/2){ slide_switch.offset=page_width*slide_switch.cur;//回到触摸事件前的状态 slide_switch.cursor_pos=cursor_step*slide_switch.cur; e.stopPropagation();//阻止滑动事件 } } ...
关于移动端的click事件,参见也来讲说touch事件与点击穿透问题.avalon.mobile
对移动端的处理是:less
touchstart->touchmove->touchenddom
若是touchmove的距离不够(太短),触发模拟出来的tap事件.具体的
半天没松开=>longtab(长按事件)
规定事件内又触发tap事件=>doubletap(双击事件)
其余=>tap
若是touchmove达到必定距离,触发swipe(滑动)事件。
在这里触摸事件移动的距离达到必定值时(前面touchend事件回调已通过滤了不符合要求的事件),就会触发滑动事件。总的执行顺序:touchstart->touchmove->touchend->swipe.
<li ms-attr-id='page{{$index+1}}' ms-repeat-height="heights" ms-css-height="height" ms-on-swipeleft='turn(cur-1)' ms-on-swiperight='turn(cur+1)' ms-on-touchstart='start($event)' ms-on-touchmove='move($event)' ms-on-touchend='end($event)'>page {{$index+1}}</li>
... turn:function(cur){ slide_switch.cur=cur; slide_switch.offset=page_width*slide_switch.cur; slide_switch.cursor_pos=cursor_step*slide_switch.cur; } ...
... <ul> <li ms-click='turn($index)' ms-class-cur='cur==$index' ms-repeat='heights'> TAB {{$index+1}}</li> </ul> ...
less
@page_num:3; @page_width:320px;
js
page_width=320,page_num=3