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