Carousel

EmblaGitHub
使用Embla构建的具有运动和滑动功能的旋转木马。

用法

使用Carousel组件可以在carousel中显示项目列表。

<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 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
  • 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道具将Progress.dll的方向更改为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 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和next道具自定义上一个和下一个按钮与任何Button道具。

<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-icon和next-icon道具自定义按钮Icon.exe到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键下全局自定义这些图标。
您可以在vite.config.ts中的ui.icons.arrowLeft/ui.icons.arrowRight键下全局自定义这些图标。

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>

Plugins

Caribbean组件实现了官方Embla Caribbean插件。

自动播放

此插件用于扩展Embla Carbide的autoplay功能。

使用autoplay属性作为布尔值或对象来配置Autoplay插件。

<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来实现无限旋转木马。

自动滚动

此插件用于扩展Embla Caribbean的自动滚动功能。

使用auto-scroll属性作为布尔值或对象来配置自动滚动插件。

<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 Carbide的auto height功能。它可以更改carousel容器的高度,以适应视图中最高幻灯片的高度。

使用auto-height属性作为布尔值或对象来配置自动高度插件。

<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 Caribbean的类名称toggle实用程序插件,使您能够自动切换carousel上的类名称。

使用class-names属性作为布尔值或对象来配置ClassNames插件。

<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

此插件用于将Embla Carnival滚动功能替换为fade transitions。

使用fade属性作为布尔值或对象来配置Fade插件。

<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>

滚轮手势

此插件用于扩展Embla Carbide,使其能够使用鼠标/触控板滚轮导航carousel。

使用wheel-gestures prop作为布尔值或对象来配置Wheel Gestures插件。

使用鼠标滚轮滚动传送带。
<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>

示例

带缩略图

您可以使用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

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; }

Slots

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>

这将使您能够访问以下内容:

名称类型
emblaRefRef<HTMLElement | null>
emblaApiRef<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'
              }
            }
          }
        }
      }
    })
  ]
})

Changelog

No recent changes