[js插件开发教程]实现一个比较完整的开源级选项卡插件

在这篇文章中,我实现了一个基本的选项卡功能:请猛击后面的链接>>   [js插件开发教程]原生js仿jquery架构扩展开发选项卡插件.

还缺少两个常用的切换(自动切换与透明度渐变),当然有朋友会说,还有左右,上下等等,这些动画会放在焦点图(幻灯片)插件系列.

(自动切换,停止控制,透明度渐变 ) 效果预览:

自动切换的实现:

这个思路很简单,开启定时器,让选项卡的索引+1,加到4的时候(选项卡的长度)从0开始

传统做法:

index = 0

index++

if ( index == 4 ) {

index = 0

}

小技巧(估计很多人都没有用过):

var i = ( index + 1 ) %  4

index为当前选中的选项卡 索引

当index = 0,他下一张就是1,  通过上面的取余操作,i = 1

当index = 3,他下一张就是0, 通过上面的取余操作,i = 0

这种方法不需要判断边界,只需要一句代码。在实际开发中,把那个4替换成选项卡的长度

好了,关键的思路和技巧有了,我们开始拼接框架了:

 1         var defaults = {
 2             contentClass : 'tab-content',
 3             navClass : 'tab-nav',
 4             activeClass : 'active',
 5             triggerElements : '*',
 6             activeIndex : 0,
 7             evType : 'click',
 8             effect : 'none',
 9             auto : false,
10             delay : 3000,
11             duration : 1000
12         };

defaults参数,增加几个配置:

effect: none(没有特效) / fade( 透明度切换 )

auto: false(不会自动切换) / true ( 开启自动切换 )

delay : 多少时间 切换一个选项卡

duration: 透明度开启,这个才会用到,表示,多长时间内 完成透明度的切换

1 if ( options.effect == 'fade' ) {
2             tabContent.style.position = 'relative';
3             for( var i = 0; i < tabContentEle.length; i++ ) {
4                 tabContentEle[i].style.position = 'absolute';
5             }
6             tabContentEle[opt.activeIndex].style.zIndex = _contentLen + 1;
7             opt.delay += opt.duration;
8         }

当开启透明度变化的时候,把选项卡元素设置成定位方式,当前选中的选项卡,层级为最高!!! ( 如果不是最高层级,那么默认是最后一个选项卡在最上面,所以 “内容4” 就会在最上层,显然不是我们想要的结果)层级+定位 这一招也很常用,经常用来做显示隐藏,和透明度变化.

根据opt配置,判断是否开启了auto自动切换功能

 1  //是否自动播放
 2         if ( opt.auto ) {
 3             for( var i = 0 ; i < tabNavEle.length; i++ ){
 4                 tabNavEle[i].index = i;
 5                 tabNavEle[i].onmouseover = function(){
 6                     _api.stop();
 7                     _api.setIndex( this.index );
 8                 };
 9                 tabNavEle[i].onmouseout = function(){
10                     _api.start();
11                     _api.setIndex( this.index );
12                 };
13             }
14             _api.start();
15         }

如果开启了,做两件事情:

1,调用start()函数,让索引+1

2,选项卡导航部分,添加事件控制 自动播放的暂停和开始

start与stop方法?

 1 _api.stop = function(){
 2             timer && clearInterval( timer );
 3         };
 4 
 5         _api.start = function(){
 6             _api.stop();
 7             timer = setInterval( function(){
 8                 _api.next();
 9             }, opt.delay );
10         };

调用next方法,你应该猜得到next方法做的事情就是索引+1 吧

1  _api.next = function(){
2             var i = ( _index + 1 ) % _contentLen;
3             _api.setIndex( i );
4         };

索引+1之后,再切换选项卡

最后在setIndex方法:增加透明度变化

 1 if ( _index != index ) {
 2                         tabContentEle[_index].style.zIndex = _contentLen + 1;
 3                         for (var i = 0; i < tabContentEle.length; i++) {
 4                             if (i != _index) {
 5                                 tabContentEle[i].style.zIndex = ( index + _contentLen - ( i + 1 ) ) % _contentLen + 1;
 6                                 tabContentEle[i].style.opacity = 1;
 7                             }
 8                         }
 9                         animate(tabContentEle[_index], {'opacity': 0}, function () {
10                             tabContentEle[_index].style.zIndex = ( index + _contentLen - ( _index + 1 ) ) % _contentLen + 1;
11                             _index = index;
12                         });
13                     }

完整的js代码有220行:

  1 /**
  2  * Created by ghostwu(吴华).
  3  */
  4 (function(){
  5     var G = function( selectors, context ){
  6         return new G.fn.init( selectors, context );
  7     }
  8     G.fn = G.prototype = {
  9         length : 0,
 10         constructor : G,
 11         size : function(){
 12             return this.length;
 13         },
 14         init : function( selector, context ){
 15             this.length = 0;
 16             context = context || document;
 17             if ( selector.indexOf( '#' ) == 0 ){
 18                 this[0] = document.getElementById( selector.substring( 1 ) );
 19                 this.length = 1;
 20             }else {
 21                 var aNode = context.querySelectorAll( selector );
 22                 for( var i = 0, len = aNode.length; i < len; i++ ) {
 23                     this[i] = aNode[i];
 24                 }
 25                 this.length = len;
 26             }
 27             this.selector = selector;
 28             this.context = context;
 29             return this;
 30         }
 31     }
 32 
 33     G.fn.init.prototype = G.fn;
 34     G.extend = G.fn.extend = function () {
 35         var i = 1,
 36             len = arguments.length,
 37             dst = arguments[0],
 38             j;
 39         if (dst.length === undefined) {
 40             dst.length = 0;
 41         }
 42         if (i == len) {
 43             dst = this;
 44             i--;
 45         }
 46         for (; i < len; i++) {
 47             for (j in arguments[i]) {
 48                 dst[j] = arguments[i][j];
 49                 dst.length++;
 50             }
 51         }
 52         return dst;
 53     };
 54 
 55     function css(obj, attr, value) {
 56         if (arguments.length == 3) {
 57             obj.style[attr] = value;
 58         } else {
 59             if (obj.currentStyle) {
 60                 return obj.currentStyle[attr];
 61             } else {
 62                 return getComputedStyle(obj, false)[attr];
 63             }
 64         }
 65     }
 66 
 67     function animate(obj, attr, fn) {
 68         clearInterval(obj.timer);
 69         var cur = 0;
 70         var target = 0;
 71         var speed = 0;
 72         var start = new Date().getTime();//起始时间
 73         obj.timer = setInterval(function () {
 74             var bFlag = true;
 75             for (var key in attr) {
 76                 if (key == 'opacity') {
 77                     cur = css(obj, 'opacity') * 100;
 78                 } else {
 79                     cur = parseInt(css(obj, key));
 80                 }
 81                 target = attr[key];
 82                 speed = ( target - cur ) / 8;
 83                 speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
 84                 if (cur != target) {
 85                     bFlag = false;
 86                     if (key == 'opacity') {
 87                         obj.style.opacity = ( cur + speed ) / 100;
 88                         obj.style.filter = "alpha(opacity:" + ( cur + speed ) + ")";
 89                     } else {
 90                         obj.style[key] = cur + speed + "px";
 91                     }
 92                 }
 93             }
 94             if (bFlag) {
 95                 var end = new Date().getTime();//结束时间
 96                 console.log( '总计:',  ( end - start ) );
 97                 clearInterval(obj.timer);
 98                 fn && fn.call(obj);
 99             }
100         }, 30 );
101     }
102 
103     G.fn.tabs = function( options ){
104         options = options || {};
105         var defaults = {
106             contentClass : 'tab-content',
107             navClass : 'tab-nav',
108             activeClass : 'active',
109             triggerElements : '*',
110             activeIndex : 0,
111             evType : 'click',
112             effect : 'none',
113             auto : false,
114             delay : 3000,
115             duration : 1000
116         };
117 
118         var opt = G.extend( {}, defaults, options );
119 
120         var tabContent = this[0].querySelector( "." + opt.contentClass );
121         var tabContentEle = tabContent.children;
122         var tabNavEle = this[0].querySelectorAll( "." + opt.navClass + '>' + opt.triggerElements );
123 
124         var _contentLen = tabContentEle.length; //选项卡个数
125         var _index = opt.activeIndex;
126         var timer = null;
127 
128         if ( options.effect == 'fade' ) {
129             tabContent.style.position = 'relative';
130             for( var i = 0; i < tabContentEle.length; i++ ) {
131                 tabContentEle[i].style.position = 'absolute';
132             }
133             tabContentEle[opt.activeIndex].style.zIndex = _contentLen + 1;
134             opt.delay += opt.duration;
135         }
136 
137         var _api = {};
138 
139         _api.next = function(){
140             var i = ( _index + 1 ) % _contentLen;
141             _api.setIndex( i );
142         };
143 
144         _api.stop = function(){
145             timer && clearInterval( timer );
146         };
147 
148         _api.start = function(){
149             _api.stop();
150             timer = setInterval( function(){
151                 _api.next();
152             }, opt.delay );
153         };
154 
155         _api.setIndex = function( index ){
156             //当前标签加上active样式,其余标签删除active样式
157             for ( var i = 0; i < _contentLen; i++ ) {
158                 if ( tabNavEle[i].classList.contains( 'active' ) ) {
159                     tabNavEle[i].classList.remove('active');
160                 }
161             }
162             tabNavEle[index].classList.add( 'active' );
163             switch ( opt.effect ){
164                 case 'fade':
165                     if ( _index != index ) {
166                         tabContentEle[_index].style.zIndex = _contentLen + 1;
167                         for (var i = 0; i < tabContentEle.length; i++) {
168                             if (i != _index) {
169                                 tabContentEle[i].style.zIndex = ( index + _contentLen - ( i + 1 ) ) % _contentLen + 1;
170                                 tabContentEle[i].style.opacity = 1;
171                             }
172                         }
173                         animate(tabContentEle[_index], {'opacity': 0}, function () {
174                             tabContentEle[_index].style.zIndex = ( index + _contentLen - ( _index + 1 ) ) % _contentLen + 1;
175                             _index = index;
176                         });
177                     }
178                     break;
179                 default:
180                     for ( var i = 0; i < _contentLen; i++ ) {
181                         tabContentEle[i].style.display = 'none';
182                     }
183                     tabContentEle[index].style.display = 'block';
184                     _index = index;
185             }
186         }
187 
188         _api.setIndex( _index ); //默认的选项卡
189 
190         //所有的标签绑定事件
191         for( var i = 0, len = tabNavEle.length; i < len; i++ ) {
192             tabNavEle[i].index = i;
193             tabNavEle[i].addEventListener( opt.evType, function(){
194                 var i = this.index;
195                 _api.setIndex( i );
196             }, false );
197         }
198 
199         //是否自动播放
200         if ( opt.auto ) {
201             for( var i = 0 ; i < tabNavEle.length; i++ ){
202                 tabNavEle[i].index = i;
203                 tabNavEle[i].onmouseover = function(){
204                     _api.stop();
205                     _api.setIndex( this.index );
206                 };
207                 tabNavEle[i].onmouseout = function(){
208                     _api.start();
209                     _api.setIndex( this.index );
210                 };
211             }
212             _api.start();
213         }
214 
215         return this;
216     }
217 
218     var $ = function( selectors, context ){
219         return G( selectors, context );
220     }
221     window.$ = $;
222 })();
View Code

 

posted @ 2017-10-23 11:44  ghostwu  阅读(3681)  评论(0编辑  收藏  举报
Copyright ©2017 ghostwu