core.js 14 KB

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