Skip to content

Instantly share code, notes, and snippets.

@vinayakkulkarni
Forked from brolnickij/VmGallery.vue
Created October 10, 2021 03:23
Show Gist options
  • Select an option

  • Save vinayakkulkarni/fd894c6a0e42956ab3f5f221a6676c42 to your computer and use it in GitHub Desktop.

Select an option

Save vinayakkulkarni/fd894c6a0e42956ab3f5f221a6676c42 to your computer and use it in GitHub Desktop.

Revisions

  1. @brolnickij brolnickij revised this gist Oct 10, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions VmGallery.vue
    Original file line number Diff line number Diff line change
    @@ -73,6 +73,7 @@
    }
    if (this.imageNodes[this.currentSliderPosition - 1].endImageNode) {
    // Bad code (if you find a better solution, write me)
    setTimeout(() => {
    this.resetSlider()
    this.transitionDuration = '0ms'
  2. @brolnickij brolnickij revised this gist Oct 9, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions VmGallery.vue
    Original file line number Diff line number Diff line change
    @@ -13,11 +13,11 @@
    .vm-gallery-controls
    button(@click="sliderStepController('PREV')").vm-gallery-controls-button.vm-gallery-controls-button_left
    // Your left button icon
    button.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_left
    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
    button.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_right
    span.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_right

    </template>

  3. @brolnickij brolnickij revised this gist Oct 9, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion VmGallery.vue
    Original file line number Diff line number Diff line change
    @@ -12,7 +12,7 @@

    .vm-gallery-controls
    button(@click="sliderStepController('PREV')").vm-gallery-controls-button.vm-gallery-controls-button_left
    // Your right button icon
    // Your left button icon
    button.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
  4. @brolnickij brolnickij renamed this gist Oct 9, 2021. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. @brolnickij brolnickij revised this gist Oct 9, 2021. 1 changed file with 3 additions and 9 deletions.
    12 changes: 3 additions & 9 deletions vm-gallery.vue
    Original file line number Diff line number Diff line change
    @@ -11,17 +11,11 @@
    ).vm-gallery-item-image

    .vm-gallery-controls
    button(
    @click="sliderStepController('PREV')"
    aria-label="Предыдущее изображение"
    ).vm-gallery-controls-button.vm-gallery-controls-button_left
    button(@click="sliderStepController('PREV')").vm-gallery-controls-button.vm-gallery-controls-button_left
    // Your right button icon
    button.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_left

    button(
    @click="sliderStepController('NEXT')"
    aria-label="Следующее изображение"
    ).vm-gallery-controls-button.vm-gallery-controls-button_right
    button(@click="sliderStepController('NEXT')").vm-gallery-controls-button.vm-gallery-controls-button_right
    // Your right button icon
    button.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_right

    @@ -34,7 +28,7 @@
    props: {
    /**
    * Массив ссылок на изображения
    * Array of image links
    */
    images: {
    type: Array,
  6. @brolnickij brolnickij revised this gist Oct 9, 2021. 1 changed file with 1 addition and 19 deletions.
    20 changes: 1 addition & 19 deletions vm-gallery.vue
    Original file line number Diff line number Diff line change
    @@ -5,10 +5,8 @@
    ref="galleryItems"
    ).vm-gallery-items
    div(v-for="imageItem in imageNodes").vm-gallery-item
    component(
    :is="imageTag"
    img(
    :src="imageItem.src"
    :alt="imageAlt"
    :key="imageItem.src"
    ).vm-gallery-item-image

    @@ -42,22 +40,6 @@
    type: Array,
    required: true,
    validate: value => value?.length >= 3
    },
    /**
    * Для кастомных тегов (может использоваться для lazy-loading)
    */
    imageTag: {
    type: String,
    default: 'img'
    },
    /**
    * Alt атрибут для изображений (SEO)
    */
    imageAlt: {
    type: String,
    required: true
    }
    },
  7. @brolnickij brolnickij revised this gist Oct 9, 2021. 1 changed file with 4 additions and 20 deletions.
    24 changes: 4 additions & 20 deletions vm-gallery.vue
    Original file line number Diff line number Diff line change
    @@ -17,32 +17,20 @@
    @click="sliderStepController('PREV')"
    aria-label="Предыдущее изображение"
    ).vm-gallery-controls-button.vm-gallery-controls-button_left
    vm-icon(
    icon-name="left-gallery-arrow"
    inherit-fill-color
    default-icon-size
    inline-mode
    ).vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_left
    // Your right button icon
    button.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_left

    button(
    @click="sliderStepController('NEXT')"
    aria-label="Следующее изображение"
    ).vm-gallery-controls-button.vm-gallery-controls-button_right
    vm-icon(
    icon-name="right-gallery-arrow"
    inherit-fill-color
    default-icon-size
    inline-mode
    ).vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_right
    // Your right button icon
    button.vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_right

    </template>


    <script>
    // TODO Refactoring
    import VmIcon from '@vm/ui/vm-icon/Icon.vue'
    export default {
    name: 'VmGallery',
    @@ -176,10 +164,6 @@
    activated() {
    this.onEventListeners()
    },
    components: {
    VmIcon
    }
    }
    </script>
  8. @brolnickij brolnickij renamed this gist Oct 9, 2021. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  9. @brolnickij brolnickij created this gist Oct 9, 2021.
    263 changes: 263 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,263 @@
    <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
    component(
    :is="imageTag"
    :src="imageItem.src"
    :alt="imageAlt"
    :key="imageItem.src"
    ).vm-gallery-item-image

    .vm-gallery-controls
    button(
    @click="sliderStepController('PREV')"
    aria-label="Предыдущее изображение"
    ).vm-gallery-controls-button.vm-gallery-controls-button_left
    vm-icon(
    icon-name="left-gallery-arrow"
    inherit-fill-color
    default-icon-size
    inline-mode
    ).vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_left

    button(
    @click="sliderStepController('NEXT')"
    aria-label="Следующее изображение"
    ).vm-gallery-controls-button.vm-gallery-controls-button_right
    vm-icon(
    icon-name="right-gallery-arrow"
    inherit-fill-color
    default-icon-size
    inline-mode
    ).vm-gallery-controls-button-arrow-icon.vm-gallery-controls-button-arrow-icon_right

    </template>


    <script>
    // TODO Refactoring
    import VmIcon from '@vm/ui/vm-icon/Icon.vue'


    export default {
    name: 'VmGallery',

    props: {
    /**
    * Массив ссылок на изображения
    */
    images: {
    type: Array,
    required: true,
    validate: value => value?.length >= 3
    },

    /**
    * Для кастомных тегов (может использоваться для lazy-loading)
    */
    imageTag: {
    type: String,
    default: 'img'
    },

    /**
    * Alt атрибут для изображений (SEO)
    */
    imageAlt: {
    type: String,
    required: true
    }
    },

    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) {
    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()
    },

    components: {
    VmIcon
    }
    }
    </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>