1 | import { Base } from "../shared/Base/Base.js";
|
2 | import { Panzoom } from "../Panzoom/Panzoom.js";
|
3 |
|
4 | import { extend } from "../shared/utils/extend.js";
|
5 | import { round } from "../shared/utils/round.js";
|
6 | import { throttle } from "../shared/utils/throttle.js";
|
7 |
|
8 | import { Plugins } from "./plugins/index.js";
|
9 |
|
10 |
|
11 | import en from "./l10n/en.js";
|
12 |
|
13 | const defaults = {
|
14 |
|
15 |
|
16 | slides: [],
|
17 |
|
18 |
|
19 | preload: 0,
|
20 |
|
21 |
|
22 |
|
23 | slidesPerPage: "auto",
|
24 |
|
25 |
|
26 | initialPage: null,
|
27 |
|
28 |
|
29 | initialSlide: null,
|
30 |
|
31 |
|
32 | friction: 0.92,
|
33 |
|
34 |
|
35 | center: true,
|
36 |
|
37 |
|
38 | infinite: true,
|
39 |
|
40 |
|
41 | fill: true,
|
42 |
|
43 |
|
44 | dragFree: false,
|
45 |
|
46 |
|
47 | prefix: "",
|
48 |
|
49 |
|
50 | classNames: {
|
51 | viewport: "carousel__viewport",
|
52 | track: "carousel__track",
|
53 | slide: "carousel__slide",
|
54 |
|
55 |
|
56 | slideSelected: "is-selected",
|
57 | },
|
58 |
|
59 |
|
60 | l10n: en,
|
61 | };
|
62 |
|
63 | export class Carousel extends Base {
|
64 | |
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 | constructor($container, options = {}) {
|
71 | options = extend(true, {}, defaults, options);
|
72 |
|
73 | super(options);
|
74 |
|
75 | this.state = "init";
|
76 |
|
77 | this.$container = $container;
|
78 |
|
79 | if (!(this.$container instanceof HTMLElement)) {
|
80 | throw new Error("No root element provided");
|
81 | }
|
82 |
|
83 | this.slideNext = throttle(this.slideNext.bind(this), 250, true);
|
84 | this.slidePrev = throttle(this.slidePrev.bind(this), 250, true);
|
85 |
|
86 | this.init();
|
87 |
|
88 | $container.__Carousel = this;
|
89 | }
|
90 |
|
91 | |
92 |
|
93 |
|
94 | init() {
|
95 | this.pages = [];
|
96 | this.page = this.pageIndex = null;
|
97 | this.prevPage = this.prevPageIndex = null;
|
98 |
|
99 | this.attachPlugins(Carousel.Plugins);
|
100 |
|
101 | this.trigger("init");
|
102 |
|
103 | this.initLayout();
|
104 |
|
105 | this.initSlides();
|
106 |
|
107 | this.updateMetrics();
|
108 |
|
109 | if (this.$track && this.pages.length) {
|
110 | this.$track.style.transform = `translate3d(${this.pages[this.page].left * -1}px, 0px, 0) scale(1)`;
|
111 | }
|
112 |
|
113 | this.manageSlideVisiblity();
|
114 |
|
115 | this.initPanzoom();
|
116 |
|
117 | this.state = "ready";
|
118 |
|
119 | this.trigger("ready");
|
120 | }
|
121 |
|
122 | |
123 |
|
124 |
|
125 | initLayout() {
|
126 | const prefix = this.option("prefix");
|
127 | const classNames = this.option("classNames");
|
128 |
|
129 | this.$viewport = this.option("viewport") || this.$container.querySelector(`.${prefix}${classNames.viewport}`);
|
130 |
|
131 | if (!this.$viewport) {
|
132 | this.$viewport = document.createElement("div");
|
133 | this.$viewport.classList.add(...(prefix + classNames.viewport).split(" "));
|
134 |
|
135 | this.$viewport.append(...this.$container.childNodes);
|
136 |
|
137 | this.$container.appendChild(this.$viewport);
|
138 | }
|
139 |
|
140 | this.$track = this.option("track") || this.$container.querySelector(`.${prefix}${classNames.track}`);
|
141 |
|
142 | if (!this.$track) {
|
143 | this.$track = document.createElement("div");
|
144 | this.$track.classList.add(...(prefix + classNames.track).split(" "));
|
145 |
|
146 | this.$track.append(...this.$viewport.childNodes);
|
147 |
|
148 | this.$viewport.appendChild(this.$track);
|
149 | }
|
150 | }
|
151 |
|
152 | |
153 |
|
154 |
|
155 | initSlides() {
|
156 | this.slides = [];
|
157 |
|
158 |
|
159 | const elems = this.$viewport.querySelectorAll(`.${this.option("prefix")}${this.option("classNames.slide")}`);
|
160 |
|
161 | elems.forEach((el) => {
|
162 | const slide = {
|
163 | $el: el,
|
164 | isDom: true,
|
165 | };
|
166 |
|
167 | this.slides.push(slide);
|
168 |
|
169 | this.trigger("createSlide", slide, this.slides.length);
|
170 | });
|
171 |
|
172 |
|
173 |
|
174 | if (Array.isArray(this.options.slides)) {
|
175 | this.slides = extend(true, [...this.slides], this.options.slides);
|
176 | }
|
177 | }
|
178 |
|
179 | |
180 |
|
181 |
|
182 | updateMetrics() {
|
183 |
|
184 |
|
185 | let contentWidth = 0;
|
186 | let indexes = [];
|
187 | let lastSlideWidth;
|
188 |
|
189 | this.slides.forEach((slide, index) => {
|
190 | const $el = slide.$el;
|
191 | const slideWidth = slide.isDom || !lastSlideWidth ? this.getSlideMetrics($el) : lastSlideWidth;
|
192 |
|
193 | slide.index = index;
|
194 | slide.width = slideWidth;
|
195 | slide.left = contentWidth;
|
196 |
|
197 | lastSlideWidth = slideWidth;
|
198 | contentWidth += slideWidth;
|
199 |
|
200 | indexes.push(index);
|
201 | });
|
202 |
|
203 | let viewportWidth = Math.max(this.$track.offsetWidth, round(this.$track.getBoundingClientRect().width));
|
204 |
|
205 | let viewportStyles = getComputedStyle(this.$track);
|
206 | viewportWidth = viewportWidth - (parseFloat(viewportStyles.paddingLeft) + parseFloat(viewportStyles.paddingRight));
|
207 |
|
208 | this.contentWidth = contentWidth;
|
209 | this.viewportWidth = viewportWidth;
|
210 |
|
211 |
|
212 |
|
213 | const pages = [];
|
214 | const slidesPerPage = this.option("slidesPerPage");
|
215 |
|
216 | if (Number.isInteger(slidesPerPage) && contentWidth > viewportWidth) {
|
217 |
|
218 | for (let i = 0; i < this.slides.length; i += slidesPerPage) {
|
219 | pages.push({
|
220 | indexes: indexes.slice(i, i + slidesPerPage),
|
221 | slides: this.slides.slice(i, i + slidesPerPage),
|
222 | });
|
223 | }
|
224 | } else {
|
225 |
|
226 | let currentPage = 0;
|
227 | let currentWidth = 0;
|
228 |
|
229 | for (let i = 0; i < this.slides.length; i += 1) {
|
230 | let slide = this.slides[i];
|
231 |
|
232 |
|
233 | if (!pages.length || currentWidth + slide.width > viewportWidth) {
|
234 | pages.push({
|
235 | indexes: [],
|
236 | slides: [],
|
237 | });
|
238 |
|
239 | currentPage = pages.length - 1;
|
240 | currentWidth = 0;
|
241 | }
|
242 |
|
243 | currentWidth += slide.width;
|
244 |
|
245 | pages[currentPage].indexes.push(i);
|
246 | pages[currentPage].slides.push(slide);
|
247 | }
|
248 | }
|
249 |
|
250 | const shouldCenter = this.option("center");
|
251 | const shouldFill = this.option("fill");
|
252 |
|
253 |
|
254 |
|
255 | pages.forEach((page, index) => {
|
256 | page.index = index;
|
257 | page.width = page.slides.reduce((sum, slide) => sum + slide.width, 0);
|
258 |
|
259 | page.left = page.slides[0].left;
|
260 |
|
261 | if (shouldCenter) {
|
262 | page.left += (viewportWidth - page.width) * 0.5 * -1;
|
263 | }
|
264 |
|
265 | if (shouldFill && !this.option("infiniteX", this.option("infinite")) && contentWidth > viewportWidth) {
|
266 | page.left = Math.max(page.left, 0);
|
267 | page.left = Math.min(page.left, contentWidth - viewportWidth);
|
268 | }
|
269 | });
|
270 |
|
271 |
|
272 |
|
273 | const rez = [];
|
274 | let prevPage;
|
275 |
|
276 | pages.forEach((page2) => {
|
277 | const page = { ...page2 };
|
278 |
|
279 | if (prevPage && page.left === prevPage.left) {
|
280 | prevPage.width += page.width;
|
281 |
|
282 | prevPage.slides = [...prevPage.slides, ...page.slides];
|
283 | prevPage.indexes = [...prevPage.indexes, ...page.indexes];
|
284 | } else {
|
285 | page.index = rez.length;
|
286 |
|
287 | prevPage = page;
|
288 |
|
289 | rez.push(page);
|
290 | }
|
291 | });
|
292 |
|
293 | this.pages = rez;
|
294 |
|
295 | let page = this.page;
|
296 |
|
297 | if (page === null) {
|
298 | const initialSlide = this.option("initialSlide");
|
299 |
|
300 | if (initialSlide !== null) {
|
301 | page = this.findPageForSlide(initialSlide);
|
302 | } else {
|
303 | page = parseInt(this.option("initialPage", 0), 10) || 0;
|
304 | }
|
305 |
|
306 | if (!rez[page]) {
|
307 | page = rez.length && page > rez.length ? rez[rez.length - 1].index : 0;
|
308 | }
|
309 |
|
310 | this.page = page;
|
311 | this.pageIndex = page;
|
312 | }
|
313 |
|
314 | this.updatePanzoom();
|
315 |
|
316 | this.trigger("refresh");
|
317 | }
|
318 |
|
319 | |
320 |
|
321 |
|
322 |
|
323 |
|
324 | getSlideMetrics(node) {
|
325 | if (!node) {
|
326 | const firstSlide = this.slides[0];
|
327 |
|
328 | node = document.createElement("div");
|
329 |
|
330 | node.dataset.isTestEl = 1;
|
331 | node.style.visibility = "hidden";
|
332 | node.classList.add(...(this.option("prefix") + this.option("classNames.slide")).split(" "));
|
333 |
|
334 |
|
335 | if (firstSlide.customClass) {
|
336 | node.classList.add(...firstSlide.customClass.split(" "));
|
337 | }
|
338 |
|
339 | this.$track.prepend(node);
|
340 | }
|
341 |
|
342 | let width = Math.max(node.offsetWidth, round(node.getBoundingClientRect().width));
|
343 |
|
344 |
|
345 | const style = node.currentStyle || window.getComputedStyle(node);
|
346 | width = width + (parseFloat(style.marginLeft) || 0) + (parseFloat(style.marginRight) || 0);
|
347 |
|
348 | if (node.dataset.isTestEl) {
|
349 | node.remove();
|
350 | }
|
351 |
|
352 | return width;
|
353 | }
|
354 |
|
355 | |
356 |
|
357 |
|
358 |
|
359 |
|
360 | findPageForSlide(index) {
|
361 | index = parseInt(index, 10) || 0;
|
362 |
|
363 | const page = this.pages.find((page) => {
|
364 | return page.indexes.indexOf(index) > -1;
|
365 | });
|
366 |
|
367 | return page ? page.index : null;
|
368 | }
|
369 |
|
370 | |
371 |
|
372 |
|
373 | slideNext() {
|
374 | this.slideTo(this.pageIndex + 1);
|
375 | }
|
376 |
|
377 | |
378 |
|
379 |
|
380 | slidePrev() {
|
381 | this.slideTo(this.pageIndex - 1);
|
382 | }
|
383 |
|
384 | |
385 |
|
386 |
|
387 |
|
388 |
|
389 | slideTo(page, params = {}) {
|
390 | const { x = this.setPage(page, true) * -1, y = 0, friction = this.option("friction") } = params;
|
391 |
|
392 | if (this.Panzoom.content.x === x && !this.Panzoom.velocity.x && friction) {
|
393 | return;
|
394 | }
|
395 |
|
396 | this.Panzoom.panTo({
|
397 | x,
|
398 | y,
|
399 | friction,
|
400 | ignoreBounds: true,
|
401 | });
|
402 |
|
403 | if (this.state === "ready" && this.Panzoom.state === "ready") {
|
404 | this.trigger("settle");
|
405 | }
|
406 | }
|
407 |
|
408 | |
409 |
|
410 |
|
411 | initPanzoom() {
|
412 | if (this.Panzoom) {
|
413 | this.Panzoom.destroy();
|
414 | }
|
415 |
|
416 |
|
417 | const options = extend(
|
418 | true,
|
419 | {},
|
420 | {
|
421 |
|
422 | content: this.$track,
|
423 | wrapInner: false,
|
424 | resizeParent: false,
|
425 |
|
426 |
|
427 | zoom: false,
|
428 | click: false,
|
429 |
|
430 |
|
431 | lockAxis: "x",
|
432 |
|
433 | x: this.pages.length ? this.pages[this.page].left * -1 : 0,
|
434 | centerOnStart: false,
|
435 |
|
436 |
|
437 | textSelection: () => this.option("textSelection", false),
|
438 |
|
439 |
|
440 | panOnlyZoomed: function () {
|
441 | return this.content.width <= this.viewport.width;
|
442 | },
|
443 | },
|
444 | this.option("Panzoom")
|
445 | );
|
446 |
|
447 |
|
448 | this.Panzoom = new Panzoom(this.$container, options);
|
449 |
|
450 | this.Panzoom.on({
|
451 |
|
452 | "*": (name, ...details) => this.trigger(`Panzoom.${name}`, ...details),
|
453 |
|
454 | afterUpdate: () => {
|
455 | this.updatePage();
|
456 | },
|
457 | beforeTransform: this.onBeforeTransform.bind(this),
|
458 | touchEnd: this.onTouchEnd.bind(this),
|
459 | endAnimation: () => {
|
460 | this.trigger("settle");
|
461 | },
|
462 | });
|
463 |
|
464 |
|
465 |
|
466 | this.updateMetrics();
|
467 | this.manageSlideVisiblity();
|
468 | }
|
469 |
|
470 | updatePanzoom() {
|
471 | if (!this.Panzoom) {
|
472 | return;
|
473 | }
|
474 |
|
475 | this.Panzoom.content = {
|
476 | ...this.Panzoom.content,
|
477 | fitWidth: this.contentWidth,
|
478 | origWidth: this.contentWidth,
|
479 | width: this.contentWidth,
|
480 | };
|
481 |
|
482 | if (this.pages.length > 1 && this.option("infiniteX", this.option("infinite"))) {
|
483 | this.Panzoom.boundX = null;
|
484 | } else if (this.pages.length) {
|
485 | this.Panzoom.boundX = {
|
486 | from: this.pages[this.pages.length - 1].left * -1,
|
487 | to: this.pages[0].left * -1,
|
488 | };
|
489 | }
|
490 |
|
491 | if (this.option("infiniteY", this.option("infinite"))) {
|
492 | this.Panzoom.boundY = null;
|
493 | } else {
|
494 | this.Panzoom.boundY = {
|
495 | from: 0,
|
496 | to: 0,
|
497 | };
|
498 | }
|
499 |
|
500 | this.Panzoom.handleCursor();
|
501 | }
|
502 |
|
503 | manageSlideVisiblity() {
|
504 | const contentWidth = this.contentWidth;
|
505 | const viewportWidth = this.viewportWidth;
|
506 |
|
507 | let currentX = this.Panzoom ? this.Panzoom.content.x * -1 : this.pages.length ? this.pages[this.page].left : 0;
|
508 |
|
509 | const preload = this.option("preload");
|
510 | const infinite = this.option("infiniteX", this.option("infinite"));
|
511 |
|
512 | const paddingLeft = parseFloat(getComputedStyle(this.$viewport, null).getPropertyValue("padding-left"));
|
513 | const paddingRight = parseFloat(getComputedStyle(this.$viewport, null).getPropertyValue("padding-right"));
|
514 |
|
515 |
|
516 | this.slides.forEach((slide) => {
|
517 | let leftBoundary, rightBoundary;
|
518 |
|
519 | let hasDiff = 0;
|
520 |
|
521 |
|
522 | leftBoundary = currentX - paddingLeft;
|
523 | rightBoundary = currentX + viewportWidth + paddingRight;
|
524 |
|
525 | leftBoundary -= preload * (viewportWidth + paddingLeft + paddingRight);
|
526 | rightBoundary += preload * (viewportWidth + paddingLeft + paddingRight);
|
527 |
|
528 | const insideCurrentInterval = slide.left + slide.width > leftBoundary && slide.left < rightBoundary;
|
529 |
|
530 |
|
531 | leftBoundary = currentX + contentWidth - paddingLeft;
|
532 | rightBoundary = currentX + contentWidth + viewportWidth + paddingRight;
|
533 |
|
534 |
|
535 | leftBoundary -= preload * (viewportWidth + paddingLeft + paddingRight);
|
536 |
|
537 | const insidePrevInterval = infinite && slide.left + slide.width > leftBoundary && slide.left < rightBoundary;
|
538 |
|
539 |
|
540 | leftBoundary = currentX - contentWidth - paddingLeft;
|
541 | rightBoundary = currentX - contentWidth + viewportWidth + paddingRight;
|
542 |
|
543 |
|
544 | leftBoundary -= preload * (viewportWidth + paddingLeft + paddingRight);
|
545 |
|
546 | const insideNextInterval = infinite && slide.left + slide.width > leftBoundary && slide.left < rightBoundary;
|
547 |
|
548 |
|
549 | if (insidePrevInterval || insideCurrentInterval || insideNextInterval) {
|
550 | this.createSlideEl(slide);
|
551 |
|
552 | if (insideCurrentInterval) {
|
553 | hasDiff = 0;
|
554 | }
|
555 |
|
556 | if (insidePrevInterval) {
|
557 | hasDiff = -1;
|
558 | }
|
559 |
|
560 | if (insideNextInterval) {
|
561 | hasDiff = 1;
|
562 | }
|
563 |
|
564 |
|
565 | if (slide.left + slide.width > currentX && slide.left <= currentX + viewportWidth + paddingRight) {
|
566 | hasDiff = 0;
|
567 | }
|
568 | } else {
|
569 | this.removeSlideEl(slide);
|
570 | }
|
571 |
|
572 | slide.hasDiff = hasDiff;
|
573 | });
|
574 |
|
575 |
|
576 | let nextIndex = 0;
|
577 | let nextPos = 0;
|
578 |
|
579 | this.slides.forEach((slide, index) => {
|
580 | let updatedX = 0;
|
581 |
|
582 | if (slide.$el) {
|
583 | if (index !== nextIndex || slide.hasDiff) {
|
584 | updatedX = nextPos + slide.hasDiff * contentWidth;
|
585 | } else {
|
586 | nextPos = 0;
|
587 | }
|
588 |
|
589 | slide.$el.style.left = Math.abs(updatedX) > 0.1 ? `${nextPos + slide.hasDiff * contentWidth}px` : "";
|
590 |
|
591 | nextIndex++;
|
592 | } else {
|
593 | nextPos += slide.width;
|
594 | }
|
595 | });
|
596 |
|
597 | this.markSelectedSlides();
|
598 | }
|
599 |
|
600 | |
601 |
|
602 |
|
603 |
|
604 |
|
605 | createSlideEl(slide) {
|
606 | if (!slide) {
|
607 | return;
|
608 | }
|
609 |
|
610 | if (slide.$el) {
|
611 | let curentIndex = slide.$el.dataset.index;
|
612 |
|
613 | if (!curentIndex || parseInt(curentIndex, 10) !== slide.index) {
|
614 | slide.$el.dataset.index = slide.index;
|
615 |
|
616 |
|
617 |
|
618 | slide.$el.querySelectorAll("[data-lazy-srcset]").forEach((node) => {
|
619 | node.srcset = node.dataset.lazySrcset;
|
620 | });
|
621 |
|
622 | slide.$el.querySelectorAll("[data-lazy-src]").forEach((node) => {
|
623 | let lazySrc = node.dataset.lazySrc;
|
624 |
|
625 | if (node instanceof HTMLImageElement) {
|
626 | node.src = lazySrc;
|
627 | } else {
|
628 | node.style.backgroundImage = `url('${lazySrc}')`;
|
629 | }
|
630 | });
|
631 |
|
632 |
|
633 |
|
634 | let lazySrc;
|
635 |
|
636 | if ((lazySrc = slide.$el.dataset.lazySrc)) {
|
637 | slide.$el.style.backgroundImage = `url('${lazySrc}')`;
|
638 | }
|
639 |
|
640 | slide.state = "ready";
|
641 | }
|
642 |
|
643 | return;
|
644 | }
|
645 |
|
646 | const div = document.createElement("div");
|
647 |
|
648 | div.dataset.index = slide.index;
|
649 | div.classList.add(...(this.option("prefix") + this.option("classNames.slide")).split(" "));
|
650 |
|
651 | if (slide.customClass) {
|
652 | div.classList.add(...slide.customClass.split(" "));
|
653 | }
|
654 |
|
655 | if (slide.html) {
|
656 | div.innerHTML = slide.html;
|
657 | }
|
658 |
|
659 | const allElelements = [];
|
660 |
|
661 | this.slides.forEach((slide, index) => {
|
662 | if (slide.$el) {
|
663 | allElelements.push(index);
|
664 | }
|
665 | });
|
666 |
|
667 |
|
668 | const goal = slide.index;
|
669 | let refSlide = null;
|
670 |
|
671 | if (allElelements.length) {
|
672 | let refIndex = allElelements.reduce((prev, curr) =>
|
673 | Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev
|
674 | );
|
675 | refSlide = this.slides[refIndex];
|
676 | }
|
677 |
|
678 | this.$track.insertBefore(
|
679 | div,
|
680 | refSlide && refSlide.$el ? (refSlide.index < slide.index ? refSlide.$el.nextSibling : refSlide.$el) : null
|
681 | );
|
682 |
|
683 | slide.$el = div;
|
684 |
|
685 | this.trigger("createSlide", slide, goal);
|
686 |
|
687 | return slide;
|
688 | }
|
689 |
|
690 | |
691 |
|
692 |
|
693 |
|
694 | removeSlideEl(slide) {
|
695 | if (slide.$el && !slide.isDom) {
|
696 | this.trigger("removeSlide", slide);
|
697 |
|
698 | slide.$el.remove();
|
699 | slide.$el = null;
|
700 | }
|
701 | }
|
702 |
|
703 | |
704 |
|
705 |
|
706 | markSelectedSlides() {
|
707 | const selectedClass = this.option("classNames.slideSelected");
|
708 | const attr = "aria-hidden";
|
709 |
|
710 | this.slides.forEach((slide, index) => {
|
711 | const $el = slide.$el;
|
712 |
|
713 | if (!$el) {
|
714 | return;
|
715 | }
|
716 |
|
717 | const page = this.pages[this.page];
|
718 |
|
719 | if (page && page.indexes && page.indexes.indexOf(index) > -1) {
|
720 | if (selectedClass && !$el.classList.contains(selectedClass)) {
|
721 | $el.classList.add(selectedClass);
|
722 | this.trigger("selectSlide", slide);
|
723 | }
|
724 |
|
725 | $el.removeAttribute(attr);
|
726 | } else {
|
727 | if (selectedClass && $el.classList.contains(selectedClass)) {
|
728 | $el.classList.remove(selectedClass);
|
729 | this.trigger("unselectSlide", slide);
|
730 | }
|
731 |
|
732 | $el.setAttribute(attr, true);
|
733 | }
|
734 | });
|
735 | }
|
736 |
|
737 | |
738 |
|
739 |
|
740 | updatePage() {
|
741 | this.updateMetrics();
|
742 |
|
743 | this.slideTo(this.page, { friction: 0 });
|
744 | }
|
745 |
|
746 | |
747 |
|
748 |
|
749 |
|
750 | onBeforeTransform() {
|
751 | if (this.option("infiniteX", this.option("infinite"))) {
|
752 | this.manageInfiniteTrack();
|
753 | }
|
754 |
|
755 | this.manageSlideVisiblity();
|
756 | }
|
757 |
|
758 | |
759 |
|
760 |
|
761 | manageInfiniteTrack() {
|
762 | const contentWidth = this.contentWidth;
|
763 | const viewportWidth = this.viewportWidth;
|
764 |
|
765 | if (!this.option("infiniteX", this.option("infinite")) || this.pages.length < 2 || contentWidth < viewportWidth) {
|
766 | return;
|
767 | }
|
768 |
|
769 | const panzoom = this.Panzoom;
|
770 |
|
771 | let isFlipped = false;
|
772 |
|
773 | if (panzoom.content.x < (contentWidth - viewportWidth) * -1) {
|
774 | panzoom.content.x += contentWidth;
|
775 |
|
776 | this.pageIndex = this.pageIndex - this.pages.length;
|
777 |
|
778 | isFlipped = true;
|
779 | }
|
780 |
|
781 | if (panzoom.content.x > viewportWidth) {
|
782 | panzoom.content.x -= contentWidth;
|
783 |
|
784 | this.pageIndex = this.pageIndex + this.pages.length;
|
785 |
|
786 | isFlipped = true;
|
787 | }
|
788 |
|
789 | if (isFlipped && panzoom.state === "pointerdown") {
|
790 | panzoom.resetDragPosition();
|
791 | }
|
792 |
|
793 | return isFlipped;
|
794 | }
|
795 |
|
796 | |
797 |
|
798 |
|
799 |
|
800 | onTouchEnd(panzoom, event) {
|
801 | const dragFree = this.option("dragFree");
|
802 |
|
803 |
|
804 | if (
|
805 | !dragFree &&
|
806 | this.pages.length > 1 &&
|
807 | panzoom.dragOffset.time < 350 &&
|
808 | Math.abs(panzoom.dragOffset.y) < 1 &&
|
809 | Math.abs(panzoom.dragOffset.x) > 5
|
810 | ) {
|
811 | this[panzoom.dragOffset.x < 0 ? "slideNext" : "slidePrev"]();
|
812 | return;
|
813 | }
|
814 |
|
815 |
|
816 |
|
817 | if (dragFree) {
|
818 | const [, nextPageIndex] = this.getPageFromPosition(panzoom.transform.x * -1);
|
819 | this.setPage(nextPageIndex);
|
820 | } else {
|
821 | this.slideToClosest();
|
822 | }
|
823 | }
|
824 |
|
825 | |
826 |
|
827 |
|
828 |
|
829 | slideToClosest(params = {}) {
|
830 | let [, nextPageIndex] = this.getPageFromPosition(this.Panzoom.content.x * -1);
|
831 |
|
832 | this.slideTo(nextPageIndex, params);
|
833 | }
|
834 |
|
835 | |
836 |
|
837 |
|
838 |
|
839 | getPageFromPosition(xPos) {
|
840 | const pageCount = this.pages.length;
|
841 | const center = this.option("center");
|
842 |
|
843 | if (center) {
|
844 | xPos += this.viewportWidth * 0.5;
|
845 | }
|
846 |
|
847 | const interval = Math.floor(xPos / this.contentWidth);
|
848 |
|
849 | xPos -= interval * this.contentWidth;
|
850 |
|
851 | let slide = this.slides.find((slide) => slide.left <= xPos && slide.left + slide.width > xPos);
|
852 |
|
853 | if (slide) {
|
854 | let pageIndex = this.findPageForSlide(slide.index);
|
855 |
|
856 | return [pageIndex, pageIndex + interval * pageCount];
|
857 | }
|
858 |
|
859 | return [0, 0];
|
860 | }
|
861 |
|
862 | |
863 |
|
864 |
|
865 |
|
866 |
|
867 | setPage(page, toClosest) {
|
868 | let nextPosition = 0;
|
869 | let pageIndex = parseInt(page, 10) || 0;
|
870 |
|
871 | const prevPage = this.page,
|
872 | prevPageIndex = this.pageIndex,
|
873 | pageCount = this.pages.length;
|
874 |
|
875 | const contentWidth = this.contentWidth;
|
876 | const viewportWidth = this.viewportWidth;
|
877 |
|
878 | page = ((pageIndex % pageCount) + pageCount) % pageCount;
|
879 |
|
880 | if (this.option("infiniteX", this.option("infinite")) && contentWidth > viewportWidth) {
|
881 | const nextInterval = Math.floor(pageIndex / pageCount) || 0,
|
882 | elemDimWidth = contentWidth;
|
883 |
|
884 | nextPosition = this.pages[page].left + nextInterval * elemDimWidth;
|
885 |
|
886 | if (toClosest === true && pageCount > 2) {
|
887 | let currPosition = this.Panzoom.content.x * -1;
|
888 |
|
889 |
|
890 | const decreasedPosition = nextPosition - elemDimWidth,
|
891 | increasedPosition = nextPosition + elemDimWidth,
|
892 | diff1 = Math.abs(currPosition - nextPosition),
|
893 | diff2 = Math.abs(currPosition - decreasedPosition),
|
894 | diff3 = Math.abs(currPosition - increasedPosition);
|
895 |
|
896 | if (diff3 < diff1 && diff3 <= diff2) {
|
897 | nextPosition = increasedPosition;
|
898 | pageIndex += pageCount;
|
899 | } else if (diff2 < diff1 && diff2 < diff3) {
|
900 | nextPosition = decreasedPosition;
|
901 | pageIndex -= pageCount;
|
902 | }
|
903 | }
|
904 | } else {
|
905 | page = pageIndex = Math.max(0, Math.min(pageIndex, pageCount - 1));
|
906 |
|
907 | nextPosition = this.pages.length ? this.pages[page].left : 0;
|
908 | }
|
909 |
|
910 | this.page = page;
|
911 | this.pageIndex = pageIndex;
|
912 |
|
913 | if (prevPage !== null && page !== prevPage) {
|
914 | this.prevPage = prevPage;
|
915 | this.prevPageIndex = prevPageIndex;
|
916 |
|
917 | this.trigger("change", page, prevPage);
|
918 | }
|
919 |
|
920 | return nextPosition;
|
921 | }
|
922 |
|
923 | |
924 |
|
925 |
|
926 | destroy() {
|
927 | this.state = "destroy";
|
928 |
|
929 | this.slides.forEach((slide) => {
|
930 | this.removeSlideEl(slide);
|
931 | });
|
932 |
|
933 | this.slides = [];
|
934 |
|
935 | this.Panzoom.destroy();
|
936 |
|
937 | this.detachPlugins();
|
938 | }
|
939 | }
|
940 |
|
941 |
|
942 | Carousel.version = "__VERSION__";
|
943 |
|
944 |
|
945 | Carousel.Plugins = Plugins;
|