vue3+ts+EmblaCarousel轮播动画

436 阅读1分钟
  • 平替swiperembla-carousel插件,无视vue、react等框架的限制,并且更加轻量
  • 代码示例如下:
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import EmblaCarousel from 'embla-carousel'
import Autoplay from 'embla-carousel-autoplay'

const emblaNode = ref<HTMLElement | null>(null)
const emblaApi = ref<any>(null)
const currentIndex = ref(0)

const scrollPrev = () => emblaApi.value?.scrollPrev()
const scrollNext = () => emblaApi.value?.scrollNext()

onMounted(() => {
  if (emblaNode.value) {
    emblaApi.value = EmblaCarousel(
      emblaNode.value,
      {
        loop: true,
        align: 'center',
        containScroll: false,
        dragFree: false
      },
      [
        Autoplay({
          delay: 2000,
          stopOnInteraction: false
        })
      ]
    )

    emblaApi.value.on('select', () => {
      currentIndex.value = emblaApi.value?.selectedScrollSnap() || 0
    })
  }
})
</script>

<template>
  <div class="embla">
    <div class="embla__viewport" ref="emblaNode">
      <div class="embla__container">
        <div class="embla__slide" v-for="n in 5" :key="n">
          <div class="embla__slide__number">
            Slide {{ n }}
          </div>
        </div>
      </div>
    </div>

    <div class="embla__controls">
      <button class="embla__button" @click="scrollPrev">
        <span class="embla__button__text">Prev</span>
      </button>

      <div class="embla__dots">
        <button
          v-for="n in 5"
          :key="n"
          class="embla__dot"
          :class="{ 'embla__dot--selected': currentIndex === n - 1 }"
          @click="emblaApi?.scrollTo(n - 1)"
        />
      </div>

      <button class="embla__button" @click="scrollNext">
        <span class="embla__button__text">Next</span>
      </button>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.embla {
  max-width: 48rem;
  margin: auto;
  
  &__viewport {
    overflow: hidden;
  }

  &__container {
    display: flex;
    user-select: none;
    -webkit-touch-callout: none;
    -webkit-tap-highlight-color: transparent;
  }

  &__slide {
    position: relative;
    flex: 0 0 100%; // 关键:设置每个 slide 宽度为 100%
    min-width: 0;

    &__number {
      display: flex;
      align-items: center;
      justify-content: center;
      height: 20rem;
      font-size: 2rem;
      background-color: #f5f5f5;
      border-radius: 0.5rem;
    }
  }

  &__controls {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem;
  }

  &__button {
    padding: 0.5rem 1rem;
    color: white;
    cursor: pointer;
    background-color: #4a5568;
    border: none;
    border-radius: 0.25rem;

    &:hover {
      background-color: #2d3748;
    }
  }

  &__dots {
    display: flex;
    gap: 0.5rem;
    padding: 0 1rem;
  }

  &__dot {
    width: 0.75rem;
    height: 0.75rem;
    cursor: pointer;
    background-color: #e2e8f0;
    border: none;
    border-radius: 50%;
    padding: 0;

    &--selected {
      background-color: #4a5568;
    }
  }
}
</style>