移动web:tab选项卡

  平常做移动端会用到tab选项卡,这和PC端有些区别,移动端是触摸滑动切换,PC端是点击、移入切换。

  这里滑动切换就是一个移动端事件的应用,这里主要用到的触摸事件:touchstart、touchmove、touchend。

  和做其他的效果一样,先有html结构,css样式修饰,再写JS代码。

  html:

复制代码
<div class="mtabs" id="tabs">
    <ul class="mhead">
        <li>tab1</li>
        <li>tab2</li>
        <li>tab3</li>
    </ul>
    <div class="mcontent">
        <ul>
            <li>tab1内容内容内容内容</li>
            <li>tab1内容内容内容内容</li>
            <li>tab1内容内容内容内容</li>
            <li>tab1内容内容内容内容</li>
            <li>tab1内容内容内容内容</li>
        </ul>
        <ul>
            <li>tab2内容内容内容内容</li>
            <li>tab2内容内容内容内容</li>
            <li>tab2内容内容内容内容</li>
            <li>tab2内容内容内容内容</li>
            <li>tab2内容内容内容内容</li>
        </ul>
        <ul>
            <li>tab3内容内容内容内容</li>
            <li>tab3内容内容内容内容</li>
            <li>tab3内容内容内容内容</li>
            <li>tab3内容内容内容内容</li>
            <li>tab3内容内容内容内容</li>
        </ul>
    </div>
</div><!-- End .mtabs -->
View Code
复制代码

  css:

