Carousel

Embla를 사용하여 제작된 모션 및 스와이프가 있는 회전목마.

Usage

회전목마 구성요소를 사용하여 회전목마의 항목 목록을 표시합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    loop
    arrows
    :autoplay="{ delay: 2000 }"
    wheel-gestures
    :prev="{ variant: 'solid' }"
    :next="{ variant: 'solid' }"
    :items="items"
    :ui="{
      item: 'basis-1/3 ps-0',
      prev: 'sm:start-8',
      next: 'sm:end-8',
      container: 'ms-0'
    }"
  >
    <img :src="item" width="320" height="320">
  </UCarousel>
</template>
마우스를 사용하여 바탕 화면에서 회전목마를 수평으로 끕니다.

Items 이미지

items Prop을 배열로 사용하고 기본 슬롯을 사용하여 각 항목을 렌더링합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" class="w-full max-w-xs mx-auto">
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>

또한 다음 속성을 사용하여 객체 배열을 전달할 수 있습니다.

  • class?: any - class?: any
  • ui?: { item?: ClassNameValue }

item에서 basis/width 유틸리티 클래스를 사용하여 표시되는 항목 수를 제어할 수 있습니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/468/468?random=1',
  'https://picsum.photos/468/468?random=2',
  'https://picsum.photos/468/468?random=3',
  'https://picsum.photos/468/468?random=4',
  'https://picsum.photos/468/468?random=5',
  'https://picsum.photos/468/468?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" :items="items" :ui="{ item: 'basis-1/3' }">
    <img :src="item" width="234" height="234" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>

방향 지정

orientation Prop을 사용하여 Progress의 방향을 변경합니다. 기본값은 horizontal입니다.

마우스를 사용하여 회전 목마를 바탕 화면에서 세로로 끕니다.
<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    orientation="vertical"
    :items="items"
    :ui="{ container: 'h-[336px]' }"
    class="w-full max-w-xs mx-auto"
  >
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>
컨테이너에 height를 수직 방향으로 지정해야 합니다.

Arrows

arrows prop을 사용하여 prev 및 next 버튼을 표시합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" arrows :items="items" class="w-full max-w-xs mx-auto">
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>

Prev/다음

prev 및 next props를 사용하여 Button props를 사용하여 prev 및 next 버튼을 사용자 정의합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    arrows
    :prev="{ color: 'primary' }"
    :next="{ variant: 'solid' }"
    :items="items"
    class="w-full max-w-xs mx-auto"
  >
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>

Prev/다음 아이콘

prev-icon 및 next-icon 소품을 사용하여 Icon 버튼을 사용자 정의합니다. 기본값은 i-lucide-arrow-left/i-lucide-arrow-right입니다.

<script setup lang="ts">
defineProps<{
  prevIcon?: string
  nextIcon?: string
}>()

const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    arrows
    :prev-icon="prevIcon"
    :next-icon="nextIcon"
    :items="items"
    class="w-full max-w-xs mx-auto"
  >
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>
app.config.ts에서 ui.icons.arrowLeft/ui.icons.arrowRight 키 아래의 이러한 아이콘을 전역적으로 사용자 정의할 수 있습니다.
ui.icons.arrowLeft/ui.icons.arrowRight 키 아래의 vite.config.ts에서 이러한 아이콘을 전역적으로 사용자 정의할 수 있습니다.

Dots 파일

dots 소품을 사용하여 특정 슬라이드로 스크롤할 점 목록을 표시합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" dots :items="items" class="w-full max-w-xs mx-auto">
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>

점 수는 뷰에 표시되는 슬라이드 수에 따라 결정됩니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel v-slot="{ item }" dots :items="items" :ui="{ item: 'basis-1/3' }">
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>

플러그인 Name

Carousel 컴포넌트는 공식 Embla Carousel plugins를 구현합니다.

자동 재생

이 플러그인은 autoplay 기능을 사용하여 Embla Carousel을 확장하는 데 사용됩니다.

autoplay prop를 boolean 또는 객체로 사용하여 Autoplay plugin를 구성합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/468/468?random=1',
  'https://picsum.photos/468/468?random=2',
  'https://picsum.photos/468/468?random=3',
  'https://picsum.photos/468/468?random=4',
  'https://picsum.photos/468/468?random=5',
  'https://picsum.photos/468/468?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    loop
    arrows
    dots
    :autoplay="{ delay: 2000 }"
    :items="items"
    :ui="{ item: 'basis-1/3' }"
  >
    <img :src="item" width="234" height="234" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>
이 예제에서는 무한 회전목마를 위해 loop prop을 사용하고 있습니다.

자동으로 스크롤

