pagination.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. import $ from '../../shared/dom.js';
  2. import classesToSelector from '../../shared/classes-to-selector.js';
  3. import createElementIfNotDefined from '../../shared/create-element-if-not-defined.js';
  4. export default function Pagination(_ref) {
  5. let {
  6. swiper,
  7. extendParams,
  8. on,
  9. emit
  10. } = _ref;
  11. const pfx = 'swiper-pagination';
  12. extendParams({
  13. pagination: {
  14. el: null,
  15. bulletElement: 'span',
  16. clickable: false,
  17. hideOnClick: false,
  18. renderBullet: null,
  19. renderProgressbar: null,
  20. renderFraction: null,
  21. renderCustom: null,
  22. progressbarOpposite: false,
  23. type: 'bullets',
  24. // 'bullets' or 'progressbar' or 'fraction' or 'custom'
  25. dynamicBullets: false,
  26. dynamicMainBullets: 1,
  27. formatFractionCurrent: number => number,
  28. formatFractionTotal: number => number,
  29. bulletClass: `${pfx}-bullet`,
  30. bulletActiveClass: `${pfx}-bullet-active`,
  31. modifierClass: `${pfx}-`,
  32. currentClass: `${pfx}-current`,
  33. totalClass: `${pfx}-total`,
  34. hiddenClass: `${pfx}-hidden`,
  35. progressbarFillClass: `${pfx}-progressbar-fill`,
  36. progressbarOppositeClass: `${pfx}-progressbar-opposite`,
  37. clickableClass: `${pfx}-clickable`,
  38. lockClass: `${pfx}-lock`,
  39. horizontalClass: `${pfx}-horizontal`,
  40. verticalClass: `${pfx}-vertical`
  41. }
  42. });
  43. swiper.pagination = {
  44. el: null,
  45. $el: null,
  46. bullets: []
  47. };
  48. let bulletSize;
  49. let dynamicBulletIndex = 0;
  50. function isPaginationDisabled() {
  51. return !swiper.params.pagination.el || !swiper.pagination.el || !swiper.pagination.$el || swiper.pagination.$el.length === 0;
  52. }
  53. function setSideBullets($bulletEl, position) {
  54. const {
  55. bulletActiveClass
  56. } = swiper.params.pagination;
  57. $bulletEl[position]().addClass(`${bulletActiveClass}-${position}`)[position]().addClass(`${bulletActiveClass}-${position}-${position}`);
  58. }
  59. function update() {
  60. // Render || Update Pagination bullets/items
  61. const rtl = swiper.rtl;
  62. const params = swiper.params.pagination;
  63. if (isPaginationDisabled()) return;
  64. const slidesLength = swiper.virtual && swiper.params.virtual.enabled ? swiper.virtual.slides.length : swiper.slides.length;
  65. const $el = swiper.pagination.$el; // Current/Total
  66. let current;
  67. const total = swiper.params.loop ? Math.ceil((slidesLength - swiper.loopedSlides * 2) / swiper.params.slidesPerGroup) : swiper.snapGrid.length;
  68. if (swiper.params.loop) {
  69. current = Math.ceil((swiper.activeIndex - swiper.loopedSlides) / swiper.params.slidesPerGroup);
  70. if (current > slidesLength - 1 - swiper.loopedSlides * 2) {
  71. current -= slidesLength - swiper.loopedSlides * 2;
  72. }
  73. if (current > total - 1) current -= total;
  74. if (current < 0 && swiper.params.paginationType !== 'bullets') current = total + current;
  75. } else if (typeof swiper.snapIndex !== 'undefined') {
  76. current = swiper.snapIndex;
  77. } else {
  78. current = swiper.activeIndex || 0;
  79. } // Types
  80. if (params.type === 'bullets' && swiper.pagination.bullets && swiper.pagination.bullets.length > 0) {
  81. const bullets = swiper.pagination.bullets;
  82. let firstIndex;
  83. let lastIndex;
  84. let midIndex;
  85. if (params.dynamicBullets) {
  86. bulletSize = bullets.eq(0)[swiper.isHorizontal() ? 'outerWidth' : 'outerHeight'](true);
  87. $el.css(swiper.isHorizontal() ? 'width' : 'height', `${bulletSize * (params.dynamicMainBullets + 4)}px`);
  88. if (params.dynamicMainBullets > 1 && swiper.previousIndex !== undefined) {
  89. dynamicBulletIndex += current - (swiper.previousIndex - swiper.loopedSlides || 0);
  90. if (dynamicBulletIndex > params.dynamicMainBullets - 1) {
  91. dynamicBulletIndex = params.dynamicMainBullets - 1;
  92. } else if (dynamicBulletIndex < 0) {
  93. dynamicBulletIndex = 0;
  94. }
  95. }
  96. firstIndex = Math.max(current - dynamicBulletIndex, 0);
  97. lastIndex = firstIndex + (Math.min(bullets.length, params.dynamicMainBullets) - 1);
  98. midIndex = (lastIndex + firstIndex) / 2;
  99. }
  100. bullets.removeClass(['', '-next', '-next-next', '-prev', '-prev-prev', '-main'].map(suffix => `${params.bulletActiveClass}${suffix}`).join(' '));
  101. if ($el.length > 1) {
  102. bullets.each(bullet => {
  103. const $bullet = $(bullet);
  104. const bulletIndex = $bullet.index();
  105. if (bulletIndex === current) {
  106. $bullet.addClass(params.bulletActiveClass);
  107. }
  108. if (params.dynamicBullets) {
  109. if (bulletIndex >= firstIndex && bulletIndex <= lastIndex) {
  110. $bullet.addClass(`${params.bulletActiveClass}-main`);
  111. }
  112. if (bulletIndex === firstIndex) {
  113. setSideBullets($bullet, 'prev');
  114. }
  115. if (bulletIndex === lastIndex) {
  116. setSideBullets($bullet, 'next');
  117. }
  118. }
  119. });
  120. } else {
  121. const $bullet = bullets.eq(current);
  122. const bulletIndex = $bullet.index();
  123. $bullet.addClass(params.bulletActiveClass);
  124. if (params.dynamicBullets) {
  125. const $firstDisplayedBullet = bullets.eq(firstIndex);
  126. const $lastDisplayedBullet = bullets.eq(lastIndex);
  127. for (let i = firstIndex; i <= lastIndex; i += 1) {
  128. bullets.eq(i).addClass(`${params.bulletActiveClass}-main`);
  129. }
  130. if (swiper.params.loop) {
  131. if (bulletIndex >= bullets.length) {
  132. for (let i = params.dynamicMainBullets; i >= 0; i -= 1) {
  133. bullets.eq(bullets.length - i).addClass(`${params.bulletActiveClass}-main`);
  134. }
  135. bullets.eq(bullets.length - params.dynamicMainBullets - 1).addClass(`${params.bulletActiveClass}-prev`);
  136. } else {
  137. setSideBullets($firstDisplayedBullet, 'prev');
  138. setSideBullets($lastDisplayedBullet, 'next');
  139. }
  140. } else {
  141. setSideBullets($firstDisplayedBullet, 'prev');
  142. setSideBullets($lastDisplayedBullet, 'next');
  143. }
  144. }
  145. }
  146. if (params.dynamicBullets) {
  147. const dynamicBulletsLength = Math.min(bullets.length, params.dynamicMainBullets + 4);
  148. const bulletsOffset = (bulletSize * dynamicBulletsLength - bulletSize) / 2 - midIndex * bulletSize;
  149. const offsetProp = rtl ? 'right' : 'left';
  150. bullets.css(swiper.isHorizontal() ? offsetProp : 'top', `${bulletsOffset}px`);
  151. }
  152. }
  153. if (params.type === 'fraction') {
  154. $el.find(classesToSelector(params.currentClass)).text(params.formatFractionCurrent(current + 1));
  155. $el.find(classesToSelector(params.totalClass)).text(params.formatFractionTotal(total));
  156. }
  157. if (params.type === 'progressbar') {
  158. let progressbarDirection;
  159. if (params.progressbarOpposite) {
  160. progressbarDirection = swiper.isHorizontal() ? 'vertical' : 'horizontal';
  161. } else {
  162. progressbarDirection = swiper.isHorizontal() ? 'horizontal' : 'vertical';
  163. }
  164. const scale = (current + 1) / total;
  165. let scaleX = 1;
  166. let scaleY = 1;
  167. if (progressbarDirection === 'horizontal') {
  168. scaleX = scale;
  169. } else {
  170. scaleY = scale;
  171. }
  172. $el.find(classesToSelector(params.progressbarFillClass)).transform(`translate3d(0,0,0) scaleX(${scaleX}) scaleY(${scaleY})`).transition(swiper.params.speed);
  173. }
  174. if (params.type === 'custom' && params.renderCustom) {
  175. $el.html(params.renderCustom(swiper, current + 1, total));
  176. emit('paginationRender', $el[0]);
  177. } else {
  178. emit('paginationUpdate', $el[0]);
  179. }
  180. if (swiper.params.watchOverflow && swiper.enabled) {
  181. $el[swiper.isLocked ? 'addClass' : 'removeClass'](params.lockClass);
  182. }
  183. }
  184. function render() {
  185. // Render Container
  186. const params = swiper.params.pagination;
  187. if (isPaginationDisabled()) return;
  188. const slidesLength = swiper.virtual && swiper.params.virtual.enabled ? swiper.virtual.slides.length : swiper.slides.length;
  189. const $el = swiper.pagination.$el;
  190. let paginationHTML = '';
  191. if (params.type === 'bullets') {
  192. let numberOfBullets = swiper.params.loop ? Math.ceil((slidesLength - swiper.loopedSlides * 2) / swiper.params.slidesPerGroup) : swiper.snapGrid.length;
  193. if (swiper.params.freeMode && swiper.params.freeMode.enabled && !swiper.params.loop && numberOfBullets > slidesLength) {
  194. numberOfBullets = slidesLength;
  195. }
  196. for (let i = 0; i < numberOfBullets; i += 1) {
  197. if (params.renderBullet) {
  198. paginationHTML += params.renderBullet.call(swiper, i, params.bulletClass);
  199. } else {
  200. paginationHTML += `<${params.bulletElement} class="${params.bulletClass}"></${params.bulletElement}>`;
  201. }
  202. }
  203. $el.html(paginationHTML);
  204. swiper.pagination.bullets = $el.find(classesToSelector(params.bulletClass));
  205. }
  206. if (params.type === 'fraction') {
  207. if (params.renderFraction) {
  208. paginationHTML = params.renderFraction.call(swiper, params.currentClass, params.totalClass);
  209. } else {
  210. paginationHTML = `<span class="${params.currentClass}"></span>` + ' / ' + `<span class="${params.totalClass}"></span>`;
  211. }
  212. $el.html(paginationHTML);
  213. }
  214. if (params.type === 'progressbar') {
  215. if (params.renderProgressbar) {
  216. paginationHTML = params.renderProgressbar.call(swiper, params.progressbarFillClass);
  217. } else {
  218. paginationHTML = `<span class="${params.progressbarFillClass}"></span>`;
  219. }
  220. $el.html(paginationHTML);
  221. }
  222. if (params.type !== 'custom') {
  223. emit('paginationRender', swiper.pagination.$el[0]);
  224. }
  225. }
  226. function init() {
  227. swiper.params.pagination = createElementIfNotDefined(swiper, swiper.originalParams.pagination, swiper.params.pagination, {
  228. el: 'swiper-pagination'
  229. });
  230. const params = swiper.params.pagination;
  231. if (!params.el) return;
  232. let $el = $(params.el);
  233. if ($el.length === 0) return;
  234. if (swiper.params.uniqueNavElements && typeof params.el === 'string' && $el.length > 1) {
  235. $el = swiper.$el.find(params.el); // check if it belongs to another nested Swiper
  236. if ($el.length > 1) {
  237. $el = $el.filter(el => {
  238. if ($(el).parents('.swiper')[0] !== swiper.el) return false;
  239. return true;
  240. });
  241. }
  242. }
  243. if (params.type === 'bullets' && params.clickable) {
  244. $el.addClass(params.clickableClass);
  245. }
  246. $el.addClass(params.modifierClass + params.type);
  247. $el.addClass(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass);
  248. if (params.type === 'bullets' && params.dynamicBullets) {
  249. $el.addClass(`${params.modifierClass}${params.type}-dynamic`);
  250. dynamicBulletIndex = 0;
  251. if (params.dynamicMainBullets < 1) {
  252. params.dynamicMainBullets = 1;
  253. }
  254. }
  255. if (params.type === 'progressbar' && params.progressbarOpposite) {
  256. $el.addClass(params.progressbarOppositeClass);
  257. }
  258. if (params.clickable) {
  259. $el.on('click', classesToSelector(params.bulletClass), function onClick(e) {
  260. e.preventDefault();
  261. let index = $(this).index() * swiper.params.slidesPerGroup;
  262. if (swiper.params.loop) index += swiper.loopedSlides;
  263. swiper.slideTo(index);
  264. });
  265. }
  266. Object.assign(swiper.pagination, {
  267. $el,
  268. el: $el[0]
  269. });
  270. if (!swiper.enabled) {
  271. $el.addClass(params.lockClass);
  272. }
  273. }
  274. function destroy() {
  275. const params = swiper.params.pagination;
  276. if (isPaginationDisabled()) return;
  277. const $el = swiper.pagination.$el;
  278. $el.removeClass(params.hiddenClass);
  279. $el.removeClass(params.modifierClass + params.type);
  280. $el.removeClass(swiper.isHorizontal() ? params.horizontalClass : params.verticalClass);
  281. if (swiper.pagination.bullets && swiper.pagination.bullets.removeClass) swiper.pagination.bullets.removeClass(params.bulletActiveClass);
  282. if (params.clickable) {
  283. $el.off('click', classesToSelector(params.bulletClass));
  284. }
  285. }
  286. on('init', () => {
  287. init();
  288. render();
  289. update();
  290. });
  291. on('activeIndexChange', () => {
  292. if (swiper.params.loop) {
  293. update();
  294. } else if (typeof swiper.snapIndex === 'undefined') {
  295. update();
  296. }
  297. });
  298. on('snapIndexChange', () => {
  299. if (!swiper.params.loop) {
  300. update();
  301. }
  302. });
  303. on('slidesLengthChange', () => {
  304. if (swiper.params.loop) {
  305. render();
  306. update();
  307. }
  308. });
  309. on('snapGridLengthChange', () => {
  310. if (!swiper.params.loop) {
  311. render();
  312. update();
  313. }
  314. });
  315. on('destroy', () => {
  316. destroy();
  317. });
  318. on('enable disable', () => {
  319. const {
  320. $el
  321. } = swiper.pagination;
  322. if ($el) {
  323. $el[swiper.enabled ? 'removeClass' : 'addClass'](swiper.params.pagination.lockClass);
  324. }
  325. });
  326. on('lock unlock', () => {
  327. update();
  328. });
  329. on('click', (_s, e) => {
  330. const targetEl = e.target;
  331. const {
  332. $el
  333. } = swiper.pagination;
  334. if (swiper.params.pagination.el && swiper.params.pagination.hideOnClick && $el.length > 0 && !$(targetEl).hasClass(swiper.params.pagination.bulletClass)) {
  335. if (swiper.navigation && (swiper.navigation.nextEl && targetEl === swiper.navigation.nextEl || swiper.navigation.prevEl && targetEl === swiper.navigation.prevEl)) return;
  336. const isHidden = $el.hasClass(swiper.params.pagination.hiddenClass);
  337. if (isHidden === true) {
  338. emit('paginationShow');
  339. } else {
  340. emit('paginationHide');
  341. }
  342. $el.toggleClass(swiper.params.pagination.hiddenClass);
  343. }
  344. });
  345. Object.assign(swiper.pagination, {
  346. render,
  347. update,
  348. init,
  349. destroy
  350. });
  351. }