复制代码
body,div,ul,li{
    margin:0;
    padding:0;
}
ul,li {
    list-style:none;
}
body {
    font-size:100%;
    font-family:Helvetica,STHeiti,Droid Sans Fallback;
}
.mtabs {
    width:100%;
    overflow:hidden;
}
.mhead {
    height:38px;
    border-top:2px solid #9ac7ed;
    background:#ECF2F6;
    -webkit-tap-highlight-color:rgba(0,0,0,0);
}
.mhead li {
    position:relative;
    font-size:1.125em;
    text-align:center;
    float:left;
    width:64px;
    height:38px;
    line-height:38px;
    color:#2a70be;
}
.mhead li.current {
    border-top:2px solid #2a70be;
    margin-top:-2px;
    background:#FFF;
    color:#c14545;
}
.mcontent {
    width:100%;
    overflow:hidden;
}
.mcontent ul {
    width:100%;
    float:left;
}
.mcontent li {
    height:35px;
    line-height:35px;
    font-size:1.125em;
    padding:0 10px;
}
View Code
复制代码

  下面的截图是想要的一个效果预览:

  

   下面是实际效果,可以在Chrome的移动模式查看:

  • tab1
  • tab2
  • tab3
  • tab1内容内容内容内容
  • tab1内容内容内容内容
  • tab1内容内容内容内容
  • tab1内容内容内容内容
  • tab1内容内容内容内容
  • tab2内容内容内容内容
  • tab2内容内容内容内容
  • tab2内容内容内容内容
  • tab2内容内容内容内容
  • tab2内容内容内容内容
  • tab3内容内容内容内容
  • tab3内容内容内容内容
  • tab3内容内容内容内容
  • tab3内容内容内容内容
  • tab3内容内容内容内容

   先贴上JS代码,供参考

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/**
 * LBS mTabs
 * Date: 2014-5-10
 * ===================================================
 * opts.mtab    tabs外围容器/滑动事件对象(一个CSS选择器)
 * opts.mhead   tabs标题容器/点击对象(一个CSS选择器)
 * opts.mcontent    tabs内容容器/滑动切换对象(一个CSS选择器)
 * opts.index   tabs索引(默认0) 指定显示哪个索引的标题、内容
 * opts.current  tabs当前项的类名(默认current)
 * ===================================================
**/
;(function(){
window.mTabs = function(opts){
    if(typeof opts === undefined) return;
     //取得tabs外围容器、标题容器、内容容器
    this.mtab = document.querySelector(opts.mtab);
    this.mhead = document.querySelector(opts.mhead);
    this.mcontent = document.querySelector(opts.mcontent);
     //取得标题容器内选项集合、内容容器内容集合
    this.mheads = this.mhead.children;
    this.mcontents = this.mcontent.children;
     
    this.length = this.mheads.length;
    if(this.length < 1) return;
    if(opts.index > this.length-1) opts.index = this.length-1;
    this.index = this.oIndex = opts.index || 0;
    this.current = opts.current || 'current'; //当前活动选项类名
    this.touch = {};//自定义一个对象 用来保存手指触摸相关信息
 
    this.init();
}
mTabs.prototype = {
    init: function(opts){
        this.set();
        this.initset();
        this.bind();
    },
    initset: function(){
        for(var i = 0; i < this.length; i++){
            this.mheads[i].index = i;//设置了一个属性 方便点击时判断是点了哪一项
            this.mheads[i].className = this.mheads[i].className.replace(this.current,'');
            this.mcontents[i].className = this.mcontents[i].className.replace(this.current,'');
        }//初始化设置、先清空手动加在标题或内容HTML标签的当前类名(this.current)、再设置哪一项为当前选项并设置类名
        this.mheads[this.index].className += ' '+this.current;
        this.mcontents[this.index].className += ' '+this.current;<br>          //对应的内容要显示在可视区域
        //this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
        //this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
    },
    set: function(){//获取浏览器的视口宽度、并设置内容容器的宽度、每一项内容区域的宽度,屏幕旋转,浏览器窗口变换会再次设置这些值
        this.width =  document.documentElement.clientWidth || document.body.clientWidth;
        this.mcontent.style.width = this.length * this.width + 'px';       
        for(var i = 0; i < this.length; i++) this.mcontents[i].style.width = this.width + 'px';//调整在可视区域显示的内容项
        //this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
        this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
    },
    bind: function(){<br>         //绑定各种事件
        var _this = this;
        this.mtab.addEventListener("touchstart",function(e){
            _this.touchStart(e);
        }, false);
        this.mtab.addEventListener("touchmove",function(e){
            _this.touchMove(e);
        }, false);
        this.mtab.addEventListener("touchend",function(e){
            _this.touchEnd(e);
        }, false);
        this.mtab.addEventListener("touchcancel",function(e){
            _this.touchEnd(e);
        }, false);
        this.mhead.addEventListener("click",function(e){
            _this.touchClick(e);
        }, false);
        this.mcontent.addEventListener('webkitTransitionEnd',function(){
            _this.transitionEnd();
        }, false);
        window.addEventListener("resize", function(){
            setTimeout(function(){
                _this.set();
            },100);
        }, false);
        window.addEventListener("orientationchange",function(){
            setTimeout(function(){
                _this.set();
            },100);
        }, false);
    },
    touchStart: function(e){
        this.touch.x = e.touches[0].pageX;
        this.touch.y = e.touches[0].pageY;
        this.touch.time = Date.now();
        this.touch.disX = 0;
        this.touch.disY = 0;
        this.touch.fixed = ''; //重要 这里采用了判断是滚动页面行为、还是切换选项行为 如果是滚动页面就在滑动时只滚动页面 相应的切换选项就切换不会滚动页面
    },
    touchMove: function(e){
        if(this.touch.fixed === 'up') return;
        e.stopPropagation();
        if(e.touches.length > 1 || e.scale && e.scale !== 1) return;
        this.touch.disX = e.touches[0].pageX - this.touch.x;
        this.touch.disY = e.touches[0].pageY - this.touch.y;
        if(this.touch.fixed === ''){//行为判断 采用这种方式 主要解决手指按下移动(左上、右上)时滑动切换和滚动页面同时执行的问题 
            if( Math.abs(this.touch.disY) > Math.abs(this.touch.disX) ){
                this.touch.fixed = 'up';
            }else{
                this.touch.fixed = 'left';
            }
        }
        if(this.touch.fixed === 'left'){
            e.preventDefault();
            if( (this.index === 0 && this.touch.disX > 0) || (this.index === this.length-1 && this.touch.disX < 0) ) this.touch.disX /= 4; //在 第一项向右滑动、最后一项向左滑动 时
            //this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + ( this.touch.disX - this.index * this.width ) + "px)";
            this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + ( this.touch.disX - this.index * this.width ) + "px,0,0)";
        }
    },
    touchEnd: function(e){ 
        if(this.touch.fixed === 'left'){
            var _this = this, X = Math.abs(this.touch.disX);
            this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 100ms';
            if( (Date.now() - this.touch.time > 100 && X > 10) || X > this.width/2 ){
                this.touch.time = Date.now();
                this.touch.disX > 0 ? this.index-- : this.index++;
                this.index < 0 && (this.index = 0);
                this.index > this.length - 1 && (this.index = this.length - 1);
                if(this.index === this.oIndex) this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 300ms';
                if(this.index !== this.oIndex) this.replace();
            }
            //this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
            this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
        }  
    },
    transitionEnd: function(){
        this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 0ms';
    },
    touchClick: function(e){
        var target = e.target;
        if(target.nodeType === 1 && target.index !== undefined){
            if(target.index === this.index) return;
            e.preventDefault();
            e.stopPropagation();
            this.index = target.index;
            this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 100ms';
            //this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
            this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
            this.replace();
        }
    },
    replace: function(){
        this.mheads[this.index].className += ' '+this.current;
        this.mheads[this.oIndex].className = this.mheads[this.oIndex].className.replace(this.current,'').trim();
        this.mcontents[this.index].className += ' '+this.current;
        this.mcontents[this.oIndex].className = this.mcontents[this.oIndex].className.replace(this.current,'').trim();
        this.oIndex = this.index;
    }
}
}());

  使用方式很简单,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