이 플러그인은 auto scroll 기능을 사용하여 Embla Carousel을 확장하는 데 사용됩니다.

auto-scroll prop을 부울 또는 객체로 사용하여 Auto Scroll plugin를 구성합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/468/468?random=1',
  'https://picsum.photos/468/468?random=2',
  'https://picsum.photos/468/468?random=3',
  'https://picsum.photos/468/468?random=4',
  'https://picsum.photos/468/468?random=5',
  'https://picsum.photos/468/468?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    loop
    dots
    arrows
    auto-scroll
    :items="items"
    :ui="{ item: 'basis-1/3' }"
  >
    <img :src="item" width="234" height="234" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>
이 예제에서는 무한 회전목마를 위해 loop prop을 사용하고 있습니다.

자동 높이

이 플러그인은 Embla Carousel을 auto height 기능으로 확장하는 데 사용되며, 회전목마 컨테이너의 높이를 보는 가장 높은 슬라이드의 높이에 맞게 변경합니다.

auto-height prop를 부울 또는 객체로 사용하여 Auto Height plugin를 구성합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/320?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/320?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/320?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    auto-height
    arrows
    dots
    :items="items"
    :ui="{
      container: 'transition-[height]',
      controls: 'absolute -top-8 inset-x-12',
      dots: '-top-7',
      dot: 'w-6 h-1'
    }"
    class="w-full max-w-xs mx-auto"
  >
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>
이 예제에서는 컨테이너에 transition-[height] 클래스를 추가하여 높이 변화를 애니메이션합니다.

클래스 이름

Class Names 는 Embla Carousel 용 class name toggle 유틸리티 플러그인으로 회전목마에서 클래스 이름의 토글을 자동화 할 수 있습니다.

class-names prop을 부울 또는 객체로 사용하여 Class Names plugin를 구성합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/528/528?random=1',
  'https://picsum.photos/528/528?random=2',
  'https://picsum.photos/528/528?random=3',
  'https://picsum.photos/528/528?random=4',
  'https://picsum.photos/528/528?random=5',
  'https://picsum.photos/528/528?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    class-names
    arrows
    :items="items"
    :ui="{
      item: 'basis-[70%] transition-opacity [&:not(.is-snapped)]:opacity-10'
    }"
    class="mx-auto max-w-sm"
  >
    <img :src="item" width="264" height="264" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>
이 예제에서는 item에 transition-opacity [&:not(.is-snapped)]:opacity-10 클래스를 추가하여 불투명도 변화를 애니메이션합니다.

Fade 의 발음을 ### Fade en

이 플러그인은 Embla Carousel 스크롤 기능을 fade transitions로 대체하는 데 사용됩니다.

fade prop를 부울 또는 객체로 사용하여 Fade plugin를 구성합니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    fade
    arrows
    dots
    :items="items"
    class="w-full max-w-xs mx-auto"
  >
    <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>

Wheel 동작

이 플러그인은 Embla Carousel을 확장하는 데 사용되며 use 마우스 / 트랙패드 wheel를 사용하여 회전목마를 탐색 할 수 있습니다.

wheel-gestures prop를 부울 또는 객체로 사용하여 Wheel Gestures plugin를 구성합니다.

마우스 휠을 사용하여 회전목마를 회전합니다.
<script setup lang="ts">
const items = [
  'https://picsum.photos/468/468?random=1',
  'https://picsum.photos/468/468?random=2',
  'https://picsum.photos/468/468?random=3',
  'https://picsum.photos/468/468?random=4',
  'https://picsum.photos/468/468?random=5',
  'https://picsum.photos/468/468?random=6'
]
</script>

<template>
  <UCarousel
    v-slot="{ item }"
    loop
    wheel-gestures
    :items="items"
    :ui="{ item: 'basis-1/3' }"
  >
    <img :src="item" width="234" height="234" class="rounded-lg" loading="lazy">
  </UCarousel>
</template>

examples 예제

썸네일 포함

emblaApi에서 scrollTo 메서드를 사용하여 특정 슬라이드로 이동하는 회전목마 아래에 축소판을 표시할 수 있습니다.

<script setup lang="ts">
const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]

const carousel = useTemplateRef('carousel')
const activeIndex = ref(0)

function onClickPrev() {
  activeIndex.value--
}
function onClickNext() {
  activeIndex.value++
}
function onSelect(index: number) {
  activeIndex.value = index
}

function select(index: number) {
  activeIndex.value = index

  carousel.value?.emblaApi?.scrollTo(index)
}
</script>

