mui.class.scroll.slider.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /**
  2. * snap 重构
  3. * @param {Object} $
  4. * @param {Object} window
  5. */
  6. (function($, window) {
  7. var CLASS_SLIDER = $.className('slider');
  8. var CLASS_SLIDER_GROUP = $.className('slider-group');
  9. var CLASS_SLIDER_LOOP = $.className('slider-loop');
  10. var CLASS_SLIDER_INDICATOR = $.className('slider-indicator');
  11. var CLASS_ACTION_PREVIOUS = $.className('action-previous');
  12. var CLASS_ACTION_NEXT = $.className('action-next');
  13. var CLASS_SLIDER_ITEM = $.className('slider-item');
  14. var CLASS_ACTIVE = $.className('active');
  15. var SELECTOR_SLIDER_ITEM = '.' + CLASS_SLIDER_ITEM;
  16. var SELECTOR_SLIDER_INDICATOR = '.' + CLASS_SLIDER_INDICATOR;
  17. var SELECTOR_SLIDER_PROGRESS_BAR = $.classSelector('.slider-progress-bar');
  18. var Slider = $.Slider = $.Scroll.extend({
  19. init: function(element, options) {
  20. this._super(element, $.extend(true, {
  21. fingers: 1,
  22. interval: 0, //设置为0,则不定时轮播
  23. scrollY: false,
  24. scrollX: true,
  25. indicators: false,
  26. scrollTime: 1000,
  27. startX: false,
  28. slideTime: 0, //滑动动画时间
  29. snap: SELECTOR_SLIDER_ITEM
  30. }, options));
  31. if (this.options.startX) {
  32. // $.trigger(this.wrapper, 'scrollend', this);
  33. }
  34. },
  35. _init: function() {
  36. this._reInit();
  37. if (this.scroller) {
  38. this.scrollerStyle = this.scroller.style;
  39. this.progressBar = this.wrapper.querySelector(SELECTOR_SLIDER_PROGRESS_BAR);
  40. if (this.progressBar) {
  41. this.progressBarWidth = this.progressBar.offsetWidth;
  42. this.progressBarStyle = this.progressBar.style;
  43. }
  44. //忘记这个代码是干什么的了?
  45. // this.x = this._getScroll();
  46. // if (this.options.startX === false) {
  47. // this.options.startX = this.x;
  48. // }
  49. //根据active修正startX
  50. this._super();
  51. this._initTimer();
  52. }
  53. },
  54. _triggerSlide: function() {
  55. var self = this;
  56. self.isInTransition = false;
  57. var page = self.currentPage;
  58. self.slideNumber = self._fixedSlideNumber();
  59. if (self.loop) {
  60. if (self.slideNumber === 0) {
  61. self.setTranslate(self.pages[1][0].x, 0);
  62. } else if (self.slideNumber === self.itemLength - 3) {
  63. self.setTranslate(self.pages[self.itemLength - 2][0].x, 0);
  64. }
  65. }
  66. if (self.lastSlideNumber != self.slideNumber) {
  67. self.lastSlideNumber = self.slideNumber;
  68. self.lastPage = self.currentPage;
  69. $.trigger(self.wrapper, 'slide', {
  70. slideNumber: self.slideNumber
  71. });
  72. }
  73. self._initTimer();
  74. },
  75. _handleSlide: function(e) {
  76. var self = this;
  77. if (e.target !== self.wrapper) {
  78. return;
  79. }
  80. var detail = e.detail;
  81. detail.slideNumber = detail.slideNumber || 0;
  82. var temps = self.scroller.querySelectorAll(SELECTOR_SLIDER_ITEM);
  83. var items = [];
  84. for (var i = 0, len = temps.length; i < len; i++) {
  85. var item = temps[i];
  86. if (item.parentNode === self.scroller) {
  87. items.push(item);
  88. }
  89. }
  90. var _slideNumber = detail.slideNumber;
  91. if (self.loop) {
  92. _slideNumber += 1;
  93. }
  94. if (!self.wrapper.classList.contains($.className('segmented-control'))) {
  95. for (var i = 0, len = items.length; i < len; i++) {
  96. var item = items[i];
  97. if (item.parentNode === self.scroller) {
  98. if (i === _slideNumber) {
  99. item.classList.add(CLASS_ACTIVE);
  100. } else {
  101. item.classList.remove(CLASS_ACTIVE);
  102. }
  103. }
  104. }
  105. }
  106. var indicatorWrap = self.wrapper.querySelector($.classSelector('.slider-indicator'));
  107. if (indicatorWrap) {
  108. if (indicatorWrap.getAttribute('data-scroll')) { //scroll
  109. $(indicatorWrap).scroll().gotoPage(detail.slideNumber);
  110. }
  111. var indicators = indicatorWrap.querySelectorAll($.classSelector('.indicator'));
  112. if (indicators.length > 0) { //图片轮播
  113. for (var i = 0, len = indicators.length; i < len; i++) {
  114. indicators[i].classList[i === detail.slideNumber ? 'add' : 'remove'](CLASS_ACTIVE);
  115. }
  116. } else {
  117. var number = indicatorWrap.querySelector($.classSelector('.number span'));
  118. if (number) { //图文表格
  119. number.innerText = (detail.slideNumber + 1);
  120. } else { //segmented controls
  121. var controlItems = indicatorWrap.querySelectorAll($.classSelector('.control-item'));
  122. for (var i = 0, len = controlItems.length; i < len; i++) {
  123. controlItems[i].classList[i === detail.slideNumber ? 'add' : 'remove'](CLASS_ACTIVE);
  124. }
  125. }
  126. }
  127. }
  128. e.stopPropagation();
  129. },
  130. _handleTabShow: function(e) {
  131. var self = this;
  132. self.gotoItem((e.detail.tabNumber || 0), self.options.slideTime);
  133. },
  134. _handleIndicatorTap: function(event) {
  135. var self = this;
  136. var target = event.target;
  137. if (target.classList.contains(CLASS_ACTION_PREVIOUS) || target.classList.contains(CLASS_ACTION_NEXT)) {
  138. self[target.classList.contains(CLASS_ACTION_PREVIOUS) ? 'prevItem' : 'nextItem']();
  139. event.stopPropagation();
  140. }
  141. },
  142. _initEvent: function(detach) {
  143. var self = this;
  144. self._super(detach);
  145. var action = detach ? 'removeEventListener' : 'addEventListener';
  146. self.wrapper[action]('slide', this);
  147. self.wrapper[action]($.eventName('shown', 'tab'), this);
  148. },
  149. handleEvent: function(e) {
  150. this._super(e);
  151. switch (e.type) {
  152. case 'slide':
  153. this._handleSlide(e);
  154. break;
  155. case $.eventName('shown', 'tab'):
  156. if (~this.snaps.indexOf(e.target)) { //避免嵌套监听错误的tab show
  157. this._handleTabShow(e);
  158. }
  159. break;
  160. }
  161. },
  162. _scrollend: function(e) {
  163. this._super(e);
  164. this._triggerSlide(e);
  165. },
  166. _drag: function(e) {
  167. this._super(e);
  168. var direction = e.detail.direction;
  169. if (direction === 'left' || direction === 'right') {
  170. //拖拽期间取消定时
  171. var slidershowTimer = this.wrapper.getAttribute('data-slidershowTimer');
  172. slidershowTimer && window.clearTimeout(slidershowTimer);
  173. e.stopPropagation();
  174. }
  175. },
  176. _initTimer: function() {
  177. var self = this;
  178. var slider = self.wrapper;
  179. var interval = self.options.interval;
  180. var slidershowTimer = slider.getAttribute('data-slidershowTimer');
  181. slidershowTimer && window.clearTimeout(slidershowTimer);
  182. if (interval) {
  183. slidershowTimer = window.setTimeout(function() {
  184. if (!slider) {
  185. return;
  186. }
  187. //仅slider显示状态进行自动轮播
  188. if (!!(slider.offsetWidth || slider.offsetHeight)) {
  189. self.nextItem(true);
  190. //下一个
  191. }
  192. self._initTimer();
  193. }, interval);
  194. slider.setAttribute('data-slidershowTimer', slidershowTimer);
  195. }
  196. },
  197. _fixedSlideNumber: function(page) {
  198. page = page || this.currentPage;
  199. var slideNumber = page.pageX;
  200. if (this.loop) {
  201. if (page.pageX === 0) {
  202. slideNumber = this.itemLength - 3;
  203. } else if (page.pageX === (this.itemLength - 1)) {
  204. slideNumber = 0;
  205. } else {
  206. slideNumber = page.pageX - 1;
  207. }
  208. }
  209. return slideNumber;
  210. },
  211. _reLayout: function() {
  212. this.hasHorizontalScroll = true;
  213. this.loop = this.scroller.classList.contains(CLASS_SLIDER_LOOP);
  214. this._super();
  215. },
  216. _getScroll: function() {
  217. var result = $.parseTranslateMatrix($.getStyles(this.scroller, 'webkitTransform'));
  218. return result ? result.x : 0;
  219. },
  220. _transitionEnd: function(e) {
  221. if (e.target !== this.scroller || !this.isInTransition) {
  222. return;
  223. }
  224. this._transitionTime();
  225. this.isInTransition = false;
  226. $.trigger(this.wrapper, 'scrollend', this);
  227. },
  228. _flick: function(e) {
  229. if (!this.moved) { //无moved
  230. return;
  231. }
  232. var detail = e.detail;
  233. var direction = detail.direction;
  234. this._clearRequestAnimationFrame();
  235. this.isInTransition = true;
  236. // if (direction === 'up' || direction === 'down') {
  237. // this.resetPosition(this.options.bounceTime);
  238. // return;
  239. // }
  240. if (e.type === 'flick') {
  241. if (detail.deltaTime < 200) { //flick,太容易触发,额外校验一下deltaTime
  242. this.x = this._getPage((this.slideNumber + (direction === 'right' ? -1 : 1)), true).x;
  243. }
  244. this.resetPosition(this.options.bounceTime);
  245. } else if (e.type === 'dragend' && !detail.flick) {
  246. this.resetPosition(this.options.bounceTime);
  247. }
  248. e.stopPropagation();
  249. },
  250. _initSnap: function() {
  251. this.scrollerWidth = this.itemLength * this.scrollerWidth;
  252. this.maxScrollX = Math.min(this.wrapperWidth - this.scrollerWidth, 0);
  253. this._super();
  254. if (!this.currentPage.x) {
  255. //当slider处于隐藏状态时,导致snap计算是错误的,临时先这么判断一下,后续要考虑解决所有scroll在隐藏状态下初始化属性不正确的问题
  256. var currentPage = this.pages[this.loop ? 1 : 0];
  257. currentPage = currentPage || this.pages[0];
  258. if (!currentPage) {
  259. return;
  260. }
  261. this.currentPage = currentPage[0];
  262. this.slideNumber = 0;
  263. this.lastSlideNumber = typeof this.lastSlideNumber === 'undefined' ? 0 : this.lastSlideNumber;
  264. } else {
  265. this.slideNumber = this._fixedSlideNumber();
  266. this.lastSlideNumber = typeof this.lastSlideNumber === 'undefined' ? this.slideNumber : this.lastSlideNumber;
  267. }
  268. this.options.startX = this.currentPage.x || 0;
  269. },
  270. _getSnapX: function(offsetLeft) {
  271. return Math.max(-offsetLeft, this.maxScrollX);
  272. },
  273. _getPage: function(slideNumber, isFlick) {
  274. if (this.loop) {
  275. if (slideNumber > (this.itemLength - (isFlick ? 2 : 3))) {
  276. slideNumber = 1;
  277. time = 0;
  278. } else if (slideNumber < (isFlick ? -1 : 0)) {
  279. slideNumber = this.itemLength - 2;
  280. time = 0;
  281. } else {
  282. slideNumber += 1;
  283. }
  284. } else {
  285. if (!isFlick) {
  286. if (slideNumber > (this.itemLength - 1)) {
  287. slideNumber = 0;
  288. time = 0;
  289. } else if (slideNumber < 0) {
  290. slideNumber = this.itemLength - 1;
  291. time = 0;
  292. }
  293. }
  294. slideNumber = Math.min(Math.max(0, slideNumber), this.itemLength - 1);
  295. }
  296. return this.pages[slideNumber][0];
  297. },
  298. _gotoItem: function(slideNumber, time) {
  299. this.currentPage = this._getPage(slideNumber, true); //此处传true。可保证程序切换时,动画与人手操作一致(第一张,最后一张的切换动画)
  300. this.scrollTo(this.currentPage.x, 0, time, this.options.scrollEasing);
  301. if (time === 0) {
  302. $.trigger(this.wrapper, 'scrollend', this);
  303. }
  304. },
  305. //API
  306. setTranslate: function(x, y) {
  307. this._super(x, y);
  308. var progressBar = this.progressBar;
  309. if (progressBar) {
  310. this.progressBarStyle.webkitTransform = this._getTranslateStr((-x * (this.progressBarWidth / this.wrapperWidth)), 0);
  311. }
  312. },
  313. resetPosition: function(time) {
  314. time = time || 0;
  315. if (this.x > 0) {
  316. this.x = 0;
  317. } else if (this.x < this.maxScrollX) {
  318. this.x = this.maxScrollX;
  319. }
  320. this.currentPage = this._nearestSnap(this.x);
  321. this.scrollTo(this.currentPage.x, 0, time, this.options.scrollEasing);
  322. return true;
  323. },
  324. gotoItem: function(slideNumber, time) {
  325. this._gotoItem(slideNumber, typeof time === 'undefined' ? this.options.scrollTime : time);
  326. },
  327. nextItem: function() {
  328. this._gotoItem(this.slideNumber + 1, this.options.scrollTime);
  329. },
  330. prevItem: function() {
  331. this._gotoItem(this.slideNumber - 1, this.options.scrollTime);
  332. },
  333. getSlideNumber: function() {
  334. return this.slideNumber || 0;
  335. },
  336. _reInit: function() {
  337. var groups = this.wrapper.querySelectorAll('.' + CLASS_SLIDER_GROUP);
  338. for (var i = 0, len = groups.length; i < len; i++) {
  339. if (groups[i].parentNode === this.wrapper) {
  340. this.scroller = groups[i];
  341. break;
  342. }
  343. }
  344. this.scrollerStyle = this.scroller && this.scroller.style;
  345. if (this.progressBar) {
  346. this.progressBarWidth = this.progressBar.offsetWidth;
  347. this.progressBarStyle = this.progressBar.style;
  348. }
  349. },
  350. refresh: function(options) {
  351. if (options) {
  352. $.extend(this.options, options);
  353. this._super();
  354. this._initTimer();
  355. } else {
  356. this._super();
  357. }
  358. },
  359. destroy: function() {
  360. this._initEvent(true); //detach
  361. delete $.data[this.wrapper.getAttribute('data-slider')];
  362. this.wrapper.setAttribute('data-slider', '');
  363. }
  364. });
  365. $.fn.slider = function(options) {
  366. var slider = null;
  367. this.each(function() {
  368. var sliderElement = this;
  369. if (!this.classList.contains(CLASS_SLIDER)) {
  370. sliderElement = this.querySelector('.' + CLASS_SLIDER);
  371. }
  372. if (sliderElement && sliderElement.querySelector(SELECTOR_SLIDER_ITEM)) {
  373. var id = sliderElement.getAttribute('data-slider');
  374. if (!id) {
  375. id = ++$.uuid;
  376. $.data[id] = slider = new Slider(sliderElement, options);
  377. sliderElement.setAttribute('data-slider', id);
  378. } else {
  379. slider = $.data[id];
  380. if (slider && options) {
  381. slider.refresh(options);
  382. }
  383. }
  384. }
  385. });
  386. return slider;
  387. };
  388. $.ready(function() {
  389. // setTimeout(function() {
  390. $($.classSelector('.slider')).slider();
  391. $($.classSelector('.scroll-wrapper.slider-indicator.segmented-control')).scroll({
  392. scrollY: false,
  393. scrollX: true,
  394. indicators: false,
  395. snap: $.classSelector('.control-item')
  396. });
  397. // }, 500); //临时处理slider宽度计算不正确的问题(初步确认是scrollbar导致的)
  398. });
  399. })(mui, window);