core.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /* eslint no-param-reassign: "off" */
  2. import { getDocument } from 'ssr-window';
  3. import $ from '../shared/dom.js';
  4. import { extend, now, deleteProps } from '../shared/utils.js';
  5. import { getSupport } from '../shared/get-support.js';
  6. import { getDevice } from '../shared/get-device.js';
  7. import { getBrowser } from '../shared/get-browser.js';
  8. import Resize from './modules/resize/resize.js';
  9. import Observer from './modules/observer/observer.js';
  10. import eventsEmitter from './events-emitter.js';
  11. import update from './update/index.js';
  12. import translate from './translate/index.js';
  13. import transition from './transition/index.js';
  14. import slide from './slide/index.js';
  15. import loop from './loop/index.js';
  16. import grabCursor from './grab-cursor/index.js';
  17. import events from './events/index.js';
  18. import breakpoints from './breakpoints/index.js';
  19. import classes from './classes/index.js';
  20. import images from './images/index.js';
  21. import checkOverflow from './check-overflow/index.js';
  22. import defaults from './defaults.js';
  23. import moduleExtendParams from './moduleExtendParams.js';
  24. const prototypes = {
  25. eventsEmitter,
  26. update,
  27. translate,
  28. transition,
  29. slide,
  30. loop,
  31. grabCursor,
  32. events,
  33. breakpoints,
  34. checkOverflow,
  35. classes,
  36. images
  37. };
  38. const extendedDefaults = {};
  39. class Swiper {
  40. constructor(...args) {
  41. let el;
  42. let params;
  43. if (args.length === 1 && args[0].constructor && Object.prototype.toString.call(args[0]).slice(8, -1) === 'Object') {
  44. params = args[0];
  45. } else {
  46. [el, params] = args;
  47. }
  48. if (!params) params = {};
  49. params = extend({}, params);
  50. if (el && !params.el) params.el = el;
  51. if (params.el && $(params.el).length > 1) {
  52. const swipers = [];
  53. $(params.el).each(containerEl => {
  54. const newParams = extend({}, params, {
  55. el: containerEl
  56. });
  57. swipers.push(new Swiper(newParams));
  58. });
  59. return swipers;
  60. } // Swiper Instance
  61. const swiper = this;
  62. swiper.__swiper__ = true;
  63. swiper.support = getSupport();
  64. swiper.device = getDevice({
  65. userAgent: params.userAgent
  66. });
  67. swiper.browser = getBrowser();
  68. swiper.eventsListeners = {};
  69. swiper.eventsAnyListeners = [];
  70. swiper.modules = [...swiper.__modules__];
  71. if (params.modules && Array.isArray(params.modules)) {
  72. swiper.modules.push(...params.modules);
  73. }
  74. const allModulesParams = {};
  75. swiper.modules.forEach(mod => {
  76. mod({
  77. swiper,
  78. extendParams: moduleExtendParams(params, allModulesParams),
  79. on: swiper.on.bind(swiper),
  80. once: swiper.once.bind(swiper),
  81. off: swiper.off.bind(swiper),
  82. emit: swiper.emit.bind(swiper)
  83. });
  84. }); // Extend defaults with modules params
  85. const swiperParams = extend({}, defaults, allModulesParams); // Extend defaults with passed params
  86. swiper.params = extend({}, swiperParams, extendedDefaults, params);
  87. swiper.originalParams = extend({}, swiper.params);
  88. swiper.passedParams = extend({}, params); // add event listeners
  89. if (swiper.params && swiper.params.on) {
  90. Object.keys(swiper.params.on).forEach(eventName => {
  91. swiper.on(eventName, swiper.params.on[eventName]);
  92. });
  93. }
  94. if (swiper.params && swiper.params.onAny) {
  95. swiper.onAny(swiper.params.onAny);
  96. } // Save Dom lib
  97. swiper.$ = $; // Extend Swiper
  98. Object.assign(swiper, {
  99. enabled: swiper.params.enabled,
  100. el,
  101. // Classes
  102. classNames: [],
  103. // Slides
  104. slides: $(),
  105. slidesGrid: [],
  106. snapGrid: [],
  107. slidesSizesGrid: [],
  108. // isDirection
  109. isHorizontal() {
  110. return swiper.params.direction === 'horizontal';
  111. },
  112. isVertical() {
  113. return swiper.params.direction === 'vertical';
  114. },
  115. // Indexes
  116. activeIndex: 0,
  117. realIndex: 0,
  118. //
  119. isBeginning: true,
  120. isEnd: false,
  121. // Props
  122. translate: 0,
  123. previousTranslate: 0,
  124. progress: 0,
  125. velocity: 0,
  126. animating: false,
  127. // Locks
  128. allowSlideNext: swiper.params.allowSlideNext,
  129. allowSlidePrev: swiper.params.allowSlidePrev,
  130. // Touch Events
  131. touchEvents: function touchEvents() {
  132. const touch = ['touchstart', 'touchmove', 'touchend', 'touchcancel'];
  133. const desktop = ['pointerdown', 'pointermove', 'pointerup'];
  134. swiper.touchEventsTouch = {
  135. start: touch[0],
  136. move: touch[1],
  137. end: touch[2],
  138. cancel: touch[3]
  139. };
  140. swiper.touchEventsDesktop = {
  141. start: desktop[0],
  142. move: desktop[1],
  143. end: desktop[2]
  144. };
  145. return swiper.support.touch || !swiper.params.simulateTouch ? swiper.touchEventsTouch : swiper.touchEventsDesktop;
  146. }(),
  147. touchEventsData: {
  148. isTouched: undefined,
  149. isMoved: undefined,
  150. allowTouchCallbacks: undefined,
  151. touchStartTime: undefined,
  152. isScrolling: undefined,
  153. currentTranslate: undefined,
  154. startTranslate: undefined,
  155. allowThresholdMove: undefined,
  156. // Form elements to match
  157. focusableElements: swiper.params.focusableElements,
  158. // Last click time
  159. lastClickTime: now(),
  160. clickTimeout: undefined,
  161. // Velocities
  162. velocities: [],
  163. allowMomentumBounce: undefined,
  164. isTouchEvent: undefined,
  165. startMoving: undefined
  166. },
  167. // Clicks
  168. allowClick: true,
  169. // Touches
  170. allowTouchMove: swiper.params.allowTouchMove,
  171. touches: {
  172. startX: 0,
  173. startY: 0,
  174. currentX: 0,
  175. currentY: 0,
  176. diff: 0
  177. },
  178. // Images
  179. imagesToLoad: [],
  180. imagesLoaded: 0
  181. });
  182. swiper.emit('_swiper'); // Init
  183. if (swiper.params.init) {
  184. swiper.init();
  185. } // Return app instance
  186. return swiper;
  187. }
  188. enable() {
  189. const swiper = this;
  190. if (swiper.enabled) return;
  191. swiper.enabled = true;
  192. if (swiper.params.grabCursor) {
  193. swiper.setGrabCursor();
  194. }
  195. swiper.emit('enable');
  196. }
  197. disable() {
  198. const swiper = this;
  199. if (!swiper.enabled) return;
  200. swiper.enabled = false;
  201. if (swiper.params.grabCursor) {
  202. swiper.unsetGrabCursor();
  203. }
  204. swiper.emit('disable');
  205. }
  206. setProgress(progress, speed) {
  207. const swiper = this;
  208. progress = Math.min(Math.max(progress, 0), 1);
  209. const min = swiper.minTranslate();
  210. const max = swiper.maxTranslate();
  211. const current = (max - min) * progress + min;
  212. swiper.translateTo(current, typeof speed === 'undefined' ? 0 : speed);
  213. swiper.updateActiveIndex();
  214. swiper.updateSlidesClasses();
  215. }
  216. emitContainerClasses() {
  217. const swiper = this;
  218. if (!swiper.params._emitClasses || !swiper.el) return;
  219. const cls = swiper.el.className.split(' ').filter(className => {
  220. return className.indexOf('swiper') === 0 || className.indexOf(swiper.params.containerModifierClass) === 0;
  221. });
  222. swiper.emit('_containerClasses', cls.join(' '));
  223. }
  224. getSlideClasses(slideEl) {
  225. const swiper = this;
  226. return slideEl.className.split(' ').filter(className => {
  227. return className.indexOf('swiper-slide') === 0 || className.indexOf(swiper.params.slideClass) === 0;
  228. }).join(' ');
  229. }
  230. emitSlidesClasses() {
  231. const swiper = this;
  232. if (!swiper.params._emitClasses || !swiper.el) return;
  233. const updates = [];
  234. swiper.slides.each(slideEl => {
  235. const classNames = swiper.getSlideClasses(slideEl);
  236. updates.push({
  237. slideEl,
  238. classNames
  239. });
  240. swiper.emit('_slideClass', slideEl, classNames);
  241. });
  242. swiper.emit('_slideClasses', updates);
  243. }
  244. slidesPerViewDynamic(view = 'current', exact = false) {
  245. const swiper = this;
  246. const {
  247. params,
  248. slides,
  249. slidesGrid,
  250. slidesSizesGrid,
  251. size: swiperSize,
  252. activeIndex
  253. } = swiper;
  254. let spv = 1;
  255. if (params.centeredSlides) {
  256. let slideSize = slides[activeIndex].swiperSlideSize;
  257. let breakLoop;
  258. for (let i = activeIndex + 1; i < slides.length; i += 1) {
  259. if (slides[i] && !breakLoop) {
  260. slideSize += slides[i].swiperSlideSize;
  261. spv += 1;
  262. if (slideSize > swiperSize) breakLoop = true;
  263. }
  264. }
  265. for (let i = activeIndex - 1; i >= 0; i -= 1) {
  266. if (slides[i] && !breakLoop) {
  267. slideSize += slides[i].swiperSlideSize;
  268. spv += 1;
  269. if (slideSize > swiperSize) breakLoop = true;
  270. }
  271. }
  272. } else {
  273. // eslint-disable-next-line
  274. if (view === 'current') {
  275. for (let i = activeIndex + 1; i < slides.length; i += 1) {
  276. const slideInView = exact ? slidesGrid[i] + slidesSizesGrid[i] - slidesGrid[activeIndex] < swiperSize : slidesGrid[i] - slidesGrid[activeIndex] < swiperSize;
  277. if (slideInView) {
  278. spv += 1;
  279. }
  280. }
  281. } else {
  282. // previous
  283. for (let i = activeIndex - 1; i >= 0; i -= 1) {
  284. const slideInView = slidesGrid[activeIndex] - slidesGrid[i] < swiperSize;
  285. if (slideInView) {
  286. spv += 1;
  287. }
  288. }
  289. }
  290. }
  291. return spv;
  292. }
  293. update() {
  294. const swiper = this;
  295. if (!swiper || swiper.destroyed) return;
  296. const {
  297. snapGrid,
  298. params
  299. } = swiper; // Breakpoints
  300. if (params.breakpoints) {
  301. swiper.setBreakpoint();
  302. }
  303. swiper.updateSize();
  304. swiper.updateSlides();
  305. swiper.updateProgress();
  306. swiper.updateSlidesClasses();
  307. function setTranslate() {
  308. const translateValue = swiper.rtlTranslate ? swiper.translate * -1 : swiper.translate;
  309. const newTranslate = Math.min(Math.max(translateValue, swiper.maxTranslate()), swiper.minTranslate());
  310. swiper.setTranslate(newTranslate);
  311. swiper.updateActiveIndex();
  312. swiper.updateSlidesClasses();
  313. }
  314. let translated;
  315. if (swiper.params.freeMode && swiper.params.freeMode.enabled) {
  316. setTranslate();
  317. if (swiper.params.autoHeight) {
  318. swiper.updateAutoHeight();
  319. }
  320. } else {
  321. if ((swiper.params.slidesPerView === 'auto' || swiper.params.slidesPerView > 1) && swiper.isEnd && !swiper.params.centeredSlides) {
  322. translated = swiper.slideTo(swiper.slides.length - 1, 0, false, true);
  323. } else {
  324. translated = swiper.slideTo(swiper.activeIndex, 0, false, true);
  325. }
  326. if (!translated) {
  327. setTranslate();
  328. }
  329. }
  330. if (params.watchOverflow && snapGrid !== swiper.snapGrid) {
  331. swiper.checkOverflow();
  332. }
  333. swiper.emit('update');
  334. }
  335. changeDirection(newDirection, needUpdate = true) {
  336. const swiper = this;
  337. const currentDirection = swiper.params.direction;
  338. if (!newDirection) {
  339. // eslint-disable-next-line
  340. newDirection = currentDirection === 'horizontal' ? 'vertical' : 'horizontal';
  341. }
  342. if (newDirection === currentDirection || newDirection !== 'horizontal' && newDirection !== 'vertical') {
  343. return swiper;
  344. }
  345. swiper.$el.removeClass(`${swiper.params.containerModifierClass}${currentDirection}`).addClass(`${swiper.params.containerModifierClass}${newDirection}`);
  346. swiper.emitContainerClasses();
  347. swiper.params.direction = newDirection;
  348. swiper.slides.each(slideEl => {
  349. if (newDirection === 'vertical') {
  350. slideEl.style.width = '';
  351. } else {
  352. slideEl.style.height = '';
  353. }
  354. });
  355. swiper.emit('changeDirection');
  356. if (needUpdate) swiper.update();
  357. return swiper;
  358. }
  359. mount(el) {
  360. const swiper = this;
  361. if (swiper.mounted) return true; // Find el
  362. const $el = $(el || swiper.params.el);
  363. el = $el[0];
  364. if (!el) {
  365. return false;
  366. }
  367. el.swiper = swiper;
  368. const getWrapperSelector = () => {
  369. return `.${(swiper.params.wrapperClass || '').trim().split(' ').join('.')}`;
  370. };
  371. const getWrapper = () => {
  372. if (el && el.shadowRoot && el.shadowRoot.querySelector) {
  373. const res = $(el.shadowRoot.querySelector(getWrapperSelector())); // Children needs to return slot items
  374. res.children = options => $el.children(options);
  375. return res;
  376. }
  377. return $el.children(getWrapperSelector());
  378. }; // Find Wrapper
  379. let $wrapperEl = getWrapper();
  380. if ($wrapperEl.length === 0 && swiper.params.createElements) {
  381. const document = getDocument();
  382. const wrapper = document.createElement('div');
  383. $wrapperEl = $(wrapper);
  384. wrapper.className = swiper.params.wrapperClass;
  385. $el.append(wrapper);
  386. $el.children(`.${swiper.params.slideClass}`).each(slideEl => {
  387. $wrapperEl.append(slideEl);
  388. });
  389. }
  390. Object.assign(swiper, {
  391. $el,
  392. el,
  393. $wrapperEl,
  394. wrapperEl: $wrapperEl[0],
  395. mounted: true,
  396. // RTL
  397. rtl: el.dir.toLowerCase() === 'rtl' || $el.css('direction') === 'rtl',
  398. rtlTranslate: swiper.params.direction === 'horizontal' && (el.dir.toLowerCase() === 'rtl' || $el.css('direction') === 'rtl'),
  399. wrongRTL: $wrapperEl.css('display') === '-webkit-box'
  400. });
  401. return true;
  402. }
  403. init(el) {
  404. const swiper = this;
  405. if (swiper.initialized) return swiper;
  406. const mounted = swiper.mount(el);
  407. if (mounted === false) return swiper;
  408. swiper.emit('beforeInit'); // Set breakpoint
  409. if (swiper.params.breakpoints) {
  410. swiper.setBreakpoint();
  411. } // Add Classes
  412. swiper.addClasses(); // Create loop
  413. if (swiper.params.loop) {
  414. swiper.loopCreate();
  415. } // Update size
  416. swiper.updateSize(); // Update slides
  417. swiper.updateSlides();
  418. if (swiper.params.watchOverflow) {
  419. swiper.checkOverflow();
  420. } // Set Grab Cursor
  421. if (swiper.params.grabCursor && swiper.enabled) {
  422. swiper.setGrabCursor();
  423. }
  424. if (swiper.params.preloadImages) {
  425. swiper.preloadImages();
  426. } // Slide To Initial Slide
  427. if (swiper.params.loop) {
  428. swiper.slideTo(swiper.params.initialSlide + swiper.loopedSlides, 0, swiper.params.runCallbacksOnInit, false, true);
  429. } else {
  430. swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true);
  431. } // Attach events
  432. swiper.attachEvents(); // Init Flag
  433. swiper.initialized = true; // Emit
  434. swiper.emit('init');
  435. swiper.emit('afterInit');
  436. return swiper;
  437. }
  438. destroy(deleteInstance = true, cleanStyles = true) {
  439. const swiper = this;
  440. const {
  441. params,
  442. $el,
  443. $wrapperEl,
  444. slides
  445. } = swiper;
  446. if (typeof swiper.params === 'undefined' || swiper.destroyed) {
  447. return null;
  448. }
  449. swiper.emit('beforeDestroy'); // Init Flag
  450. swiper.initialized = false; // Detach events
  451. swiper.detachEvents(); // Destroy loop
  452. if (params.loop) {
  453. swiper.loopDestroy();
  454. } // Cleanup styles
  455. if (cleanStyles) {
  456. swiper.removeClasses();
  457. $el.removeAttr('style');
  458. $wrapperEl.removeAttr('style');
  459. if (slides && slides.length) {
  460. slides.removeClass([params.slideVisibleClass, params.slideActiveClass, params.slideNextClass, params.slidePrevClass].join(' ')).removeAttr('style').removeAttr('data-swiper-slide-index');
  461. }
  462. }
  463. swiper.emit('destroy'); // Detach emitter events
  464. Object.keys(swiper.eventsListeners).forEach(eventName => {
  465. swiper.off(eventName);
  466. });
  467. if (deleteInstance !== false) {
  468. swiper.$el[0].swiper = null;
  469. deleteProps(swiper);
  470. }
  471. swiper.destroyed = true;
  472. return null;
  473. }
  474. static extendDefaults(newDefaults) {
  475. extend(extendedDefaults, newDefaults);
  476. }
  477. static get extendedDefaults() {
  478. return extendedDefaults;
  479. }
  480. static get defaults() {
  481. return defaults;
  482. }
  483. static installModule(mod) {
  484. if (!Swiper.prototype.__modules__) Swiper.prototype.__modules__ = [];
  485. const modules = Swiper.prototype.__modules__;
  486. if (typeof mod === 'function' && modules.indexOf(mod) < 0) {
  487. modules.push(mod);
  488. }
  489. }
  490. static use(module) {
  491. if (Array.isArray(module)) {
  492. module.forEach(m => Swiper.installModule(m));
  493. return Swiper;
  494. }
  495. Swiper.installModule(module);
  496. return Swiper;
  497. }
  498. }
  499. Object.keys(prototypes).forEach(prototypeGroup => {
  500. Object.keys(prototypes[prototypeGroup]).forEach(protoMethod => {
  501. Swiper.prototype[protoMethod] = prototypes[prototypeGroup][protoMethod];
  502. });
  503. });
  504. Swiper.use([Resize, Observer]);
  505. export default Swiper;