<template>
  <div class="flex-1 w-full">
    <UCarousel
      ref="carousel"
      v-slot="{ item }"
      arrows
      :items="items"
      :prev="{ onClick: onClickPrev }"
      :next="{ onClick: onClickNext }"
      class="w-full max-w-xs mx-auto"
      @select="onSelect"
    >
      <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
    </UCarousel>

    <div class="flex gap-1 justify-between pt-4 max-w-xs mx-auto">
      <div
        v-for="(item, index) in items"
        :key="index"
        class="size-11 opacity-25 hover:opacity-100 transition-opacity"
        :class="{ 'opacity-100': activeIndex === index }"
        @click="select(index)"
      >
        <img :src="item" width="44" height="44" class="rounded-lg" loading="lazy">
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { ref, useTemplateRef } from 'vue'

const items = [
  'https://picsum.photos/640/640?random=1',
  'https://picsum.photos/640/640?random=2',
  'https://picsum.photos/640/640?random=3',
  'https://picsum.photos/640/640?random=4',
  'https://picsum.photos/640/640?random=5',
  'https://picsum.photos/640/640?random=6'
]

const carousel = useTemplateRef('carousel')
const activeIndex = ref(0)

function onClickPrev() {
  activeIndex.value--
}
function onClickNext() {
  activeIndex.value++
}
function onSelect(index: number) {
  activeIndex.value = index
}

function select(index: number) {
  activeIndex.value = index

  carousel.value?.emblaApi?.scrollTo(index)
}
</script>

<template>
  <div class="flex-1 w-full">
    <UCarousel
      ref="carousel"
      v-slot="{ item }"
      arrows
      :items="items"
      :prev="{ onClick: onClickPrev }"
      :next="{ onClick: onClickNext }"
      class="w-full max-w-xs mx-auto"
      @select="onSelect"
    >
      <img :src="item" width="320" height="320" class="rounded-lg" loading="lazy">
    </UCarousel>

    <div class="flex gap-1 justify-between pt-4 max-w-xs mx-auto">
      <div
        v-for="(item, index) in items"
        :key="index"
        class="size-11 opacity-25 hover:opacity-100 transition-opacity"
        :class="{ 'opacity-100': activeIndex === index }"
        @click="select(index)"
      >
        <img :src="item" width="44" height="44" class="rounded-lg" loading="lazy">
      </div>
    </div>
  </div>
</template>

API 파일

