Skip to content

Instantly share code, notes, and snippets.

@brolnickij
Last active October 12, 2021 11:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save brolnickij/e8ba6e6f10f20023d3a6df18917432ab to your computer and use it in GitHub Desktop.
Save brolnickij/e8ba6e6f10f20023d3a6df18917432ab to your computer and use it in GitHub Desktop.
vm-gallery
<template lang="pug">
.vm-gallery
div(
:style="{ transitionDuration: transitionDuration, transform: translate3DPosition }"
ref="galleryItems"
).vm-gallery-items
div(v-for="imageItem in imageNodes").vm-gallery-item
img(
:src="imageItem.src"
:key="imageItem.src"
).vm-gallery-item-image
.vm-gallery-controls
button(@click="sliderStepController('PREV')").vm-gallery-controls-button.vm-gallery-controls-button_left
// Your left button icon
span.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_left
button(@click="sliderStepController('NEXT')").vm-gallery-controls-button.vm-gallery-controls-button_right
// Your right button icon
span.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_right
</template>
<script>
export default {
name: 'VmGallery',
props: {
/**
* Array of image links
*/
images: {
type: Array,
required: true,
validate: images => images?.length >= 3
}
},
data() {
return {
// View
transitionDuration: '300ms',
translate3DPosition: 0,
imageNodes: [],
// System
currentSliderPosition: null,
sliderReadyToSwitch: true
}
},
methods: {
calculateCurrentSliderPosition(CURRENT_VIEW_POSITION_INDEX) {
const IMAGE_WIDTH = 820
const IMAGE_PADDING = 25
const TOTAL_POSITION = -((IMAGE_WIDTH + IMAGE_PADDING) * (CURRENT_VIEW_POSITION_INDEX - 1)) + 197.5
return `translate3d(${TOTAL_POSITION}px, 0, 0)`
},
sliderStepController(DIRECTION) {
if (!this.sliderReadyToSwitch) {
return
}
if (DIRECTION === 'PREV') {
this.currentSliderPosition -= 1
}
if (DIRECTION === 'NEXT') {
this.currentSliderPosition += 1
}
if (this.imageNodes[this.currentSliderPosition - 1].endImageNode) {
// Bad code (if you find a better solution, write me)
setTimeout(() => {
this.resetSlider()
this.transitionDuration = '0ms'
setTimeout(() => {
this.transitionDuration = '300ms'
this.sliderReadyToSwitch = true
}, 20)
}, 300)
}
this.translate3DPosition = this.calculateCurrentSliderPosition(this.currentSliderPosition)
},
// System
generateImageNodes() {
this.imageNodes = this.generateImagesNodeMetaInformation(
[
this.images[this.images.length - 1],
...this.images,
...this.images,
this.images[0],
this.images[1]
]
)
},
generateImagesNodeMetaInformation(imagesNode) {
return imagesNode.map((src, imageNodeIndex, imagesNode) => {
const IS_END_IMAGE_NODE = !!(imageNodeIndex === 1 || imagesNode.length - 2 === imageNodeIndex)
return {
src,
endImageNode: IS_END_IMAGE_NODE
}
})
},
getCenterPositionImageNumber() {
return Math.round((this.imageNodes.length - 1) / 2) + 1
},
resetSlider() {
this.currentSliderPosition = this.getCenterPositionImageNumber()
this.translate3DPosition = this.calculateCurrentSliderPosition(this.currentSliderPosition)
},
onEventListeners() {
this.$refs.galleryItems.addEventListener('transitionend', () => {
this.sliderReadyToSwitch = true
})
this.$refs.galleryItems.addEventListener('transitionstart', () => {
this.sliderReadyToSwitch = false
})
}
},
created() {
this.generateImageNodes()
this.resetSlider()
},
mounted() {
this.onEventListeners()
},
activated() {
this.onEventListeners()
}
}
</script>
<style lang="sass">
.vm-gallery
position: relative
width: 1240px
min-height: 440px
overflow: hidden
.vm-gallery-items
position: absolute
display: flex
min-height: 440px
transition-timing-function: cubic-bezier(0.22, 1, 0.36, 1)
.vm-gallery-item
width: 820px
height: 440px
margin-right: 12.5px
margin-left: 12.5px
border-radius: 15px
> img
display: block
width: 100%
height: 100%
border-radius: 25px
.vm-gallery-controls
position: absolute
top: 0
z-index: 3
display: flex
width: 100%
height: 100%
.vm-gallery-controls-button
position: relative
display: block
width: 50%
height: 100%
padding: 0
margin: 0
cursor: pointer
border: none
outline: none
box-shadow: none
opacity: 0
transition: opacity .3s cubic-bezier(0.22, 1, 0.36, 1)
&_right
background: linear-gradient(90deg, rgb(0 0 255 / 0%) 72%, rgb(0 0 0 / 20%))
&:hover
opacity: 1
&_left
background: linear-gradient(-90deg, rgb(0 0 255 / 0%) 72%, rgb(0 0 0 / 20%))
&:hover
opacity: 1
.vm-gallery-controls-button-arrow-icon
position: absolute
color: #fff
&_right
right: 70px
&_left
left: 70px
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment