在这篇文章中,我实现了一个基本的选项卡功能:请猛击后面的链接>> [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 })();