Props (### Props)

Prop Default Type
as
'div'
any

The element or component this component should render as.

prev
{ size: 'md', color: 'neutral', variant: 'link' }
Omit<ButtonProps, LinkPropsKeys>

Configure the prev button when arrows are enabled.

prevIcon
appConfig.ui.icons.arrowLeft
any

The icon displayed in the prev button.

next
{ size: 'md', color: 'neutral', variant: 'link' }
Omit<ButtonProps, LinkPropsKeys>

Configure the next button when arrows are enabled.

nextIcon
appConfig.ui.icons.arrowRight
any

The icon displayed in the next button.

arrows
false
boolean

Display prev and next buttons to scroll the carousel.

dots
false
boolean

Display dots to scroll to a specific slide.

orientation
'horizontal'
"vertical" | "horizontal"

The orientation of the carousel.

items
T[]
autoplay
false
boolean | Partial<CreateOptionsType<OptionsType>>

Enable Autoplay plugin

autoScroll
false
boolean | Partial<CreateOptionsType<OptionsType>>

Enable Auto Scroll plugin

autoHeight
false
boolean | Partial<CreateOptionsType<{ active: boolean; breakpoints: { [key: string]: Omit<Partial<any>, "breakpoints">; }; }>>

Enable Auto Height plugin

classNames
false
boolean | Partial<CreateOptionsType<OptionsType>>

Enable Class Names plugin

fade
false
boolean | Partial<CreateOptionsType<{ active: boolean; breakpoints: { [key: string]: Omit<Partial<any>, "breakpoints">; }; }>>

Enable Fade plugin

wheelGestures
false
boolean | WheelGesturesPluginOptions

Enable Wheel Gestures plugin

align
'center'
"start" | "center" | "end" | (viewSize: number, snapSize: number, index: number): number
containScroll
'trimSnaps'
false | "trimSnaps" | "keepSnaps"
slidesToScroll
1
number | "auto"
dragFree
false
boolean
dragThreshold
10
number
inViewThreshold
0
number | number[]
loop
false
boolean
skipSnaps
false
boolean
duration
25
number
startIndex
0
number
watchDrag
true
false | true | (emblaApi: EmblaCarouselType, evt: PointerEventType): boolean | void
watchResize
true
false | true | (emblaApi: EmblaCarouselType, entries: ResizeObserverEntry[]): boolean | void
watchSlides
true
false | true | (emblaApi: EmblaCarouselType, mutations: MutationRecord[]): boolean | void
watchFocus
true
false | true | (emblaApi: EmblaCarouselType, evt: FocusEvent): boolean | void
active
true
boolean
breakpoints
{}
{ [key: string]: Omit<Partial<CreateOptionsType<{ align: AlignmentOptionType; axis: AxisOptionType; container: string | HTMLElement | null; slides: string | HTMLElement[] | NodeListOf<HTMLElement> | null; containScroll: ScrollContainOptionType; direction: AxisDirectionOptionType; slidesToScroll: SlidesToScrollOptionType; dragFree: boolean; dragThreshold: number; inViewThreshold: number | number[] | undefined; loop: boolean; skipSnaps: boolean; duration: number; startIndex: number; watchDrag: DragHandlerOptionType; watchResize: ResizeHandlerOptionType; watchSlides: SlidesHandlerOptionType; watchFocus: FocusHandlerOptionType; }>>, "breakpoints">; }
ui
{ root?: SlotClass; viewport?: SlotClass; container?: SlotClass; item?: SlotClass; controls?: SlotClass; arrows?: SlotClass; prev?: SlotClass; next?: SlotClass; dots?: SlotClass; dot?: SlotClass; }

슬롯

Slot Type
default
{ item: T; index: number; }

Emits

Event Type
select
[selectedIndex: number]

노출

useTemplateRef를 사용하여 입력된 구성 요소 인스턴스에 액세스할 수 있습니다.

<script setup lang="ts">
const carousel = useTemplateRef('carousel')
</script>

<template>
  <UCarousel ref="carousel" />
</template>

이렇게 하면 다음 항목에 액세스할 수 있습니다.

이름 Name유형 (Type)
emblaRefRef<HTMLElement | null>
emblaApi 파일Ref<EmblaCarouselType | null>

Theme 테마

app.config.ts
export default defineAppConfig({
  ui: {
    carousel: {
      slots: {
        root: 'relative focus:outline-none',
        viewport: 'overflow-hidden',
        container: 'flex items-start',
        item: 'min-w-0 shrink-0 basis-full',
        controls: '',
        arrows: '',
        prev: 'absolute rounded-full',
        next: 'absolute rounded-full',
        dots: 'absolute inset-x-0 -bottom-7 flex flex-wrap items-center justify-center gap-3',
        dot: [
          'cursor-pointer size-3 bg-accented rounded-full outline-inverted/25 focus-visible:outline-3',
          'transition'
        ]
      },
      variants: {
        orientation: {
          vertical: {
            container: 'flex-col -mt-4',
            item: 'pt-4',
            prev: 'top-4 sm:-top-12 left-1/2 -translate-x-1/2 rotate-90 rtl:-rotate-90',
            next: 'bottom-4 sm:-bottom-12 left-1/2 -translate-x-1/2 rotate-90 rtl:-rotate-90'
          },
          horizontal: {
            container: 'flex-row -ms-4',
            item: 'ps-4',
            prev: 'start-4 sm:-start-12 top-1/2 -translate-y-1/2',
            next: 'end-4 sm:-end-12 top-1/2 -translate-y-1/2'
          }
        },
        active: {
          true: {
            dot: 'data-[state=active]:bg-inverted'
          }
        }
      }
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'

export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        carousel: {
          slots: {
            root: 'relative focus:outline-none',
            viewport: 'overflow-hidden',
            container: 'flex items-start',
            item: 'min-w-0 shrink-0 basis-full',
            controls: '',
            arrows: '',
            prev: 'absolute rounded-full',
            next: 'absolute rounded-full',
            dots: 'absolute inset-x-0 -bottom-7 flex flex-wrap items-center justify-center gap-3',
            dot: [
              'cursor-pointer size-3 bg-accented rounded-full outline-inverted/25 focus-visible:outline-3',
              'transition'
            ]
          },
          variants: {
            orientation: {
              vertical: {
                container: 'flex-col -mt-4',
                item: 'pt-4',
                prev: 'top-4 sm:-top-12 left-1/2 -translate-x-1/2 rotate-90 rtl:-rotate-90',
                next: 'bottom-4 sm:-bottom-12 left-1/2 -translate-x-1/2 rotate-90 rtl:-rotate-90'
              },
              horizontal: {
                container: 'flex-row -ms-4',
                item: 'ps-4',
                prev: 'start-4 sm:-start-12 top-1/2 -translate-y-1/2',
                next: 'end-4 sm:-end-12 top-1/2 -translate-y-1/2'
              }
            },
            active: {
              true: {
                dot: 'data-[state=active]:bg-inverted'
              }
            }
          }
        }
      }
    })
  ]
})

변경 로그

No recent changes