shadcn-vue Carousel 组件完全指南:基于 Embla 的滑动轮播实战
2026/9/24 14:53:22 网站建设 项目流程
  • UI组件
  • 前端

【免费下载链接】shadcn-vue

Vue port of shadcn-ui

项目地址:https://gitcode.com/gh_mirrors/sh/shadcn-vue
点击查看免费下载

本文围绕 shadcn-vue 项目(GitHub 加速计划 / sh / shadcn-vue,Vue 3 版 shadcn-ui)中开箱即用的Carousel轮播组件展开,系统讲解其基于 Embla Carousel 的安装方式、五种子组件的组合用法、尺寸与间距控制、横纵方向切换、Embla 原生 Options 透传、API 实例获取、事件监听、Slot Props 与插件扩展等完整能力。读完本文,你将能够在自己的 Vue 3 + TypeScript + Tailwind CSS 项目中独立接入并深度定制一套具备滑动、按键导航与自动播放能力的轮播组件,同时理解其在仓库 apps/v4/registry/new-york-v4/ui/carousel 下的源码实现原理。

组件概览:由五个原子组件构成的轮播体系

Carousel是一个建立在 Embla Carousel 之上的 Vue 组件集合。与"一个大而全的轮播"不同,它被拆分为五个各司其职的原子组件,全部位于仓库的apps/v4/registry/new-york-v4/ui/carousel目录:

组件职责源码位置
Carousel轮播容器,负责创建 Embla 实例、注入上下文、键盘导航Carousel.vue
CarouselContent可滚动的视口容器,持有 Embla 挂载节点CarouselContent.vue
CarouselItem单个幻灯片条目,默认占满整行宽度CarouselItem.vue
CarouselNext下一个按钮,内置箭头图标,超出边界自动禁用CarouselNext.vue
CarouselPrevious上一个按钮,内置箭头图标,超出边界自动禁用CarouselPrevious.vue

这五个组件通过@vueuse/corecreateInjectionState建立依赖注入关系:Carousel负责把 Embla 实例与滚动能力"提供"下去,其余四个子组件通过useCarousel()统一"注入"使用。类型定义集中在 interface.ts,包括optspluginsorientation三个 Props 和init-api一个 Emits;核心逻辑则在 useCarousel.ts。

安装:CLI 与手动两种方式

方式一:CLI 命令(推荐)

在你的 Vue 3 + Tailwind CSS 项目中执行:

npx shadcn-vue@latest add carousel

CLI 会自动完成依赖安装、组件文件复制与导入路径配置。

方式二:手动安装

  1. 安装底层依赖。轮播的滑动、吸附与触摸交互全部由 Embla Carousel 驱动,需要安装其 Vue 绑定:
npm install embla-carousel-vue
  1. 复制组件源码。将仓库apps/v4/registry/new-york-v4/ui/carousel下的五个.vue文件以及useCarousel.tsinterface.ts一并复制到项目的components/ui/carousel目录。
  2. 更新导入路径。组件内部使用了@/lib/utilscn工具函数)、@/registry/new-york-v4/ui/button(按钮组件)与@lucide/vue(图标),请根据你的项目结构调整这些 import 路径。

基础用法:五组件组合

轮播的最基本用法是将五个组件按"容器 → 内容 → 条目"的层级嵌套,再把前后按钮放在容器内:

<script setup lang="ts"> import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, } from '@/components/ui/carousel' </script> <template> <Carousel> <CarouselContent> <CarouselItem>...</CarouselItem> <CarouselItem>...</CarouselItem> <CarouselItem>...</CarouselItem> </CarouselContent> <CarouselPrevious /> <CarouselNext /> </Carousel> </template>

从源码可以看到默认行为的细节:

  • Carousel根节点渲染为带role="region"aria-roledescription="carousel"<div>,并设置了tabindex="0",使其可聚焦以接收键盘事件(见 Carousel.vue);
  • CarouselItem默认类为min-w-0 shrink-0 grow-0 basis-full,即每个条目默认占满一行,role="group"aria-roledescription="slide"保证了屏幕阅读器的可访问性(见 CarouselItem.vue);
  • 前后按钮本质是Button组件的outline+icon变体,通过:disabled="!canScrollNext"在到达边界时自动禁用(见 CarouselNext.vue)。

尺寸控制:用 basis 工具类定义条目宽度

默认每个CarouselItem占满一行,想一次展示多张幻灯片,可在条目上叠加basis系列工具类覆盖默认的basis-full

每个条目占轮播宽度的 1/3:

<template> <Carousel> <CarouselContent> <CarouselItem class="basis-1/3"> ... </CarouselItem> <CarouselItem class="basis-1/3"> ... </CarouselItem> <CarouselItem class="basis-1/3"> ... </CarouselItem> </CarouselContent> </Carousel> </template>

响应式宽度:小屏 1/2、大屏 1/3:

<template> <Carousel> <CarouselContent> <CarouselItem class="md:basis-1/2 lg:basis-1/3"> ... </CarouselItem> <CarouselItem class="md:basis-1/2 lg:basis-1/3"> ... </CarouselItem> <CarouselItem class="md:basis-1/2 lg:basis-1/3"> ... </CarouselItem> </CarouselContent> </Carousel> </template>

由于CarouselItem的类合并走的是cn()(内部基于 tailwind-merge),basis-1/3会正确覆盖默认的basis-full,无需担心样式冲突。

间距控制:pl 与负 ml 的配对技巧

轮播条目间距采用的是pl-[VALUE](条目左侧内边距)+-ml-[VALUE](内容容器左侧负外边距)的配对方案,而不是gapgrid布局。

为什么这么做:直接给CarouselContent使用gapgrid布局时,间距计算涉及大量数学换算,很难调对;pl-[VALUE]配合-ml-[VALUE]使用起来直观得多。你也可以在自己的项目中按需调整这个方案。

固定间距 1rem:

<template> <Carousel> <CarouselContent class="-ml-4"> <CarouselItem class="pl-4"> ... </CarouselItem> <CarouselItem class="pl-4"> ... </CarouselItem> <CarouselItem class="pl-4"> ... </CarouselItem> </CarouselContent> </Carousel> </template>

响应式间距:小屏 0.5rem、大屏 1rem:

<template> <Carousel> <CarouselContent class="-ml-2 md:-ml-4"> <CarouselItem class="pl-2 md:pl-4"> ... </CarouselItem> <CarouselItem class="pl-2 md:pl-4"> ... </CarouselItem> <CarouselItem class="pl-2 md:pl-4"> ... </CarouselItem> </CarouselContent> </Carousel> </template>

这与源码中的默认实现完全一致:CarouselContent内部默认携带-ml-4(水平)或-mt-4(垂直),CarouselItem默认携带pl-4(水平)或pt-4(垂直),你传入的类会通过cn()追加合并(见 CarouselContent.vue)。

方向切换:vertical 与 horizontal

通过orientationprop 即可切换横纵方向,该 prop 默认值为"horizontal"(见 Carousel.vue):

<Carousel orientation="vertical | horizontal"> ... </Carousel>

方向的切换在底层是全局生效的:

  • useCarousel.ts中通过axis: orientation === "horizontal" ? "x" : "y"把方向映射为 Embla 的滚动轴;
  • CarouselContent在垂直模式下改用-mt-4 flex-col布局;
  • CarouselItem在垂直模式下改用pt-4上内边距;
  • 前后按钮在垂直模式下分别定位到容器上下两侧并旋转 90°(见 CarouselPrevious.vue);
  • 键盘导航也会随之切换:水平方向监听ArrowLeft/ArrowRight,垂直方向监听ArrowUp/ArrowDown(见 Carousel.vue)。

Options:透传 Embla 原生配置

Carouseloptsprop 会原样透传给 Embla Carousel,从而获得其全部能力,例如对齐方式与无限循环:

<template> <Carousel :opts="{ align: 'start', loop: true, }" > <CarouselContent> <CarouselItem>...</CarouselItem> <CarouselItem>...</CarouselItem> <CarouselItem>...</CarouselItem> </CarouselContent> </Carousel> </template>

从 useCarousel.ts 的源码可以看到,optsorientation派生出的axis会被合并后一起传给emblaCarouselVue()plugins则作为第二参数传入。常用 Options 还包括startIndex(初始索引)、dragFree(自由拖拽)、containScroll(滚动吸附策略)、breakpoints(响应式配置)等,完整清单可查阅 Embla Carousel 官方 API Options 文档。

访问底层 API 实例

拿到 Embla 实例后即可调用scrollToscrollSnapListselectedScrollSnap等底层方法,官方提供两种方式。

Method 1:监听 @init-api 事件

Carousel组件上使用@init-api事件即可在初始化完成时拿到 API 实例。仓库中的 CarouselApi.vue 演示了如何用它实现"第 N 张 / 共 M 张"的幻灯片计数器:通过api.scrollSnapList().length获取总数,api.selectedScrollSnap() + 1获取当前索引,并在select事件中实时更新。

Method 2:通过模板 ref 访问

也可以给Carousel设置模板 ref,直接读取其暴露出的carouselApi属性:

<script setup lang="ts"> const carouselContainerRef = ref<InstanceType<typeof Carousel> | null>(null) function accessApi() { carouselContainerRef.value?.carouselApi.on('select', () => {}) } </script> <template> <Carousel ref="carouselContainerRef"> ... </Carousel> </template>

该方法可行的原因在于 Carousel.vue 通过defineExpose显式暴露了canScrollNextcanScrollPrevcarouselApicarouselReforientationscrollNextscrollPrev共 7 个响应式状态与方法。

事件监听

轮播自身不逐条转发 Embla 事件,而是推荐先获取 API 实例,再在其上监听事件。由于 API 在组件挂载后才就绪,通常配合watch在实例可用后注册监听器,并只监听一次:

<script setup lang="ts"> import { nextTick, ref, watch } from 'vue' import { useCarousel } from '@/components/ui/carousel' const api = ref<CarouselApi>() function setApi(val: CarouselApi) { api.value = val } const stop = watch(api, (api) => { if (!api) return // Watch only once or use watchOnce() in @vueuse/core nextTick(() => stop()) api.on('select', () => { // Do something on select. }) }) </script> <template> <Carousel @init-api="setApi"> ... </Carousel> </template>

Embla 的常用事件包括select(选中项变化)、initreInitscrollslidesChangedresize等,更多事件说明可参考 Embla Carousel 官方 API Events 文档。值得注意的是,仓库内部也正是靠这套事件机制工作的:useCarousel.tsonMounted时订阅initreInitselect三个事件来同步canScrollPrev/canScrollNext状态(见 useCarousel.ts)。

Slot Props:扩展按钮的显隐逻辑

Carousel通过默认插槽向外暴露一组响应式状态与方法,可用v-slot接收后自定义按钮行为,例如只在可滚动方向时显示对应按钮:

<template> <Carousel v-slot="{ canScrollNext, canScrollPrev }"> ... <CarouselPrevious v-if="canScrollPrev" /> <CarouselNext v-if="canScrollNext" /> </Carousel> </template>

可用的 Slot Props 完整集合为:carouselRefcarouselApicanScrollNextcanScrollPrevorientationscrollNextscrollPrev——这与defineExpose暴露的内容一一对应(见 Carousel.vue)。

Plugins:接入 Embla 官方插件

pluginsprop 接收 Embla 插件数组,最常见的场景是自动播放。先安装插件:

npm install embla-carousel-autoplay

再在组件中传入:

<script setup lang="ts"> import Autoplay from 'embla-carousel-autoplay' </script> <template> <Carousel class="w-full max-w-xs" :plugins="[Autoplay({ delay: 2000, })]" > ... </Carousel> </template>

Autoplay的常用配置包括delay(播放间隔毫秒数)、stopOnInteraction(用户交互后是否停止)、stopOnMouseEnter(鼠标悬停是否暂停)等;Embla 生态还提供embla-carousel-auto-scrollembla-carousel-class-namesembla-carousel-fade等插件,可通过pluginsprop 自由组合,完整用法见 Embla Carousel 官方 Plugins 文档。

源码原理速览

最后,将整个组件的运行机制串联起来看:

  1. 依赖注入Carousel挂载时调用useProvideCarousel(props, emits),内部通过createInjectionState创建上下文;useCarousel()在子组件中注入该上下文,若脱离Carousel使用会抛出"useCarousel must be used within a <Carousel />"错误(见 useCarousel.ts);
  2. Embla 初始化emblaCarouselVue返回[emblaNode, emblaApi]emblaNode绑定到CarouselContentref上作为滚动容器;
  3. 状态同步init/reInit/select事件驱动canScrollPrev/canScrollNext,从而控制前后按钮的禁用态;
  4. 类型安全:interface.ts 中的CarouselPropsCarouselEmitsCarouselApi类型贯穿组件与useCarousel,保证optsplugins与 Embla 的类型完全对齐。

仓库apps/v4/components/demo下还提供了CarouselDemo.vueCarouselSize.vueCarouselSpacing.vueCarouselOrientation.vueCarouselApi.vueCarouselPlugin.vue等完整可运行的示例,配合本文内容对照阅读,即可快速掌握 shadcn-vue Carousel 组件的全部用法。

  • UI组件
  • 前端

【免费下载链接】shadcn-vue

Vue port of shadcn-ui

项目地址:https://gitcode.com/gh_mirrors/sh/shadcn-vue
点击查看免费下载
上一篇:gopher-reading-list移动端适配:在手机上高效阅读的技巧
下一篇:LND通道使用统计:生成通道活跃度报告

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询