UNPKG

vue-carousel-3d

Version:

Beautiful, flexible and touch supported 3D Carousel for Vue.js

858 lines (857 loc) 29.4 kB
(function() { //#region \0vite/all-css try { if (typeof document != "undefined") { var elementStyle = document.createElement("style"); elementStyle.appendChild(document.createTextNode(".carousel-3d-slide {\r\n position: absolute;\r\n opacity: 0;\r\n visibility: hidden;\r\n overflow: hidden;\r\n top: 0;\r\n border-radius: 1px;\r\n border-color: #000;\r\n border-color: rgba(0, 0, 0, 0.4);\r\n border-style: solid;\r\n background-size: cover;\r\n background-color: #ccc;\r\n display: block;\r\n margin: 0;\r\n box-sizing: border-box;\n}\n.carousel-3d-slide {\r\n text-align: left;\n}\n.carousel-3d-slide img {\r\n width: 100%;\n}\n.carousel-3d-slide.current {\r\n opacity: 1 !important;\r\n visibility: visible !important;\r\n transform: none !important;\r\n z-index: 999;\n}\r\n\r\n\n.carousel-3d-controls[data-v-35bea2a2] {\r\n position: absolute;\r\n top: 50%;\r\n height: 0;\r\n margin-top: -30px;\r\n left: 0;\r\n width: 100%;\r\n z-index: 1000;\n}\n.next[data-v-35bea2a2], .prev[data-v-35bea2a2] {\r\n padding: 0;\r\n border: 0;\r\n background: transparent;\r\n width: 60px;\r\n position: absolute;\r\n z-index: 1010;\r\n font-size: 60px;\r\n height: 60px;\r\n line-height: 60px;\r\n color: #333;\r\n -webkit-user-select: none;\r\n -moz-user-select: none;\r\n -ms-user-select: none;\r\n user-select: none;\r\n text-decoration: none;\r\n top: 0;\n}\n.next[data-v-35bea2a2]:hover, .prev[data-v-35bea2a2]:hover {\r\n cursor: pointer;\r\n opacity: 0.7;\n}\n.prev[data-v-35bea2a2] {\r\n left: 10px;\r\n text-align: left;\n}\n.next[data-v-35bea2a2] {\r\n right: 10px;\r\n text-align: right;\n}\n.disabled[data-v-35bea2a2] {\r\n opacity: 0.2;\r\n cursor: default;\n}\n.disabled[data-v-35bea2a2]:hover {\r\n cursor: default;\r\n opacity: 0.2;\n}\r\n\n.carousel-3d-dots[data-v-e1ffb936] {\r\n position: absolute;\r\n left: 0;\r\n width: 100%;\r\n display: flex;\r\n justify-content: center;\r\n gap: 8px;\r\n z-index: 900;\r\n pointer-events: none;\n}\n.carousel-3d-dots--bottom[data-v-e1ffb936] {\r\n bottom: 14px;\n}\n.carousel-3d-dots--top[data-v-e1ffb936] {\r\n top: 14px;\n}\n.carousel-3d-dot[data-v-e1ffb936] {\r\n pointer-events: auto;\r\n width: 10px;\r\n height: 10px;\r\n padding: 0;\r\n border: 0;\r\n border-radius: 50%;\r\n background: rgba(0, 0, 0, 0.35);\r\n cursor: pointer;\r\n transition: background-color 200ms ease;\n}\n.carousel-3d-dot[data-v-e1ffb936]:hover {\r\n background: rgba(0, 0, 0, 0.55);\n}\n.carousel-3d-dot.is-active[data-v-e1ffb936] {\r\n background: rgba(0, 0, 0, 0.85);\n}\r\n\n.carousel-3d-container[data-v-db4a8480] {\r\n min-height: 1px;\r\n width: 100%;\r\n position: relative;\r\n z-index: 0;\r\n overflow: hidden;\r\n touch-action: pan-y;\r\n margin: 20px auto;\r\n box-sizing: border-box;\n}\n.carousel-3d-slider[data-v-db4a8480] {\r\n position: relative;\r\n margin: 0 auto;\r\n transform-style: preserve-3d;\r\n -webkit-perspective: 1000px;\r\n -moz-perspective: 1000px;\r\n perspective: 1000px;\n}\r\n\r\n/*$vite$:1*/")); document.head.appendChild(elementStyle); } } catch (e) { console.error("vite-plugin-css-injected-by-js", e); } //#endregion })(); import { Comment, Fragment, Text, createBlock, createCommentVNode, createElementBlock, createElementVNode, guardReactiveProps, normalizeClass, normalizeProps, normalizeStyle, openBlock, renderList, renderSlot, resolveComponent, withCtx, withKeys, withModifiers } from "vue"; //#region \0plugin-vue:export-helper var _plugin_vue_export_helper_default = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) target[key] = val; return target; }; //#endregion //#region src/carousel-3d/Slide.vue var _sfc_main$3 = { name: "slide", props: { index: { type: Number } }, inject: ["carousel"], data() { return { styles: {}, zIndex: 999 }; }, computed: { parent() { return this.carousel; }, isCurrent() { return this.index === this.parent.currentIndex; }, shouldRenderContent() { if (!this.parent.lazy) return true; const distance = Math.abs(this.index - this.parent.currentIndex); return Math.min(distance, this.parent.total - distance) <= Math.max(this.parent.visible + 1, 3); }, slideAriaLabel() { return Number.isInteger(this.index) ? `${this.index + 1} of ${this.parent.total}` : void 0; }, leftIndex() { if (this.parent.oneDirectional && this.getSideIndex(this.parent.leftIndices) > this.parent.currentIndex - 1) return -1; return this.getSideIndex(this.parent.leftIndices); }, rightIndex() { if (this.parent.oneDirectional && this.getSideIndex(this.parent.rightIndices) > this.parent.total - this.parent.currentIndex - 2) return -1; return this.getSideIndex(this.parent.rightIndices); }, slideStyle() { let styles = {}; if (!this.isCurrent) { const lIndex = this.leftIndex; const rIndex = this.rightIndex; if (rIndex >= 0 || lIndex >= 0) { styles = rIndex >= 0 ? this.calculatePosition(rIndex, true, this.zIndex) : this.calculatePosition(lIndex, false, this.zIndex); styles.opacity = 1; styles.visibility = "visible"; } if (this.parent.hasHiddenSlides) { if (this.matchIndex(this.parent.leftOutIndex)) styles = this.calculatePosition(this.parent.leftIndices.length - 1, false, this.zIndex); else if (this.matchIndex(this.parent.rightOutIndex)) styles = this.calculatePosition(this.parent.rightIndices.length - 1, true, this.zIndex); } } return Object.assign(styles, { "border-width": this.parent.border + "px", "width": this.parent.slideWidth + "px", "height": this.parent.slideHeight + "px", "transition": " transform " + this.parent.animationSpeed + "ms, opacity " + this.parent.animationSpeed + "ms, visibility " + this.parent.animationSpeed + "ms" }); }, computedClasses() { return { [`left-${this.leftIndex + 1}`]: this.leftIndex >= 0, [`right-${this.rightIndex + 1}`]: this.rightIndex >= 0, "current": this.isCurrent }; } }, methods: { getSideIndex(array) { let index = -1; array.forEach((pos, i) => { if (this.matchIndex(pos)) index = i; }); return index; }, matchIndex(index) { return index >= 0 ? this.index === index : this.parent.total + index === this.index; }, calculatePosition(i, positive, zIndex) { const z = !this.parent.disable3d ? parseInt(this.parent.inverseScaling) + (i + 1) * 100 : 0; const y = !this.parent.disable3d ? parseInt(this.parent.perspective) : 0; const leftRemain = this.parent.space === "auto" ? parseInt((i + 1) * (this.parent.width / 1.5), 10) : parseInt((i + 1) * this.parent.space, 10); const horizon = this.parent.horizonOffset ? parseInt(this.parent.horizonOffset, 10) * (i + 1) : 0; return { transform: positive ? "translateX(" + leftRemain + "px) translateZ(-" + z + "px) rotateY(-" + y + "deg)" + (horizon ? " translateY(" + horizon + "px)" : "") : "translateX(-" + leftRemain + "px) translateZ(-" + z + "px) rotateY(" + y + "deg)" + (horizon ? " translateY(" + horizon + "px)" : ""), top: this.parent.space === "auto" ? 0 : parseInt((i + 1) * this.parent.space), zIndex: zIndex - (Math.abs(i) + 1) }; }, goTo() { if (!this.isCurrent) { if (this.parent.clickable === true) this.parent.goFar(this.index); } else { const { index } = this; this.parent.onMainSlideClick({ index }); } } } }; var _hoisted_1$3 = ["aria-hidden", "aria-label"]; function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("div", { class: normalizeClass(["carousel-3d-slide", $options.computedClasses]), style: normalizeStyle($options.slideStyle), onClick: _cache[0] || (_cache[0] = ($event) => $options.goTo()), role: "group", "aria-roledescription": "slide", "aria-hidden": String(!$options.isCurrent), "aria-label": $options.slideAriaLabel }, [$options.shouldRenderContent ? renderSlot(_ctx.$slots, "default", { index: $props.index, isCurrent: $options.isCurrent, leftIndex: $options.leftIndex, rightIndex: $options.rightIndex }, void 0, void 0, 0) : createCommentVNode("", true)], 14, _hoisted_1$3); } var Slide_default = /*#__PURE__*/ _plugin_vue_export_helper_default(_sfc_main$3, [["render", _sfc_render$3]]); //#endregion //#region src/carousel-3d/core/carousel.js function getVisibleSlideCount(display, total) { return display > total ? total : display; } function getSideIndices({ currentIndex, total, visible, bias, dir, side }) { let count = (visible - 1) / 2; const isLeft = side === "left"; count = bias.toLowerCase() === side ? Math.ceil(count) : Math.floor(count); const indices = []; for (let position = 1; position <= count; position++) { const movesForward = isLeft && dir === "ltr" || !isLeft && dir !== "ltr"; indices.push(movesForward ? (currentIndex + position) % total : (currentIndex - position) % total); } return indices; } function getOutIndex({ currentIndex, total, visible, bias, dir, side }) { const isLeft = side === "left"; let offset = (visible - 1) / 2; offset = (bias.toLowerCase() === side ? Math.ceil(offset) : Math.floor(offset)) + 1; if (isLeft && dir === "ltr" || !isLeft && dir !== "ltr") return total - currentIndex - offset <= 0 ? -parseInt(total - currentIndex - offset, 10) : currentIndex + offset; return currentIndex - offset; } function getSafeIndex(index, total) { const targetIndex = parseInt(index, 10); return !Number.isFinite(targetIndex) || targetIndex < 0 || targetIndex >= total ? 0 : targetIndex; } function getStartIndex(index, total) { if (total === 0) return 0; const startIndex = Math.max(0, parseInt(index, 10) || 0); return startIndex > total - 1 ? total - 1 : startIndex; } //#endregion //#region src/carousel-3d/mixins/autoplay.js var isBrowser$1 = typeof window !== "undefined"; var autoplay = { props: { autoplay: { type: Boolean, default: false }, autoplayTimeout: { type: Number, default: 2e3 }, autoplayHoverPause: { type: Boolean, default: true } }, data() { return { autoplayInterval: null }; }, mounted() { if (!isBrowser$1) return; if (this.autoplayHoverPause) { this.$el.addEventListener("mouseenter", this.pauseAutoplay); this.$el.addEventListener("mouseleave", this.startAutoplay); } this.startAutoplay(); }, beforeUnmount() { if (!isBrowser$1) return; this.pauseAutoplay(); if (this.autoplayHoverPause) { this.$el.removeEventListener("mouseenter", this.pauseAutoplay); this.$el.removeEventListener("mouseleave", this.startAutoplay); } }, methods: { pauseAutoplay() { if (this.autoplayInterval) { clearInterval(this.autoplayInterval); this.autoplayInterval = null; } }, startAutoplay() { this.pauseAutoplay(); if (this.autoplay) this.autoplayInterval = setInterval(() => { this.dir === "ltr" ? this.goPrev() : this.goNext(); }, this.autoplayTimeout); } } }; //#endregion //#region src/carousel-3d/Controls.vue var _sfc_main$2 = { name: "controls", props: { /** * Width in pixels of the navigation buttons */ width: { type: [String, Number], default: 50 }, /** * Height in pixels of the navigation buttons */ height: { type: [String, Number], default: 60 }, /** * Text content of the navigation prev button */ prevHtml: { type: String, default: "&lsaquo;" }, /** * Text content of the navigation next button */ nextHtml: { type: String, default: "&rsaquo;" } }, inject: ["carousel"], computed: { parent() { return this.carousel; } } }; var _hoisted_1$2 = { class: "carousel-3d-controls" }; var _hoisted_2$1 = ["disabled"]; var _hoisted_3 = ["innerHTML"]; var _hoisted_4 = ["disabled"]; var _hoisted_5 = ["innerHTML"]; function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("div", _hoisted_1$2, [createElementVNode("button", { type: "button", class: normalizeClass(["prev", { disabled: !$options.parent.isPrevPossible }]), onClick: _cache[0] || (_cache[0] = ($event) => $options.parent.goPrev()), disabled: !$options.parent.isPrevPossible, style: normalizeStyle(`width: ${$props.width}px; height: ${$props.height}px; line-height: ${$props.height}px;`), "aria-label": "Previous slide" }, [renderSlot(_ctx.$slots, "prev", { goPrev: $options.parent.goPrev, disabled: !$options.parent.isPrevPossible }, () => [createElementVNode("span", { innerHTML: $props.prevHtml }, null, 8, _hoisted_3)], true)], 14, _hoisted_2$1), createElementVNode("button", { type: "button", class: normalizeClass(["next", { disabled: !$options.parent.isNextPossible }]), onClick: _cache[1] || (_cache[1] = ($event) => $options.parent.goNext()), disabled: !$options.parent.isNextPossible, style: normalizeStyle(`width: ${$props.width}px; height: ${$props.height}px; line-height: ${$props.height}px;`), "aria-label": "Next slide" }, [renderSlot(_ctx.$slots, "next", { goNext: $options.parent.goNext, disabled: !$options.parent.isNextPossible }, () => [createElementVNode("span", { innerHTML: $props.nextHtml }, null, 8, _hoisted_5)], true)], 14, _hoisted_4)]); } var Controls_default = /*#__PURE__*/ _plugin_vue_export_helper_default(_sfc_main$2, [["render", _sfc_render$2], ["__scopeId", "data-v-35bea2a2"]]); //#endregion //#region src/carousel-3d/Dots.vue var _sfc_main$1 = { name: "carousel-3d-dots", props: { position: { type: String, default: "bottom" } }, inject: ["carousel"], computed: { parent() { return this.carousel; }, total() { return this.parent.total; }, currentIndex() { return this.parent.currentIndex; } } }; var _hoisted_1$1 = ["aria-label"]; var _hoisted_2 = [ "aria-label", "aria-current", "onClick" ]; function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) { return openBlock(), createElementBlock("div", { class: normalizeClass(["carousel-3d-dots", `carousel-3d-dots--${$props.position}`]), role: "group", "aria-label": `Slide navigation (${$options.total} slides)` }, [(openBlock(true), createElementBlock(Fragment, null, renderList($options.total, (i) => { return openBlock(), createElementBlock("button", { key: i, type: "button", class: normalizeClass(["carousel-3d-dot", { "is-active": i - 1 === $options.currentIndex }]), "aria-label": `Go to slide ${i} of ${$options.total}`, "aria-current": i - 1 === $options.currentIndex ? "true" : void 0, onClick: ($event) => $options.parent.goSlide(i - 1) }, null, 10, _hoisted_2); }), 128))], 10, _hoisted_1$1); } var Dots_default = /*#__PURE__*/ _plugin_vue_export_helper_default(_sfc_main$1, [["render", _sfc_render$1], ["__scopeId", "data-v-e1ffb936"]]); //#endregion //#region src/carousel-3d/Carousel3d.vue var isBrowser = typeof window !== "undefined"; var noop = () => {}; function countSlides(nodes) { return nodes.reduce((count, node) => { if (node.type === Fragment && Array.isArray(node.children)) return count + countSlides(node.children); return node.type === Comment || node.type === Text ? count : count + 1; }, 0); } var _sfc_main = { name: "carousel3d", components: { Controls: Controls_default, Dots: Dots_default }, emits: [ "after-slide-change", "before-slide-change", "last-slide" ], provide() { return { carousel: this }; }, props: { count: { type: [Number, String], default: 0 }, perspective: { type: [Number, String], default: 35 }, display: { type: [Number, String], default: 5 }, loop: { type: Boolean, default: true }, animationSpeed: { type: [Number, String], default: 500 }, dir: { type: String, default: "rtl" }, width: { type: [Number, String], default: 360 }, height: { type: [Number, String], default: 270 }, border: { type: [Number, String], default: 1 }, space: { type: [Number, String], default: "auto" }, startIndex: { type: [Number, String], default: 0 }, clickable: { type: Boolean, default: true }, disable3d: { type: Boolean, default: false }, minSwipeDistance: { type: Number, default: 10 }, inverseScaling: { type: [Number, String], default: 300 }, controlsVisible: { type: Boolean, default: false }, controlsPrevHtml: { type: String, default: "&lsaquo;" }, controlsNextHtml: { type: String, default: "&rsaquo;" }, controlsWidth: { type: [String, Number], default: 50 }, controlsHeight: { type: [String, Number], default: 50 }, dots: { type: Boolean, default: false }, dotsPosition: { type: String, default: "bottom" }, horizonOffset: { type: [Number, String], default: 0 }, lazy: { type: Boolean, default: false }, beforeSlideChange: { type: Function, default: noop }, onLastSlide: { type: Function, default: noop }, onSlideChange: { type: Function, default: noop }, bias: { type: String, default: "left" }, onMainSlideClick: { type: Function, default: noop }, oneDirectional: { type: Boolean, default: false }, ariaLabel: { type: String, default: "3D carousel" } }, data() { return { viewport: 0, currentIndex: 0, total: 0, dragOffsetX: 0, dragStartX: 0, dragOffsetY: 0, dragStartY: 0, mousedown: false, zIndex: 998, animationTimer: null, navigationTimers: [] }; }, mixins: [autoplay], watch: { count() { this.computeData(); } }, created() { this.computeSlideData(true); }, computed: { isLastSlide() { return this.currentIndex === this.total - 1; }, isFirstSlide() { return this.currentIndex === 0; }, isNextPossible() { return this.total > 0 && !(!this.loop && this.isLastSlide); }, isPrevPossible() { return this.total > 0 && !(!this.loop && this.isFirstSlide); }, slideWidth() { const vw = this.viewport; const sw = parseInt(this.width) + parseInt(this.border, 10) * 2; return vw > 0 && vw < sw && isBrowser ? vw : sw; }, slideHeight() { const sw = parseInt(this.width, 10) + parseInt(this.border, 10) * 2; const sh = parseInt(parseInt(this.height) + this.border * 2, 10); const ar = this.calculateAspectRatio(sw, sh); return this.slideWidth / ar; }, visible() { return getVisibleSlideCount(this.display, this.total); }, hasHiddenSlides() { return this.total > this.visible; }, leftIndices() { return getSideIndices({ currentIndex: this.currentIndex, total: this.total, visible: this.visible, bias: this.bias, dir: this.dir, side: "left" }); }, rightIndices() { return getSideIndices({ currentIndex: this.currentIndex, total: this.total, visible: this.visible, bias: this.bias, dir: this.dir, side: "right" }); }, leftOutIndex() { return getOutIndex({ currentIndex: this.currentIndex, total: this.total, visible: this.visible, bias: this.bias, dir: this.dir, side: "left" }); }, rightOutIndex() { return getOutIndex({ currentIndex: this.currentIndex, total: this.total, visible: this.visible, bias: this.bias, dir: this.dir, side: "right" }); } }, methods: { /** * Go to next slide */ goNext() { if (this.total > 0 && this.isNextPossible) this.isLastSlide ? this.goSlide(0) : this.goSlide(this.currentIndex + 1); }, /** * Go to previous slide */ goPrev() { if (this.total > 0 && this.isPrevPossible) this.isFirstSlide ? this.goSlide(this.total - 1) : this.goSlide(this.currentIndex - 1); }, /** * Go to slide * @param {String} index of slide where to go */ goSlide(index) { if (this.total <= 0) return; const targetIndex = getSafeIndex(index, this.total); if (this.beforeSlideChange !== noop && this.beforeSlideChange(targetIndex, this.currentIndex) === false) return; this.currentIndex = targetIndex; if (this.isLastSlide) { if (this.onLastSlide !== noop) console.warn("onLastSlide deprecated, please use @last-slide"); this.onLastSlide(this.currentIndex); this.$emit("last-slide", this.currentIndex); } this.$emit("before-slide-change", this.currentIndex); clearTimeout(this.animationTimer); this.animationTimer = setTimeout(() => { this.animationTimer = null; this.animationEnd(); }, parseInt(this.animationSpeed, 10)); }, /** * Go to slide far slide */ goFar(index) { this.clearNavigationTimers(); let diff = index === this.total - 1 && this.isFirstSlide ? -1 : index - this.currentIndex; if (this.isLastSlide && index === 0) diff = 1; const diff2 = diff < 0 ? -diff : diff; let timeBuff = 0; let i = 0; while (i < diff2) { i += 1; const timer = setTimeout(() => { diff < 0 ? this.goPrev() : this.goNext(); }, diff2 === 1 ? 0 : timeBuff); this.navigationTimers.push(timer); timeBuff += this.animationSpeed / diff2; } }, clearNavigationTimers() { this.navigationTimers.forEach((timer) => clearTimeout(timer)); this.navigationTimers = []; }, /** * Trigger actions when animation ends */ animationEnd() { if (this.onSlideChange !== noop) console.warn("onSlideChange deprecated, please use @after-slide-change"); this.onSlideChange(this.currentIndex); this.$emit("after-slide-change", this.currentIndex); }, /** * Trigger actions when mouse is released * @param {Object} e The event object */ handleMouseup() { this.mousedown = false; this.dragOffsetX = 0; this.dragOffsetY = 0; }, /** * Trigger actions when mouse is pressed * @param {Object} e The event object */ handleMousedown(e) { if (!e.touches && e.cancelable) e.preventDefault(); const point = this.getEventPoint(e); if (!point) return; this.mousedown = true; this.dragStartX = point.clientX; this.dragStartY = point.clientY; }, /** * Trigger actions when mouse is pressed and then moved (mouse drag) * @param {Object} e The event object */ handleMousemove(e) { if (!this.mousedown) return; const point = this.getEventPoint(e); if (!point) return; const eventPosX = point.clientX; const eventPosY = point.clientY; const deltaX = this.dragStartX - eventPosX; const deltaY = this.dragStartY - eventPosY; this.dragOffsetX = deltaX; this.dragOffsetY = deltaY; if (Math.abs(this.dragOffsetY) > Math.abs(this.dragOffsetX)) return; if (this.dragOffsetX > this.minSwipeDistance) { this.handleMouseup(); this.goNext(); } else if (this.dragOffsetX < -this.minSwipeDistance) { this.handleMouseup(); this.goPrev(); } }, getEventPoint(e) { return e.touches && e.touches[0] || e.changedTouches && e.changedTouches[0] || e; }, addInteractionListeners() { if (window.PointerEvent) { this.$el.addEventListener("pointerdown", this.handleMousedown); this.$el.addEventListener("pointerup", this.handleMouseup); this.$el.addEventListener("pointercancel", this.handleMouseup); this.$el.addEventListener("pointerleave", this.handleMouseup); this.$el.addEventListener("pointermove", this.handleMousemove); return; } this.$el.addEventListener("touchstart", this.handleMousedown); this.$el.addEventListener("touchend", this.handleMouseup); this.$el.addEventListener("touchcancel", this.handleMouseup); this.$el.addEventListener("touchmove", this.handleMousemove); this.$el.addEventListener("mousedown", this.handleMousedown); this.$el.addEventListener("mouseup", this.handleMouseup); this.$el.addEventListener("mouseleave", this.handleMouseup); this.$el.addEventListener("mousemove", this.handleMousemove); }, removeInteractionListeners() { if (window.PointerEvent) { this.$el.removeEventListener("pointerdown", this.handleMousedown); this.$el.removeEventListener("pointerup", this.handleMouseup); this.$el.removeEventListener("pointercancel", this.handleMouseup); this.$el.removeEventListener("pointerleave", this.handleMouseup); this.$el.removeEventListener("pointermove", this.handleMousemove); return; } this.$el.removeEventListener("touchstart", this.handleMousedown); this.$el.removeEventListener("touchend", this.handleMouseup); this.$el.removeEventListener("touchcancel", this.handleMouseup); this.$el.removeEventListener("touchmove", this.handleMousemove); this.$el.removeEventListener("mousedown", this.handleMousedown); this.$el.removeEventListener("mouseup", this.handleMouseup); this.$el.removeEventListener("mouseleave", this.handleMouseup); this.$el.removeEventListener("mousemove", this.handleMousemove); }, /** * A mutation observer is used to detect changes to the containing node * in order to keep the magnet container in sync with the height its reference node. */ attachMutationObserver() { const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; if (MutationObserver) { const config = { childList: true, characterData: true, subtree: true }; this.mutationObserver = new MutationObserver(() => { this.$nextTick(() => { this.computeData(); }); }); if (this.$el) this.mutationObserver.observe(this.$el, config); } }, detachMutationObserver() { if (this.mutationObserver) this.mutationObserver.disconnect(); }, /** * Get the number of slides * @return {Number} Number of slides */ getSlideCount() { const defaultSlot = this.$slots.default; if (!defaultSlot) return 0; return countSlides(defaultSlot()); }, /** * Calculate slide with and keep defined aspect ratio * @return {Number} Aspect ratio number */ calculateAspectRatio(width, height) { return Math.min(width / height); }, /** * Re-compute the number of slides and current slide */ computeSlideData(firstRun) { this.total = this.getSlideCount(); if (this.total === 0) { this.currentIndex = 0; return; } if (firstRun || this.currentIndex >= this.total) this.currentIndex = getStartIndex(this.startIndex, this.total); }, computeData(firstRun) { this.computeSlideData(firstRun); this.viewport = this.$el.clientWidth; }, setSize() { this.viewport = this.$el.clientWidth; this.$el.style.height = this.slideHeight + "px"; const slider = this.$el.querySelector(".carousel-3d-slider"); if (slider) { slider.style.width = this.slideWidth + "px"; slider.style.height = this.slideHeight + "px"; } } }, mounted() { if (isBrowser) { this.computeData(true); this.attachMutationObserver(); window.addEventListener("resize", this.setSize); this.addInteractionListeners(); } }, beforeUnmount() { if (isBrowser) { this.detachMutationObserver(); this.removeInteractionListeners(); window.removeEventListener("resize", this.setSize); } clearTimeout(this.animationTimer); this.clearNavigationTimers(); } }; var _hoisted_1 = ["aria-label"]; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { const _component_controls = resolveComponent("controls"); const _component_dots = resolveComponent("dots"); return openBlock(), createElementBlock("div", { class: "carousel-3d-container", style: normalizeStyle({ height: $options.slideHeight + "px" }), role: "region", "aria-roledescription": "carousel", "aria-label": $props.ariaLabel, tabindex: "0", onKeydown: [_cache[0] || (_cache[0] = withKeys(withModifiers((...args) => $options.goPrev && $options.goPrev(...args), ["prevent"]), ["left"])), _cache[1] || (_cache[1] = withKeys(withModifiers((...args) => $options.goNext && $options.goNext(...args), ["prevent"]), ["right"]))] }, [ createElementVNode("div", { class: "carousel-3d-slider", style: normalizeStyle({ width: $options.slideWidth + "px", height: $options.slideHeight + "px" }) }, [renderSlot(_ctx.$slots, "default", {}, void 0, true)], 4), $props.controlsVisible ? (openBlock(), createBlock(_component_controls, { key: 0, "next-html": $props.controlsNextHtml, "prev-html": $props.controlsPrevHtml, width: $props.controlsWidth, height: $props.controlsHeight }, { prev: withCtx((slotProps) => [renderSlot(_ctx.$slots, "prev", normalizeProps(guardReactiveProps(slotProps)), void 0, true)]), next: withCtx((slotProps) => [renderSlot(_ctx.$slots, "next", normalizeProps(guardReactiveProps(slotProps)), void 0, true)]), _: 3 }, 8, [ "next-html", "prev-html", "width", "height" ])) : createCommentVNode("", true), $props.dots ? (openBlock(), createBlock(_component_dots, { key: 1, position: $props.dotsPosition }, null, 8, ["position"])) : createCommentVNode("", true) ], 44, _hoisted_1); } var Carousel3d_default = /*#__PURE__*/ _plugin_vue_export_helper_default(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-db4a8480"]]); //#endregion //#region src/carousel-3d/index.js var install = (app) => { app.component("carousel3d", Carousel3d_default); app.component("slide", Slide_default); }; var carousel_3d_default = { install }; //#endregion export { Carousel3d_default as Carousel3d, Slide_default as Slide, carousel_3d_default as default }; //# sourceMappingURL=carousel-3d.es.mjs.map