document.addEventListener('DOMContentLoaded',function(){
    //use mTabs
    new mTabs({
        mtab: '#tabs',
        mhead: '#tabs .mhead',
        mcontent: '#tabs .mcontent'
    });
 
    /*new mTabs({
        mtab: '#tabs',
        mhead: '#tabs .mhead',
        mcontent: '#tabs .mcontent',
        index: 1,
        current: 'active'
    });*/
 
},false);
复制代码
mtab: 
<div class="mtabs" id="tabs">
    //..    
</div>
mhead:
<ul class="mhead">
    //..
</ul>
mcontent:
<div class="mcontent">
    //..
</div> 
复制代码

 

  在此说下思路:

    先获得一个tabs容器对象(mtab),它包含了两个对应的类集合容器,一个是标签栏(mhead)、一个是内容栏(mcontent),再分别取得类集合容器里面对应的选项mheads、mcontents。

1
2
3
4
5
6
this.mtab = document.querySelector(opts.mtab);
this.mhead = document.querySelector(opts.mhead);
this.mcontent = document.querySelector(opts.mcontent);
 
this.mheads = this.mhead.children;
this.mcontents = this.mcontent.children;

    获取设备浏览器窗口的宽,并更新内容容器(mcontent)的宽,内容项的宽,一般在页面都会有文档声明 <!DOCTYPE html> document.documentElement.clientWidth 就能获取浏览器窗口的宽。

1
2
3
this.width =  document.documentElement.clientWidth || document.body.clientWidth;
this.mcontent.style.width = this.length * this.width + 'px';       
for(var i = 0; i < this.length; i++) this.mcontents[i].style.width = this.width + 'px';

    在手指触摸按上时(在tabs容器对象上), 获取手指按下时在页面的位置 ( e.touches[0].pageX)。 touchs想象成有几根手指,只需要第一根按下的手指( touches[0] )。初始化了一个行为判断 this.touch.fixed (当在tabs上滑动时是要滚动页面还是要切换选项卡)。

1
2
3
this.touch.x = e.touches[0].pageX;
//..
this.touch.fixed = '';

  在移动手指时,做出行为的判断。先获得移动的距离(左右方向、上下方向),根据两个方向的值比较判断是哪种行为。

1
2
3
4
5
6
7
8
9
10
this.touch.disX = e.touches[0].pageX - this.touch.x;
this.touch.disY = e.touches[0].pageY - this.touch.y;
//..
if(this.touch.fixed === ''){
    if( Math.abs(this.touch.disY) > Math.abs(this.touch.disX) ){
        this.touch.fixed = 'up';
    }else{
        this.touch.fixed = 'left';
    }
}

   在移动手指时,内容容器(mcontent)也会跟着移动,并且做了在处于第一项和最后一项时的移动限制。

1
if( (this.index === 0 && this.touch.disX > 0) || (this.index === this.length-1 && this.touch.disX < 0) ) this.touch.disX /= 4;

   在手指离开屏幕的时候,做出切换判断,是向左还是向右。在第一项时,不能向左切换,最后一项时不能向右切换。

1
2
3
this.touch.disX > 0 ? this.index-- : this.index++;
this.index < 0 && (this.index = 0);
this.index > this.length - 1 && (this.index = this.length - 1);

   最后是真正的移动切换,用了css3动画切换,translateX 或者 translate3d .

1
2
//this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translateX(" + (-this.index * this.width) + "px)";
this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";

  代码中有个transitionEnd方法,配合webkitTransitionEnd事件在动画切换执行完成时调用。这里调用这个方法是用来清除动画定义的持续时间。

1
2
3
4
5
6
7
transitionEnd: function(){
    this.mcontent.style.webkitTransition = this.mcontent.style.transition = 'all 0ms';
}
 
this.mcontent.addEventListener('webkitTransitionEnd',function(){
    _this.transitionEnd();
}, false);

   点击切换是判断点击了哪一项,在初始设置时已经为每一项保存了索引值(index),根据对应的索引值,切换选项,更新状态。可以循环绑定点击事件,也可以使用事件委托,这里使用的是事件委托。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
this.mheads[i].index = i;
 
touchClick: function(e){
    var target = e.target;
    if(target.nodeType === 1 && target.index !== undefined){
        //..
        this.index = target.index;
        //..
        this.mcontent.style.webkitTransform = this.mcontent.style.transform = "translate3d(" + (-this.index * this.width) + "px,0,0)";
         
    }
}
 
this.mhead.addEventListener("click",function(e){
    _this.touchClick(e);
}, false);

   tab选项卡主要是获得两组对应的类似集合(一组标签,一组内容),两个类似数组都有索引值(数组下标),通过这个索引值,做出对应的切换。获取索引值是tab选项卡的关键,移动web端的选项卡主要是增加了触摸事件操作这个索引值。加上定时器,每隔多少时间增加或者减少这个索引值,自动播放也就完成了。会了tab选项卡也就会了图片的切换,焦点图什么的,原理都是一样。 

   tab选项卡下载

posted @   eyeear  阅读(6007)  评论(2编辑  收藏  举报
点击右上角即可分享
微信分享提示