- UI组件
- 前端
【免费下载链接】shadcn-vue
Vue port of shadcn-ui
本文围绕 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/core的createInjectionState建立依赖注入关系:Carousel负责把 Embla 实例与滚动能力"提供"下去,其余四个子组件通过useCarousel()统一"注入"使用。类型定义集中在 interface.ts,包括opts、plugins、orientation三个 Props 和init-api一个 Emits;核心逻辑则在 useCarousel.ts。
安装:CLI 与手动两种方式
方式一:CLI 命令(推荐)
在你的 Vue 3 + Tailwind CSS 项目中执行:
npx shadcn-vue@latest add carouselCLI 会自动完成依赖安装、组件文件复制与导入路径配置。
方式二:手动安装
- 安装底层依赖。轮播的滑动、吸附与触摸交互全部由 Embla Carousel 驱动,需要安装其 Vue 绑定:
npm install embla-carousel-vue- 复制组件源码。将仓库
apps/v4/registry/new-york-v4/ui/carousel下的五个.vue文件以及useCarousel.ts、interface.ts一并复制到项目的components/ui/carousel目录。 - 更新导入路径。组件内部使用了
@/lib/utils(cn工具函数)、@/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](内容容器左侧负外边距)的配对方案,而不是gap或grid布局。
为什么这么做:直接给
CarouselContent使用gap或grid布局时,间距计算涉及大量数学换算,很难调对;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 原生配置
Carousel的optsprop 会原样透传给 Embla Carousel,从而获得其全部能力,例如对齐方式与无限循环:
<template> <Carousel :opts="{ align: 'start', loop: true, }" > <CarouselContent> <CarouselItem>...</CarouselItem> <CarouselItem>...</CarouselItem> <CarouselItem>...</CarouselItem> </CarouselContent> </Carousel> </template>从 useCarousel.ts 的源码可以看到,opts与orientation派生出的axis会被合并后一起传给emblaCarouselVue(),plugins则作为第二参数传入。常用 Options 还包括startIndex(初始索引)、dragFree(自由拖拽)、containScroll(滚动吸附策略)、breakpoints(响应式配置)等,完整清单可查阅 Embla Carousel 官方 API Options 文档。
访问底层 API 实例
拿到 Embla 实例后即可调用scrollTo、scrollSnapList、selectedScrollSnap等底层方法,官方提供两种方式。
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显式暴露了canScrollNext、canScrollPrev、carouselApi、carouselRef、orientation、scrollNext、scrollPrev共 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(选中项变化)、init、reInit、scroll、slidesChanged、resize等,更多事件说明可参考 Embla Carousel 官方 API Events 文档。值得注意的是,仓库内部也正是靠这套事件机制工作的:useCarousel.ts在onMounted时订阅init、reInit、select三个事件来同步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 完整集合为:carouselRef、carouselApi、canScrollNext、canScrollPrev、orientation、scrollNext、scrollPrev——这与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-scroll、embla-carousel-class-names、embla-carousel-fade等插件,可通过pluginsprop 自由组合,完整用法见 Embla Carousel 官方 Plugins 文档。
源码原理速览
最后,将整个组件的运行机制串联起来看:
- 依赖注入:
Carousel挂载时调用useProvideCarousel(props, emits),内部通过createInjectionState创建上下文;useCarousel()在子组件中注入该上下文,若脱离Carousel使用会抛出"useCarousel must be used within a <Carousel />"错误(见 useCarousel.ts); - Embla 初始化:
emblaCarouselVue返回[emblaNode, emblaApi],emblaNode绑定到CarouselContent的ref上作为滚动容器; - 状态同步:
init/reInit/select事件驱动canScrollPrev/canScrollNext,从而控制前后按钮的禁用态; - 类型安全:interface.ts 中的
CarouselProps、CarouselEmits、CarouselApi类型贯穿组件与useCarousel,保证opts、plugins与 Embla 的类型完全对齐。
仓库apps/v4/components/demo下还提供了CarouselDemo.vue、CarouselSize.vue、CarouselSpacing.vue、CarouselOrientation.vue、CarouselApi.vue、CarouselPlugin.vue等完整可运行的示例,配合本文内容对照阅读,即可快速掌握 shadcn-vue Carousel 组件的全部用法。
- UI组件
- 前端
【免费下载链接】shadcn-vue
Vue port of shadcn-ui
相关推荐
shadcn-svelte Carousel 组件完整实战指南:基于 Embla 的触摸滑动轮播
shadcn svelte Carousel 组件完整实战指南:基于 Embla 的触摸滑动轮播 Carousel 是 shadcn svelte 提供的高质量
UI组件前端CLI开发工具shadcn-vue Carousel 组件完全指南:基于 Embla 的轮播、滑动与 API 深度解析
shadcn vue Carousel 组件完全指南:基于 Embla 的轮播、滑动与 API 深度解析 本文以 shadcn vue 仓库中 Carousel
UI组件前端揭秘aspire-contextualsentence-multim-compsci:多向量模型如何实现句子级精准匹配
揭秘aspire contextualsentence multim compsci:多向量模型如何实现句子级精准匹配 aspire contextualsen
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考