pagination.js 13 KB

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