2

效果

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对移动端的处理是:

  1. touchstart->touchmove->touchend

  2. 如果touchmove的距离不够(过短),触发模拟出来的tap事件.具体的
    半天没松开=>longtab(长按事件)

规定事件内又触发tap事件=>doubletap(双击事件)
其他=>tap

  1. 如果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

下载


TheViper
465 声望16 粉丝