效果
chrome模拟器
真机
http://v.youku.com/v_show/id_XMTM2MjExNTM5Ng==.html
布局
tab部分
<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%
.游标
#cursor
用absolute,相对于整个tab定位,left也用百分比表示,后面滑动时动态改变left.其宽度=(100/页面数)%
.
内容部分
<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
调整后面内容的可见部分。
触摸事件
<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],即高度都是它们本身的高度。
这时,滚动条是和最高的页(第三页)对应的。
事实上,天猫的h5商品详情页面就是这样做的。
这里因为切换时没有异步加载数据,本屌就没在切换后重新设定高度。
触摸事件在这里的作用是演示滑动可能产生的效果,最终决定哪一页是当前页的是滑动事件。
touchstart
<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保存触摸的初始点。
touchmove
<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;
}
...
touchend
<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
对移动端的处理是:
touchstart->touchmove->touchend
如果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;
}
...
tab点击
...
<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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。