Skip to content

Instantly share code, notes, and snippets.

@b4n92uid
Last active August 17, 2021 22:50
Show Gist options
  • Save b4n92uid/7d52a2928614f00ea8683ae3087bd90f to your computer and use it in GitHub Desktop.
Save b4n92uid/7d52a2928614f00ea8683ae3087bd90f to your computer and use it in GitHub Desktop.
[Vuetify] Swiper
<template>
<!-- Slider main container -->
<div
class="ok-swiper"
:class="{
'--fill-height': fillHeight,
'--pagination': pagination,
'--navigation': navigation,
'--navigation-ex': navigationEx
}"
>
<div v-if="gradient" :style="gradientStyle" class="__gradient"></div>
<div v-if="navigationEx" class="__prev">
<v-btn icon @click="prev"><v-icon>mdi-menu-left</v-icon></v-btn>
</div>
<div class="swiper-container" ref="container">
<div class="swiper-wrapper">
<div
class="swiper-slide"
:class="{
'swiper-slide-auto': slidesPerView == 'auto',
...slideClassObject
}"
v-for="(item, index) in items"
:key="itemValue ? item[itemValue] : index"
>
<slot :item="item" :index="index"></slot>
</div>
</div>
<div class="swiper-pagination" ref="pagination" v-if="pagination">
<slot name="pagination"></slot>
</div>
<template v-if="navigation">
<slot name="button-prev">
<div class="swiper-button-prev" ref="buttonPrev">
<v-btn small fab
><v-icon large>{{
$vuetify.rtl ? "mdi-menu-right" : "mdi-menu-left"
}}</v-icon></v-btn
>
</div>
</slot>
<slot name="button-next">
<div class="swiper-button-next" ref="buttonNext">
<v-btn small fab
><v-icon large>{{
$vuetify.rtl ? "mdi-menu-left" : "mdi-menu-right"
}}</v-icon></v-btn
>
</div>
</slot>
</template>
<div class="swiper-scrollbar" ref="scrollbar" v-if="scrollbar">
<slot name="scrollbar"></slot>
</div>
</div>
<div v-if="navigationEx" class="__next">
<v-btn icon @click="next"><v-icon>mdi-menu-right</v-icon></v-btn>
</div>
</div>
</template>
<script>
import { isNumber, merge } from "lodash";
import { classFormatValue } from "@/utils/Array";
import Swiper from "swiper";
import "swiper/dist/css/swiper.min.css";
export default {
props: {
items: {
type: Array,
default: () => []
},
itemValue: {
type: String,
default: null
},
autoplay: {
type: [Boolean, Number],
default: false
},
slidesPerView: {
type: [Number, String],
default: 1.1
},
spaceBetween: {
type: Number,
default: 16
},
pagination: {
type: Boolean,
default: false
},
navigation: {
type: Boolean,
default: false
},
navigationEx: {
type: Boolean,
default: false
},
scrollbar: {
type: Boolean,
default: false
},
options: {
type: Object,
default: () => ({})
},
currentSlide: {
type: Number,
default: 0
},
gradient: {
type: Boolean,
default: false
},
gradientColor: {
type: String,
default: "white"
},
slideClass: {
type: [Object, String, Array],
default: null
},
fillHeight: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
swiper: null
};
},
mounted() {
const config = {
direction: "horizontal",
slidesPerView: this.slidesPerView,
spaceBetween: this.spaceBetween,
on: {
slideChange: () => this.$emit("slideChange", this.swiper.activeIndex)
}
};
if (this.autoplay !== false)
config.autoplay = {
delay: isNumber(this.autoplay) ? this.autoplay : 5000
};
if (this.pagination)
config.pagination = {
el: this.$refs.pagination
};
if (this.navigation)
config.navigation = {
nextEl: this.$refs.buttonNext,
prevEl: this.$refs.buttonPrev
};
if (this.scrollbar)
config.scrollbar = {
el: this.$refs.scrollbar
};
merge(config, this.options);
this.swiper = new Swiper(this.$refs.container, config);
if (this.disabled) {
this.swiper.detachEvents();
}
},
updated() {
this.swiper.update();
},
watch: {
currentSlide(index) {
this.swiper.slideTo(index);
},
slidesPerView(v) {
this.swiper.params.slidesPerView = v;
},
spaceBetween(v) {
this.swiper.params.spaceBetween = v;
},
disabled() {
if (this.disabled) this.swiper.detachEvents();
else this.swiper.attachEvents();
}
},
computed: {
gradientStyle() {
const cssColor =
this.gradientColor[0] === "#"
? this.gradientColor
: `var(--v-${this.gradientColor}-base)`;
return {
background: `linear-gradient(to left, ${cssColor} 5%, transparent 25%)`
};
},
slideClassObject() {
return classFormatValue(this.slideClass);
}
},
methods: {
update() {
this.swiper.update();
},
prev() {
this.swiper.slidePrev();
},
next() {
this.swiper.slideNext();
}
}
};
</script>
<style lang="scss">
.ok-swiper {
position: relative;
.__gradient {
content: "";
pointer-events: none;
position: absolute;
left: -1px;
right: -1px;
bottom: -1px;
top: -1px;
z-index: 2;
}
.swiper-container {
.swiper-button-prev {
background-image: none;
width: auto;
height: auto;
}
.swiper-button-next {
background-image: none;
width: auto;
height: auto;
}
.swiper-button-next.swiper-button-disabled,
.swiper-button-prev.swiper-button-disabled {
pointer-events: all;
}
.swiper-slide.swiper-slide-auto {
width: auto;
}
}
}
.ok-swiper.--navigation-ex {
display: flex;
align-items: center;
gap: 4px;
.swiper-container {
flex-grow: 1;
}
> .__prev {
flex-grow: 0;
}
> .__next {
flex-grow: 0;
}
}
.ok-swiper.--fill-height {
.swiper-wrapper {
align-items: stretch;
.swiper-slide {
height: auto;
}
}
}
.ok-swiper.--pagination {
--pagination-height: 48px;
.swiper-container {
padding-bottom: var(--pagination-height);
}
.swiper-pagination-bullets {
bottom: calc(var(--pagination-height) / 2 - 24px / 2);
.swiper-pagination-bullet-active {
background: var(--v-primary-base);
}
}
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment