|
|
/**
|
|
|
* snap 重构
|
|
|
* @param {Object} $
|
|
|
* @param {Object} window
|
|
|
*/
|
|
|
(function($, window) {
|
|
|
var CLASS_SLIDER = $.className('slider');
|
|
|
var CLASS_SLIDER_GROUP = $.className('slider-group');
|
|
|
var CLASS_SLIDER_LOOP = $.className('slider-loop');
|
|
|
var CLASS_SLIDER_INDICATOR = $.className('slider-indicator');
|
|
|
var CLASS_ACTION_PREVIOUS = $.className('action-previous');
|
|
|
var CLASS_ACTION_NEXT = $.className('action-next');
|
|
|
var CLASS_SLIDER_ITEM = $.className('slider-item');
|
|
|
|
|
|
var CLASS_ACTIVE = $.className('active');
|
|
|
|
|
|
var SELECTOR_SLIDER_ITEM = '.' + CLASS_SLIDER_ITEM;
|
|
|
var SELECTOR_SLIDER_INDICATOR = '.' + CLASS_SLIDER_INDICATOR;
|
|
|
var SELECTOR_SLIDER_PROGRESS_BAR = $.classSelector('.slider-progress-bar');
|
|
|
|
|
|
var Slider = $.Slider = $.Scroll.extend({
|
|
|
init: function(element, options) {
|
|
|
this._super(element, $.extend(true, {
|
|
|
fingers: 1,
|
|
|
interval: 0, //设置为0,则不定时轮播
|
|
|
scrollY: false,
|
|
|
scrollX: true,
|
|
|
indicators: false,
|
|
|
scrollTime: 1000,
|
|
|
startX: false,
|
|
|
slideTime: 0, //滑动动画时间
|
|
|
snap: SELECTOR_SLIDER_ITEM
|
|
|
}, options));
|
|
|
if (this.options.startX) {
|
|
|
// $.trigger(this.wrapper, 'scrollend', this);
|
|
|
}
|
|
|
},
|
|
|
_init: function() {
|
|
|
this._reInit();
|
|
|
if (this.scroller) {
|
|
|
this.scrollerStyle = this.scroller.style;
|
|
|
this.progressBar = this.wrapper.querySelector(SELECTOR_SLIDER_PROGRESS_BAR);
|
|
|
if (this.progressBar) {
|
|
|
this.progressBarWidth = this.progressBar.offsetWidth;
|
|
|
this.progressBarStyle = this.progressBar.style;
|
|
|
}
|
|
|
//忘记这个代码是干什么的了?
|
|
|
// this.x = this._getScroll();
|
|
|
// if (this.options.startX === false) {
|
|
|
// this.options.startX = this.x;
|
|
|
// }
|
|
|
//根据active修正startX
|
|
|
|
|
|
this._super();
|
|
|
this._initTimer();
|
|
|
}
|
|
|
},
|
|
|
_triggerSlide: function() {
|
|
|
var self = this;
|
|
|
self.isInTransition = false;
|
|
|
var page = self.currentPage;
|
|
|
self.slideNumber = self._fixedSlideNumber();
|
|
|
if (self.loop) {
|
|
|
if (self.slideNumber === 0) {
|
|
|
self.setTranslate(self.pages[1][0].x, 0);
|
|
|
} else if (self.slideNumber === self.itemLength - 3) {
|
|
|
self.setTranslate(self.pages[self.itemLength - 2][0].x, 0);
|
|
|
}
|
|
|
}
|
|
|
if (self.lastSlideNumber != self.slideNumber) {
|
|
|
self.lastSlideNumber = self.slideNumber;
|
|
|
self.lastPage = self.currentPage;
|
|
|
$.trigger(self.wrapper, 'slide', {
|
|
|
slideNumber: self.slideNumber
|
|
|
});
|
|
|
}
|
|
|
self._initTimer();
|
|
|
},
|
|
|
_handleSlide: function(e) {
|
|
|
var self = this;
|
|
|
if (e.target !== self.wrapper) {
|
|
|
return;
|
|
|
}
|
|
|
var detail = e.detail;
|
|
|
detail.slideNumber = detail.slideNumber || 0;
|
|
|
var temps = self.scroller.querySelectorAll(SELECTOR_SLIDER_ITEM);
|
|
|
var items = [];
|
|
|
for (var i = 0, len = temps.length; i < len; i++) {
|
|
|
var item = temps[i];
|
|
|
if (item.parentNode === self.scroller) {
|
|
|
items.push(item);
|
|
|
}
|
|
|
}
|
|
|
var _slideNumber = detail.slideNumber;
|
|
|
if (self.loop) {
|
|
|
_slideNumber += 1;
|
|
|
}
|
|
|
if (!self.wrapper.classList.contains($.className('segmented-control'))) {
|
|
|
for (var i = 0, len = items.length; i < len; i++) {
|
|
|
var item = items[i];
|
|
|
if (item.parentNode === self.scroller) {
|
|
|
if (i === _slideNumber) {
|
|
|
item.classList.add(CLASS_ACTIVE);
|
|
|
} else {
|
|
|
item.classList.remove(CLASS_ACTIVE);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
var indicatorWrap = self.wrapper.querySelector($.classSelector('.slider-indicator'));
|
|
|
if (indicatorWrap) {
|
|
|
if (indicatorWrap.getAttribute('data-scroll')) { //scroll
|
|
|
$(indicatorWrap).scroll().gotoPage(detail.slideNumber);
|
|
|
}
|
|
|
var indicators = indicatorWrap.querySelectorAll($.classSelector('.indicator'));
|
|
|
if (indicators.length > 0) { //图片轮播
|
|
|
for (var i = 0, len = indicators.length; i < len; i++) {
|
|
|
indicators[i].classList[i === detail.slideNumber ? 'add' : 'remove'](CLASS_ACTIVE);
|
|
|
}
|
|
|
} else {
|
|
|
var number = indicatorWrap.querySelector($.classSelector('.number span'));
|
|
|
if (number) { //图文表格
|
|
|
number.innerText = (detail.slideNumber + 1);
|
|
|
} else { //segmented controls
|
|
|
var controlItems = indicatorWrap.querySelectorAll($.classSelector('.control-item'));
|
|
|
for (var i = 0, len = controlItems.length; i < len; i++) {
|
|
|
controlItems[i].classList[i === detail.slideNumber ? 'add' : 'remove'](CLASS_ACTIVE);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
e.stopPropagation();
|
|
|
},
|
|
|
_handleTabShow: function(e) {
|
|
|
var self = this;
|
|
|
self.gotoItem((e.detail.tabNumber || 0), self.options.slideTime);
|
|
|
},
|
|
|
_handleIndicatorTap: function(event) {
|
|
|
var self = this;
|
|
|
var target = event.target;
|
|
|
if (target.classList.contains(CLASS_ACTION_PREVIOUS) || target.classList.contains(CLASS_ACTION_NEXT)) {
|
|
|
self[target.classList.contains(CLASS_ACTION_PREVIOUS) ? 'prevItem' : 'nextItem']();
|
|
|
event.stopPropagation();
|
|
|
}
|
|
|
},
|
|
|
_initEvent: function(detach) {
|
|
|
var self = this;
|
|
|
self._super(detach);
|
|
|
var action = detach ? 'removeEventListener' : 'addEventListener';
|
|
|
self.wrapper[action]('slide', this);
|
|
|
self.wrapper[action]($.eventName('shown', 'tab'), this);
|
|
|
},
|
|
|
handleEvent: function(e) {
|
|
|
this._super(e);
|
|
|
switch (e.type) {
|
|
|
case 'slide':
|
|
|
this._handleSlide(e);
|
|
|
break;
|
|
|
case $.eventName('shown', 'tab'):
|
|
|
if (~this.snaps.indexOf(e.target)) { //避免嵌套监听错误的tab show
|
|
|
this._handleTabShow(e);
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
},
|
|
|
_scrollend: function(e) {
|
|
|
this._super(e);
|
|
|
this._triggerSlide(e);
|
|
|
},
|
|
|
_drag: function(e) {
|
|
|
this._super(e);
|
|
|
var direction = e.detail.direction;
|
|
|
if (direction === 'left' || direction === 'right') {
|
|
|
//拖拽期间取消定时
|
|
|
var slidershowTimer = this.wrapper.getAttribute('data-slidershowTimer');
|
|
|
slidershowTimer && window.clearTimeout(slidershowTimer);
|
|
|
|
|
|
e.stopPropagation();
|
|
|
}
|
|
|
},
|
|
|
_initTimer: function() {
|
|
|
var self = this;
|
|
|
var slider = self.wrapper;
|
|
|
var interval = self.options.interval;
|
|
|
var slidershowTimer = slider.getAttribute('data-slidershowTimer');
|
|
|
slidershowTimer && window.clearTimeout(slidershowTimer);
|
|
|
if (interval) {
|
|
|
slidershowTimer = window.setTimeout(function() {
|
|
|
if (!slider) {
|
|
|
return;
|
|
|
}
|
|
|
//仅slider显示状态进行自动轮播
|
|
|
if (!!(slider.offsetWidth || slider.offsetHeight)) {
|
|
|
self.nextItem(true);
|
|
|
//下一个
|
|
|
}
|
|
|
self._initTimer();
|
|
|
}, interval);
|
|
|
slider.setAttribute('data-slidershowTimer', slidershowTimer);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
_fixedSlideNumber: function(page) {
|
|
|
page = page || this.currentPage;
|
|
|
var slideNumber = page.pageX;
|
|
|
if (this.loop) {
|
|
|
if (page.pageX === 0) {
|
|
|
slideNumber = this.itemLength - 3;
|
|
|
} else if (page.pageX === (this.itemLength - 1)) {
|
|
|
slideNumber = 0;
|
|
|
} else {
|
|
|
slideNumber = page.pageX - 1;
|
|
|
}
|
|
|
}
|
|
|
return slideNumber;
|
|
|
},
|
|
|
_reLayout: function() {
|
|
|
this.hasHorizontalScroll = true;
|
|
|
this.loop = this.scroller.classList.contains(CLASS_SLIDER_LOOP);
|
|
|
this._super();
|
|
|
},
|
|
|
_getScroll: function() {
|
|
|
var result = $.parseTranslateMatrix($.getStyles(this.scroller, 'webkitTransform'));
|
|
|
return result ? result.x : 0;
|
|
|
},
|
|
|
_transitionEnd: function(e) {
|
|
|
if (e.target !== this.scroller || !this.isInTransition) {
|
|
|
return;
|
|
|
}
|
|
|
this._transitionTime();
|
|
|
this.isInTransition = false;
|
|
|
$.trigger(this.wrapper, 'scrollend', this);
|
|
|
},
|
|
|
_flick: function(e) {
|
|
|
if (!this.moved) { //无moved
|
|
|
return;
|
|
|
}
|
|
|
var detail = e.detail;
|
|
|
var direction = detail.direction;
|
|
|
this._clearRequestAnimationFrame();
|
|
|
this.isInTransition = true;
|
|
|
// if (direction === 'up' || direction === 'down') {
|
|
|
// this.resetPosition(this.options.bounceTime);
|
|
|
// return;
|
|
|
// }
|
|
|
if (e.type === 'flick') {
|
|
|
if (detail.deltaTime < 200) { //flick,太容易触发,额外校验一下deltaTime
|
|
|
this.x = this._getPage((this.slideNumber + (direction === 'right' ? -1 : 1)), true).x;
|
|
|
}
|
|
|
this.resetPosition(this.options.bounceTime);
|
|
|
} else if (e.type === 'dragend' && !detail.flick) {
|
|
|
this.resetPosition(this.options.bounceTime);
|
|
|
}
|
|
|
e.stopPropagation();
|
|
|
},
|
|
|
_initSnap: function() {
|
|
|
this.scrollerWidth = this.itemLength * this.scrollerWidth;
|
|
|
this.maxScrollX = Math.min(this.wrapperWidth - this.scrollerWidth, 0);
|
|
|
this._super();
|
|
|
if (!this.currentPage.x) {
|
|
|
//当slider处于隐藏状态时,导致snap计算是错误的,临时先这么判断一下,后续要考虑解决所有scroll在隐藏状态下初始化属性不正确的问题
|
|
|
var currentPage = this.pages[this.loop ? 1 : 0];
|
|
|
currentPage = currentPage || this.pages[0];
|
|
|
if (!currentPage) {
|
|
|
return;
|
|
|
}
|
|
|
this.currentPage = currentPage[0];
|
|
|
this.slideNumber = 0;
|
|
|
this.lastSlideNumber = typeof this.lastSlideNumber === 'undefined' ? 0 : this.lastSlideNumber;
|
|
|
} else {
|
|
|
this.slideNumber = this._fixedSlideNumber();
|
|
|
this.lastSlideNumber = typeof this.lastSlideNumber === 'undefined' ? this.slideNumber : this.lastSlideNumber;
|
|
|
}
|
|
|
this.options.startX = this.currentPage.x || 0;
|
|
|
},
|
|
|
_getSnapX: function(offsetLeft) {
|
|
|
return Math.max(-offsetLeft, this.maxScrollX);
|
|
|
},
|
|
|
_getPage: function(slideNumber, isFlick) {
|
|
|
if (this.loop) {
|
|
|
if (slideNumber > (this.itemLength - (isFlick ? 2 : 3))) {
|
|
|
slideNumber = 1;
|
|
|
time = 0;
|
|
|
} else if (slideNumber < (isFlick ? -1 : 0)) {
|
|
|
slideNumber = this.itemLength - 2;
|
|
|
time = 0;
|
|
|
} else {
|
|
|
slideNumber += 1;
|
|
|
}
|
|
|
} else {
|
|
|
if (!isFlick) {
|
|
|
if (slideNumber > (this.itemLength - 1)) {
|
|
|
slideNumber = 0;
|
|
|
time = 0;
|
|
|
} else if (slideNumber < 0) {
|
|
|
slideNumber = this.itemLength - 1;
|
|
|
time = 0;
|
|
|
}
|
|
|
}
|
|
|
slideNumber = Math.min(Math.max(0, slideNumber), this.itemLength - 1);
|
|
|
}
|
|
|
return this.pages[slideNumber][0];
|
|
|
},
|
|
|
_gotoItem: function(slideNumber, time) {
|
|
|
this.currentPage = this._getPage(slideNumber, true); //此处传true。可保证程序切换时,动画与人手操作一致(第一张,最后一张的切换动画)
|
|
|
this.scrollTo(this.currentPage.x, 0, time, this.options.scrollEasing);
|
|
|
if (time === 0) {
|
|
|
$.trigger(this.wrapper, 'scrollend', this);
|
|
|
}
|
|
|
},
|
|
|
//API
|
|
|
setTranslate: function(x, y) {
|
|
|
this._super(x, y);
|
|
|
var progressBar = this.progressBar;
|
|
|
if (progressBar) {
|
|
|
this.progressBarStyle.webkitTransform = this._getTranslateStr((-x * (this.progressBarWidth / this.wrapperWidth)), 0);
|
|
|
}
|
|
|
},
|
|
|
resetPosition: function(time) {
|
|
|
time = time || 0;
|
|
|
if (this.x > 0) {
|
|
|
this.x = 0;
|
|
|
} else if (this.x < this.maxScrollX) {
|
|
|
this.x = this.maxScrollX;
|
|
|
}
|
|
|
this.currentPage = this._nearestSnap(this.x);
|
|
|
this.scrollTo(this.currentPage.x, 0, time, this.options.scrollEasing);
|
|
|
return true;
|
|
|
},
|
|
|
gotoItem: function(slideNumber, time) {
|
|
|
this._gotoItem(slideNumber, typeof time === 'undefined' ? this.options.scrollTime : time);
|
|
|
},
|
|
|
nextItem: function() {
|
|
|
this._gotoItem(this.slideNumber + 1, this.options.scrollTime);
|
|
|
},
|
|
|
prevItem: function() {
|
|
|
this._gotoItem(this.slideNumber - 1, this.options.scrollTime);
|
|
|
},
|
|
|
getSlideNumber: function() {
|
|
|
return this.slideNumber || 0;
|
|
|
},
|
|
|
_reInit: function() {
|
|
|
var groups = this.wrapper.querySelectorAll('.' + CLASS_SLIDER_GROUP);
|
|
|
for (var i = 0, len = groups.length; i < len; i++) {
|
|
|
if (groups[i].parentNode === this.wrapper) {
|
|
|
this.scroller = groups[i];
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
this.scrollerStyle = this.scroller && this.scroller.style;
|
|
|
if (this.progressBar) {
|
|
|
this.progressBarWidth = this.progressBar.offsetWidth;
|
|
|
this.progressBarStyle = this.progressBar.style;
|
|
|
}
|
|
|
},
|
|
|
refresh: function(options) {
|
|
|
if (options) {
|
|
|
$.extend(this.options, options);
|
|
|
this._super();
|
|
|
this._initTimer();
|
|
|
} else {
|
|
|
this._super();
|
|
|
}
|
|
|
},
|
|
|
destroy: function() {
|
|
|
this._initEvent(true); //detach
|
|
|
delete $.data[this.wrapper.getAttribute('data-slider')];
|
|
|
this.wrapper.setAttribute('data-slider', '');
|
|
|
}
|
|
|
});
|
|
|
$.fn.slider = function(options) {
|
|
|
var slider = null;
|
|
|
this.each(function() {
|
|
|
var sliderElement = this;
|
|
|
if (!this.classList.contains(CLASS_SLIDER)) {
|
|
|
sliderElement = this.querySelector('.' + CLASS_SLIDER);
|
|
|
}
|
|
|
if (sliderElement && sliderElement.querySelector(SELECTOR_SLIDER_ITEM)) {
|
|
|
var id = sliderElement.getAttribute('data-slider');
|
|
|
if (!id) {
|
|
|
id = ++$.uuid;
|
|
|
$.data[id] = slider = new Slider(sliderElement, options);
|
|
|
sliderElement.setAttribute('data-slider', id);
|
|
|
} else {
|
|
|
slider = $.data[id];
|
|
|
if (slider && options) {
|
|
|
slider.refresh(options);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
return slider;
|
|
|
};
|
|
|
$.ready(function() {
|
|
|
// setTimeout(function() {
|
|
|
$($.classSelector('.slider')).slider();
|
|
|
$($.classSelector('.scroll-wrapper.slider-indicator.segmented-control')).scroll({
|
|
|
scrollY: false,
|
|
|
scrollX: true,
|
|
|
indicators: false,
|
|
|
snap: $.classSelector('.control-item')
|
|
|
});
|
|
|
// }, 500); //临时处理slider宽度计算不正确的问题(初步确认是scrollbar导致的)
|
|
|
|
|
|
});
|
|
|
})(mui, window); |