/** * @file 图片轮播组件 * @name Slider * @desc ../gmu/_examples/widget/slider/slider.html * 图片轮播组件 * @import core/touch.js, core/zepto.extend.js, core/zepto.ui.js */ (function($, undefined) { /** * @name $.ui.slider * @grammar $.ui.slider(el [,options]) => instance * @desc **el** * css选择器, 或者zepto对象 * **Options** * - ''container'' {selector|zepto}: (可选)放置的父容器 * - ''content'' {Array}: (必选)内容,格式为:\[ {href:'图片跳转URL', pic:'图片路径', title:'图片下方文字'}, {...}\] * - ''viewNum'' {Number}: (可选, 默认:1) 可以同时看到几张图片 * - ''imgInit'' {Number}: (可选, 默认:2)初始加载几张图片 * - ''itemRender'' {Function}: (可选)render模式时,使用的自定义内容构造函数,接受一个从0开始的index参数,返回空值时构造结束 * - ''imgZoom'' {Boolean}: (可选, 默认:false)是否缩放图片,设为true时可以将超出边界的图片等比缩放 * - ''loop'' {Boolean}: (可选, 默认:false)设为true时,播放到最后一张时继续正向播放第一张(无缝滑动),设为false则反向播放倒数第2张 * - ''stopPropagation'' {Boolean}: (可选, 默认:false)是否在横向滑动的时候阻止冒泡(慎用,会导致上层元素接受不到touchMove事件) * - ''springBackDis'' {Number}: (可选, 默认:15)滑动能够回弹的最大距离 * - ''autoPlay'' {Boolean}: ((可选, 默认:true)是否自动播放 * - ''autoPlayTime'' {Number}: (可选, 默认:4000ms)自动播放的间隔 * - ''animationTime'' {Number}: (可选, 默认:400ms)滑动动画时间 * - ''showArr'' {Boolean}: (可选, 默认:true)是否展示上一个下一个箭头 * - ''showDot'' {Boolean}: (可选, 默认:true)是否展示页码 * - ''slide'' {Function}: (可选)开始切换页面时执行的函数,第1个参数为Event对象,第2个参数为滑动后的page页码 * - ''slideend'' {Function}: (可选)页面切换完成(滑动完成)时执行的函数,第1个参数为Event对象,第2个参数为滑动后的page页码 * * **Demo** * * ../gmu/_examples/widget/slider/slider.html * */ $.ui.define('slider', { _data:{ viewNum: 1, imgInit: 2, itemRender: null, imgZoom: false, loop: false, stopPropagation: false, springBackDis: 15, autoPlay: true, autoPlayTime: 4000, animationTime: 400, showArr: true, showDot: true, slide: null, slideend: null, index: 0, _stepLength: 1, _direction: 1 }, _create:function() { var me = this, i = 0, j, k = [], content = me.data('content'); me._initConfig(); (me.root() || me.root($('
'))).addClass('ui-slider').appendTo(me.data('container') || (me.root().parent().length ? '' : document.body)).html( '
' + (function() { if(me.data('itemRender')) { var render = me.data('itemRender'); while(j = render.call(me, i++)) k.push('
' + j + '
'); } else { while(j = content[i++]) k.push('
' + (j.title ? '

' + j.title + '

': '') + '
'); } k.push(me.data('loop') ? '
' + k.join('') + '
' : ''); return k.join(''); }())); me._addDots(); }, _setup: function(mode) { var me = this, root = me.root().addClass('ui-slider'); me._initConfig(); if(!mode) { var items = root.children(), group = $('
').append(items.addClass('ui-slider-item')); root.empty().append($('
').append(group).append(me.data('loop') ? group.clone() : '')); me._addDots(); } else me.data('loop') && $('.ui-slider-wheel', root).append($('.ui-slider-group', root).clone()); }, _init:function() { var me = this, index = me.data('index'), root = me.root(), _eventHandler = $.proxy( me._eventHandler, me ); me._setWidth(); $(me.data('wheel')).on('touchstart touchmove touchend touchcancel webkitTransitionEnd', _eventHandler); $(window).on('ortchange', _eventHandler); $('.ui-slider-pre', root).on('tap', function() { me.pre() }); $('.ui-slider-next', root).on('tap', function() { me.next() }); me.on('destroy',function() { clearTimeout(me.data('play')); $(window).off('ortchange', _eventHandler); }); me.data('autoPlay') && me._setTimeout(); }, /** * 初始化参数配置 */ _initConfig: function() { var o = this._data; if(o.viewNum > 1) { o.loop = false; o.showDot = false; o.imgInit = o.viewNum + 1; } }, /** * 添加底部圆点及两侧箭头 */ _addDots:function() { var me = this, root = me.root(), length = $('.ui-slider-item', root).length / (me.data('loop') ? 2 : 1), html = []; if(me.data('showDot')) { html.push('

'); while(length--) html.push(''); html.push('

'); } me.data('showArr') && (html.push('')); root.append(html.join('')); }, /** * 设置轮播条及元素宽度,设置选中dot,设置索引map,加载图片 */ _setWidth:function(){ var me = this, o = me._data, root = me.root(), width = Math.ceil(root.width() / o.viewNum), height = root.height(), loop = o.loop, items = $('.ui-slider-item', root).toArray(), length = items.length, wheel = $('.ui-slider-wheel', root).width(width * length)[0], dots = $('.ui-slider-dots b', root).toArray(), allImgs = $('img', root).toArray(), lazyImgs = allImgs.concat(), dotIndex = {}, i, j, l = o.imgInit || length; o.showDot && (dots[0].className = 'ui-slider-dot-select'); if(o.imgZoom) $(lazyImgs).on('load', function() { var h = this.height, w = this.width, min_h = Math.min(h, height), min_w = Math.min(w, width); if(h/height > w/width) this.style.cssText += 'height:' + min_h + 'px;' + 'width:' + min_h/h * w + 'px;'; else this.style.cssText += 'height:' + min_w/w * h + 'px;' + 'width:' + min_w + 'px'; this.onload = null; }); for(i = 0; i < length; i++) { items[i].style.cssText += 'width:'+ width + 'px;position:absolute;-webkit-transform:translate3d(' + i * width + 'px,0,0);z-index:' + (900 - i); dotIndex[i] = loop ? (i > length/2 - 1 ? i - length/2 : i) : i; if(i < l) { j = lazyImgs.shift(); j && (j.src = j.getAttribute('lazyload')); if(o.loop) { j = allImgs[i + length/2]; j && (j.src = j.getAttribute('lazyload')); } } } me.data({ root: root[0], wheel: wheel, items: items, lazyImgs: lazyImgs, allImgs: allImgs, length: length, width: width, height: height, dots: dots, dotIndex: dotIndex, dot: dots[0] }); return me; }, /** * 事件管理函数 */ _eventHandler:function(e) { var me = this; switch (e.type) { case 'touchmove': me._touchMove(e); break; case 'touchstart': me._touchStart(e); break; case 'touchcancel': case 'touchend': me._touchEnd(); break; case 'webkitTransitionEnd': me._transitionEnd(); break; case 'ortchange': me._resize.call(me); break; } }, /** * touchstart事件 */ _touchStart:function(e) { var me = this; me.data({ pageX: e.touches[0].pageX, pageY: e.touches[0].pageY, S: false, //isScrolling T: false, //isTested X: 0 //horizontal moved }); me.data('wheel').style.webkitTransitionDuration = '0ms'; }, /** * touchmove事件 */ _touchMove:function(e) { var o = this._data, X = o.X = e.touches[0].pageX - o.pageX; if(!o.T) { var index = o.index, length = o.length, S = Math.abs(X) < Math.abs(e.touches[0].pageY - o.pageY); o.loop && (o.index = index > 0 && (index < length - 1) ? index : (index === length - 1) && X < 0 ? length/2 - 1 : index === 0 && X > 0 ? length/2 : index); S || clearTimeout(o.play); o.T = true; o.S = S; } if(!o.S) { o.stopPropagation && e.stopPropagation(); e.preventDefault(); o.wheel.style.webkitTransform = 'translate3d(' + (X - o.index * o.width) + 'px,0,0)'; } }, /** * touchend事件 */ _touchEnd:function() { var me = this, o = me._data; if(!o.S) { var distance = o.springBackDis, stepLength = o.X <= -distance ? Math.ceil(-o.X / o.width) : (o.X > distance) ? -Math.ceil(o.X / o.width) : 0; o._stepLength = Math.abs(stepLength); me._slide(o.index + stepLength); } }, /** * 轮播位置判断 */ _slide:function(index, auto) { var me = this, o = me._data, length = o.length, end = length - o.viewNum + 1; if(-1 < index && index < end) { me._move(index); } else if(index >= end) { if(!o.loop) { me._move(end - (auto ? 2 : 1)); o._direction = -1; } else { o.wheel.style.cssText += '-webkit-transition:0ms;-webkit-transform:translate3d(-' + (length/2 - 1) * o.width + 'px,0,0);'; o._direction = 1; $.later(function() {me._move(length/2)}, 20); } } else { if(!o.loop) me._move(auto ? 1 : 0); else { o.wheel.style.cssText += '-webkit-transition:0ms;-webkit-transform:translate3d(-' + (length/2) * o.width + 'px,0,0);'; $.later(function() {me._move(length/2 - 1)}, 20); } o._direction = 1; } return me; }, /** * 轮播方法 */ _move:function(index) { var o = this._data, dotIndex = o.dotIndex[index]; this.trigger('slide', dotIndex); if(o.lazyImgs.length) { var j = o.allImgs[index]; j && j.src || (j.src = j.getAttribute('lazyload')); } if(o.showDot) { o.dot.className = ''; o.dots[dotIndex].className = 'ui-slider-dot-select'; o.dot = o.dots[dotIndex]; } o.index = index; o.wheel.style.cssText += '-webkit-transition:' + o.animationTime + 'ms;-webkit-transform:translate3d(-' + index * o.width + 'px,0,0);'; }, /** * 滑动结束 */ _transitionEnd:function() { var me = this, o = me._data; me.trigger('slideend', o.dotIndex[o.index]); if(o.lazyImgs.length){ for(var length = o._stepLength, i = 0; i< length; i++) { var j = o.lazyImgs.shift(); j && (j.src = j.getAttribute('lazyload')); if(o.loop) { j = o.allImgs[o.index + o.length / 2]; j && !j.src && (j.src = j.getAttribute('lazyload')); } } o._stepLength = 1; } me._setTimeout(); }, /** * 设置自动播放 */ _setTimeout:function() { var me = this, o = me._data; if(!o.autoPlay) return me; clearTimeout(o.play); o.play = $.later(function() { me._slide.call(me, o.index + o._direction, true); }, o.autoPlayTime); return me; }, /** * 重设容器及子元素宽度 */ _resize:function() { var me = this, o = me._data, width = o.root.offsetWidth / o.viewNum, //todo 添加获取隐藏元素大小的方法 length = o.length, items = o.items; if(!width) return me; o.width = width; clearTimeout(o.play); for(var i = 0; i < length; i++) items[i].style.cssText += 'width:' + width + 'px;-webkit-transform:translate3d(' + i * width + 'px,0,0);'; o.wheel.style.removeProperty('-webkit-transition'); o.wheel.style.cssText += 'width:' + width * length + 'px;-webkit-transform:translate3d(-' + o.index * width + 'px,0,0);'; o._direction = 1; me._setTimeout(); return me; }, /** * @desc 滚动到上一张 * @name pre * @grammar pre() => self * @example * //setup mode * $('#slider').slider('pre'); * * //render mode * var demo = $.ui.slider(); * demo.pre(); */ pre:function() { var me = this; me._slide(me.data('index') - 1); return me; }, /** * @desc 滚动到下一张 * @name next * @grammar next() => self * @example * //setup mode * $('#slider').slider('next'); * * //render mode * var demo = $.ui.slider(); * demo.next(); */ next:function() { var me = this; me._slide(me.data('index') + 1); return me; }, /** * @desc 停止自动播放 * @name stop * @grammar stop() => self * @example * //setup mode * $('#slider').slider('stop'); * * //render mode * var demo = $.ui.slider(); * demo.stop(); */ stop:function() { var me = this; clearTimeout(me.data('play')); me.data('autoPlay', false); return me; }, /** * @desc 恢复自动播放 * @name resume * @grammar resume() => self * @example * //setup mode * $('#slider').slider('resume'); * * //render mode * var demo = $.ui.slider(); * demo.resume(); */ resume:function() { var me = this; me.data('_direction',1); me.data('autoPlay', true); me._setTimeout(); return me; } /** * @name Trigger Events * @theme event * @desc 组件内部触发的事件 * ^ 名称 ^ 处理函数参数 ^ 描述 ^ * | init | event | 组件初始化的时候触发,不管是render模式还是setup模式都会触发 | * | slide | event | 开始切换页面时执行的函数,参数为滑动后的page页码 | * | slideend | event | 页面切换完成(滑动完成)时执行的函数,参数为滑动后的page页码 | * | destroy | event | 组件在销毁的时候触发 | */ }); })(Zepto);