swiper.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. import { h, ref, onMounted, onUpdated, onBeforeUnmount, watch, nextTick } from 'vue';
  2. import { getParams } from './get-params.js';
  3. import { initSwiper, mountSwiper } from './init-swiper.js';
  4. import { needsScrollbar, needsNavigation, needsPagination, uniqueClasses, extend } from './utils.js';
  5. import { renderLoop, calcLoopedSlides } from './loop.js';
  6. import { getChangedParams } from './get-changed-params.js';
  7. import { getChildren } from './get-children.js';
  8. import { updateSwiper } from './update-swiper.js';
  9. import { renderVirtual, updateOnVirtualData } from './virtual.js';
  10. const Swiper = {
  11. name: 'Swiper',
  12. props: {
  13. tag: {
  14. type: String,
  15. default: 'div'
  16. },
  17. wrapperTag: {
  18. type: String,
  19. default: 'div'
  20. },
  21. modules: {
  22. type: Array,
  23. default: undefined
  24. },
  25. init: {
  26. type: Boolean,
  27. default: undefined
  28. },
  29. direction: {
  30. type: String,
  31. default: undefined
  32. },
  33. touchEventsTarget: {
  34. type: String,
  35. default: undefined
  36. },
  37. initialSlide: {
  38. type: Number,
  39. default: undefined
  40. },
  41. speed: {
  42. type: Number,
  43. default: undefined
  44. },
  45. cssMode: {
  46. type: Boolean,
  47. default: undefined
  48. },
  49. updateOnWindowResize: {
  50. type: Boolean,
  51. default: undefined
  52. },
  53. resizeObserver: {
  54. type: Boolean,
  55. default: undefined
  56. },
  57. nested: {
  58. type: Boolean,
  59. default: undefined
  60. },
  61. focusableElements: {
  62. type: String,
  63. default: undefined
  64. },
  65. width: {
  66. type: Number,
  67. default: undefined
  68. },
  69. height: {
  70. type: Number,
  71. default: undefined
  72. },
  73. preventInteractionOnTransition: {
  74. type: Boolean,
  75. default: undefined
  76. },
  77. userAgent: {
  78. type: String,
  79. default: undefined
  80. },
  81. url: {
  82. type: String,
  83. default: undefined
  84. },
  85. edgeSwipeDetection: {
  86. type: [Boolean, String],
  87. default: undefined
  88. },
  89. edgeSwipeThreshold: {
  90. type: Number,
  91. default: undefined
  92. },
  93. autoHeight: {
  94. type: Boolean,
  95. default: undefined
  96. },
  97. setWrapperSize: {
  98. type: Boolean,
  99. default: undefined
  100. },
  101. virtualTranslate: {
  102. type: Boolean,
  103. default: undefined
  104. },
  105. effect: {
  106. type: String,
  107. default: undefined
  108. },
  109. breakpoints: {
  110. type: Object,
  111. default: undefined
  112. },
  113. spaceBetween: {
  114. type: Number,
  115. default: undefined
  116. },
  117. slidesPerView: {
  118. type: [Number, String],
  119. default: undefined
  120. },
  121. slidesPerGroup: {
  122. type: Number,
  123. default: undefined
  124. },
  125. slidesPerGroupSkip: {
  126. type: Number,
  127. default: undefined
  128. },
  129. slidesPerGroupAuto: {
  130. type: Boolean,
  131. default: undefined
  132. },
  133. centeredSlides: {
  134. type: Boolean,
  135. default: undefined
  136. },
  137. centeredSlidesBounds: {
  138. type: Boolean,
  139. default: undefined
  140. },
  141. slidesOffsetBefore: {
  142. type: Number,
  143. default: undefined
  144. },
  145. slidesOffsetAfter: {
  146. type: Number,
  147. default: undefined
  148. },
  149. normalizeSlideIndex: {
  150. type: Boolean,
  151. default: undefined
  152. },
  153. centerInsufficientSlides: {
  154. type: Boolean,
  155. default: undefined
  156. },
  157. watchOverflow: {
  158. type: Boolean,
  159. default: undefined
  160. },
  161. roundLengths: {
  162. type: Boolean,
  163. default: undefined
  164. },
  165. touchRatio: {
  166. type: Number,
  167. default: undefined
  168. },
  169. touchAngle: {
  170. type: Number,
  171. default: undefined
  172. },
  173. simulateTouch: {
  174. type: Boolean,
  175. default: undefined
  176. },
  177. shortSwipes: {
  178. type: Boolean,
  179. default: undefined
  180. },
  181. longSwipes: {
  182. type: Boolean,
  183. default: undefined
  184. },
  185. longSwipesRatio: {
  186. type: Number,
  187. default: undefined
  188. },
  189. longSwipesMs: {
  190. type: Number,
  191. default: undefined
  192. },
  193. followFinger: {
  194. type: Boolean,
  195. default: undefined
  196. },
  197. allowTouchMove: {
  198. type: Boolean,
  199. default: undefined
  200. },
  201. threshold: {
  202. type: Number,
  203. default: undefined
  204. },
  205. touchMoveStopPropagation: {
  206. type: Boolean,
  207. default: undefined
  208. },
  209. touchStartPreventDefault: {
  210. type: Boolean,
  211. default: undefined
  212. },
  213. touchStartForcePreventDefault: {
  214. type: Boolean,
  215. default: undefined
  216. },
  217. touchReleaseOnEdges: {
  218. type: Boolean,
  219. default: undefined
  220. },
  221. uniqueNavElements: {
  222. type: Boolean,
  223. default: undefined
  224. },
  225. resistance: {
  226. type: Boolean,
  227. default: undefined
  228. },
  229. resistanceRatio: {
  230. type: Number,
  231. default: undefined
  232. },
  233. watchSlidesProgress: {
  234. type: Boolean,
  235. default: undefined
  236. },
  237. grabCursor: {
  238. type: Boolean,
  239. default: undefined
  240. },
  241. preventClicks: {
  242. type: Boolean,
  243. default: undefined
  244. },
  245. preventClicksPropagation: {
  246. type: Boolean,
  247. default: undefined
  248. },
  249. slideToClickedSlide: {
  250. type: Boolean,
  251. default: undefined
  252. },
  253. preloadImages: {
  254. type: Boolean,
  255. default: undefined
  256. },
  257. updateOnImagesReady: {
  258. type: Boolean,
  259. default: undefined
  260. },
  261. loop: {
  262. type: Boolean,
  263. default: undefined
  264. },
  265. loopAdditionalSlides: {
  266. type: Number,
  267. default: undefined
  268. },
  269. loopedSlides: {
  270. type: Number,
  271. default: undefined
  272. },
  273. loopFillGroupWithBlank: {
  274. type: Boolean,
  275. default: undefined
  276. },
  277. loopPreventsSlide: {
  278. type: Boolean,
  279. default: undefined
  280. },
  281. allowSlidePrev: {
  282. type: Boolean,
  283. default: undefined
  284. },
  285. allowSlideNext: {
  286. type: Boolean,
  287. default: undefined
  288. },
  289. swipeHandler: {
  290. type: Boolean,
  291. default: undefined
  292. },
  293. noSwiping: {
  294. type: Boolean,
  295. default: undefined
  296. },
  297. noSwipingClass: {
  298. type: String,
  299. default: undefined
  300. },
  301. noSwipingSelector: {
  302. type: String,
  303. default: undefined
  304. },
  305. passiveListeners: {
  306. type: Boolean,
  307. default: undefined
  308. },
  309. containerModifierClass: {
  310. type: String,
  311. default: undefined
  312. },
  313. slideClass: {
  314. type: String,
  315. default: undefined
  316. },
  317. slideBlankClass: {
  318. type: String,
  319. default: undefined
  320. },
  321. slideActiveClass: {
  322. type: String,
  323. default: undefined
  324. },
  325. slideDuplicateActiveClass: {
  326. type: String,
  327. default: undefined
  328. },
  329. slideVisibleClass: {
  330. type: String,
  331. default: undefined
  332. },
  333. slideDuplicateClass: {
  334. type: String,
  335. default: undefined
  336. },
  337. slideNextClass: {
  338. type: String,
  339. default: undefined
  340. },
  341. slideDuplicateNextClass: {
  342. type: String,
  343. default: undefined
  344. },
  345. slidePrevClass: {
  346. type: String,
  347. default: undefined
  348. },
  349. slideDuplicatePrevClass: {
  350. type: String,
  351. default: undefined
  352. },
  353. wrapperClass: {
  354. type: String,
  355. default: undefined
  356. },
  357. runCallbacksOnInit: {
  358. type: Boolean,
  359. default: undefined
  360. },
  361. observer: {
  362. type: Boolean,
  363. default: undefined
  364. },
  365. observeParents: {
  366. type: Boolean,
  367. default: undefined
  368. },
  369. observeSlideChildren: {
  370. type: Boolean,
  371. default: undefined
  372. },
  373. a11y: {
  374. type: [Boolean, Object],
  375. default: undefined
  376. },
  377. autoplay: {
  378. type: [Boolean, Object],
  379. default: undefined
  380. },
  381. controller: {
  382. type: Object,
  383. default: undefined
  384. },
  385. coverflowEffect: {
  386. type: Object,
  387. default: undefined
  388. },
  389. cubeEffect: {
  390. type: Object,
  391. default: undefined
  392. },
  393. fadeEffect: {
  394. type: Object,
  395. default: undefined
  396. },
  397. flipEffect: {
  398. type: Object,
  399. default: undefined
  400. },
  401. creativeEffect: {
  402. type: Object,
  403. default: undefined
  404. },
  405. cardsEffect: {
  406. type: Object,
  407. default: undefined
  408. },
  409. hashNavigation: {
  410. type: [Boolean, Object],
  411. default: undefined
  412. },
  413. history: {
  414. type: [Boolean, Object],
  415. default: undefined
  416. },
  417. keyboard: {
  418. type: [Boolean, Object],
  419. default: undefined
  420. },
  421. lazy: {
  422. type: [Boolean, Object],
  423. default: undefined
  424. },
  425. mousewheel: {
  426. type: [Boolean, Object],
  427. default: undefined
  428. },
  429. navigation: {
  430. type: [Boolean, Object],
  431. default: undefined
  432. },
  433. pagination: {
  434. type: [Boolean, Object],
  435. default: undefined
  436. },
  437. parallax: {
  438. type: [Boolean, Object],
  439. default: undefined
  440. },
  441. scrollbar: {
  442. type: [Boolean, Object],
  443. default: undefined
  444. },
  445. thumbs: {
  446. type: Object,
  447. default: undefined
  448. },
  449. virtual: {
  450. type: [Boolean, Object],
  451. default: undefined
  452. },
  453. zoom: {
  454. type: [Boolean, Object],
  455. default: undefined
  456. },
  457. grid: {
  458. type: [Object],
  459. default: undefined
  460. },
  461. freeMode: {
  462. type: [Boolean, Object],
  463. default: undefined
  464. }
  465. },
  466. emits: ['_beforeBreakpoint', '_containerClasses', '_slideClass', '_slideClasses', '_swiper', 'activeIndexChange', 'afterInit', 'autoplay', 'autoplayStart', 'autoplayStop', 'beforeDestroy', 'beforeInit', 'beforeLoopFix', 'beforeResize', 'beforeSlideChangeStart', 'beforeTransitionStart', 'breakpoint', 'changeDirection', 'click', 'disable', 'doubleTap', 'doubleClick', 'destroy', 'enable', 'fromEdge', 'hashChange', 'hashSet', 'imagesReady', 'init', 'keyPress', 'lazyImageLoad', 'lazyImageReady', 'lock', 'loopFix', 'momentumBounce', 'navigationHide', 'navigationShow', 'observerUpdate', 'orientationchange', 'paginationHide', 'paginationRender', 'paginationShow', 'paginationUpdate', 'progress', 'reachBeginning', 'reachEnd', 'realIndexChange', 'resize', 'scroll', 'scrollbarDragEnd', 'scrollbarDragMove', 'scrollbarDragStart', 'setTransition', 'setTranslate', 'slideChange', 'slideChangeTransitionEnd', 'slideChangeTransitionStart', 'slideNextTransitionEnd', 'slideNextTransitionStart', 'slidePrevTransitionEnd', 'slidePrevTransitionStart', 'slideResetTransitionStart', 'slideResetTransitionEnd', 'sliderMove', 'sliderFirstMove', 'slidesLengthChange', 'slidesGridLengthChange', 'snapGridLengthChange', 'snapIndexChange', 'swiper', 'tap', 'toEdge', 'touchEnd', 'touchMove', 'touchMoveOpposite', 'touchStart', 'transitionEnd', 'transitionStart', 'unlock', 'update', 'zoomChange'],
  467. setup(props, {
  468. slots: originalSlots,
  469. emit
  470. }) {
  471. const {
  472. tag: Tag,
  473. wrapperTag: WrapperTag
  474. } = props;
  475. const containerClasses = ref('swiper');
  476. const virtualData = ref(null);
  477. const breakpointChanged = ref(false);
  478. const initializedRef = ref(false);
  479. const swiperElRef = ref(null);
  480. const swiperRef = ref(null);
  481. const oldPassedParamsRef = ref(null);
  482. const slidesRef = {
  483. value: []
  484. };
  485. const oldSlidesRef = {
  486. value: []
  487. };
  488. const nextElRef = ref(null);
  489. const prevElRef = ref(null);
  490. const paginationElRef = ref(null);
  491. const scrollbarElRef = ref(null);
  492. const {
  493. params: swiperParams,
  494. passedParams
  495. } = getParams(props);
  496. getChildren(originalSlots, slidesRef, oldSlidesRef);
  497. oldPassedParamsRef.value = passedParams;
  498. oldSlidesRef.value = slidesRef.value;
  499. const onBeforeBreakpoint = () => {
  500. getChildren(originalSlots, slidesRef, oldSlidesRef);
  501. breakpointChanged.value = true;
  502. };
  503. swiperParams.onAny = (event, ...args) => {
  504. emit(event, ...args);
  505. };
  506. Object.assign(swiperParams.on, {
  507. _beforeBreakpoint: onBeforeBreakpoint,
  508. _containerClasses(swiper, classes) {
  509. containerClasses.value = classes;
  510. }
  511. }); // init Swiper
  512. swiperRef.value = initSwiper(swiperParams);
  513. swiperRef.value.loopCreate = () => {};
  514. swiperRef.value.loopDestroy = () => {};
  515. if (swiperParams.loop) {
  516. swiperRef.value.loopedSlides = calcLoopedSlides(slidesRef.value, swiperParams);
  517. }
  518. if (swiperRef.value.virtual && swiperRef.value.params.virtual.enabled) {
  519. swiperRef.value.virtual.slides = slidesRef.value;
  520. const extendWith = {
  521. cache: false,
  522. slides: slidesRef.value,
  523. renderExternal: data => {
  524. virtualData.value = data;
  525. },
  526. renderExternalUpdate: false
  527. };
  528. extend(swiperRef.value.params.virtual, extendWith);
  529. extend(swiperRef.value.originalParams.virtual, extendWith);
  530. }
  531. onUpdated(() => {
  532. // set initialized flag
  533. if (!initializedRef.value && swiperRef.value) {
  534. swiperRef.value.emitSlidesClasses();
  535. initializedRef.value = true;
  536. } // watch for params change
  537. const {
  538. passedParams: newPassedParams
  539. } = getParams(props);
  540. const changedParams = getChangedParams(newPassedParams, oldPassedParamsRef.value, slidesRef.value, oldSlidesRef.value);
  541. oldPassedParamsRef.value = newPassedParams;
  542. if ((changedParams.length || breakpointChanged.value) && swiperRef.value && !swiperRef.value.destroyed) {
  543. updateSwiper({
  544. swiper: swiperRef.value,
  545. slides: slidesRef.value,
  546. passedParams: newPassedParams,
  547. changedParams,
  548. nextEl: nextElRef.value,
  549. prevEl: prevElRef.value,
  550. scrollbarEl: scrollbarElRef.value,
  551. paginationEl: paginationElRef.value
  552. });
  553. }
  554. breakpointChanged.value = false;
  555. }); // update on virtual update
  556. watch(virtualData, () => {
  557. nextTick(() => {
  558. updateOnVirtualData(swiperRef.value);
  559. });
  560. }); // mount swiper
  561. onMounted(() => {
  562. if (!swiperElRef.value) return;
  563. mountSwiper({
  564. el: swiperElRef.value,
  565. nextEl: nextElRef.value,
  566. prevEl: prevElRef.value,
  567. paginationEl: paginationElRef.value,
  568. scrollbarEl: scrollbarElRef.value,
  569. swiper: swiperRef.value
  570. }, swiperParams);
  571. emit('swiper', swiperRef.value);
  572. });
  573. onBeforeUnmount(() => {
  574. if (swiperRef.value && !swiperRef.value.destroyed) {
  575. swiperRef.value.destroy(true, false);
  576. }
  577. }); // bypass swiper instance to slides
  578. function renderSlides(slides) {
  579. if (swiperParams.virtual) {
  580. return renderVirtual(swiperRef, slides, virtualData.value);
  581. }
  582. if (!swiperParams.loop || swiperRef.value && swiperRef.value.destroyed) {
  583. slides.forEach(slide => {
  584. if (!slide.props) slide.props = {};
  585. slide.props.swiperRef = swiperRef;
  586. });
  587. return slides;
  588. }
  589. return renderLoop(swiperRef, slides, swiperParams);
  590. }
  591. return () => {
  592. const {
  593. slides,
  594. slots
  595. } = getChildren(originalSlots, slidesRef, oldSlidesRef);
  596. return h(Tag, {
  597. ref: swiperElRef,
  598. class: uniqueClasses(containerClasses.value)
  599. }, [slots['container-start'], needsNavigation(props) && [h('div', {
  600. ref: prevElRef,
  601. class: 'swiper-button-prev'
  602. }), h('div', {
  603. ref: nextElRef,
  604. class: 'swiper-button-next'
  605. })], needsScrollbar(props) && h('div', {
  606. ref: scrollbarElRef,
  607. class: 'swiper-scrollbar'
  608. }), needsPagination(props) && h('div', {
  609. ref: paginationElRef,
  610. class: 'swiper-pagination'
  611. }), h(WrapperTag, {
  612. class: 'swiper-wrapper'
  613. }, [slots['wrapper-start'], renderSlides(slides), slots['wrapper-end']]), slots['container-end']]);
  614. };
  615. }
  616. };
  617. export { Swiper };