SwCarousel

SwCarousel 是基于 transform: translateX 的自动轮播组件,用于 banner / 首图等场景。首尾各克隆一张做无缝循环(切到克隆帧时无动画跳回真实帧,视觉无跳变),内置 autoplay、触摸阈值翻页、dots / 缩略图指示器。

items 制:传 :items 数据 + #default="{ item, index }" 作用域插槽渲染每张(组件按 items 克隆,index 为真实逻辑索引)。

必须写 #default 插槽:组件不知道怎么渲染你的 item,传了 :items 却没写 #default 会渲染空白帧(dev 期 console 警告)。

自由横排滚动用 SwRail:SwCarousel 是「一次一屏 + 自动轮播」;「一行 N 个靠滚动」用 SwRail。

基础用法

自动轮播 banner

加载中...
<SwCarousel :items="banners" aria-label="首页 banner">
  <template #default="{ item }">
    <SwLink :to="item.link"><SwImage :src="item.img" ratio="16/9" /></SwLink>
  </template>
</SwCarousel>

SwCarousel 本身不提供任何链接 / 文案 / 按钮——每张 banner 的内容(图片 / 标题 / 按钮 / 跳转地址)全部来自 item 数据,通过 #default="{ item, index }" 作用域插槽渲染。需要可点击的 banner 时按下面两种形态二选一:

  • 纯图 banner(整张可点):用 SwLink 包住整张 slide(见上方基础用法)。
  • 带文案 + 按钮的 banner:把 SwLink / SwButton 作为 CTA 放进 slide 内部,不要把整张再套 <a>(链接里嵌套可交互元素是非法 HTML)。

图片 + 标题 + CTA 链接(全由 item 驱动)

加载中...
<!-- 真实 banner:图片 + 标题 + CTA 按钮,链接来自 item.link -->
<SwCarousel :items="banners" aria-label="首页 banner">
  <template #default="{ item }">
    <div class="banner">
      <SwImage :src="item.img" ratio="12/5" />
      <h3 class="title">{{ item.title }}</h3>
      <SwLink :to="item.link" class="cta">{{ item.cta }}</SwLink>
    </div>
  </template>
</SwCarousel>

autoplay 与暂停

默认开 autoplay(autoplayDelay 控制每张停留 ms,默认 4000——带文案 banner 够阅读;纯图可传 3000)。桌面 hover、键盘焦点落入、按下拖拽、页面切到后台(visibilitychange)会自动暂停,离开 / 回到前台续播;命中系统「减少动态」(prefers-reduced-motion)时不自动播放。单图或 :autoplay="false" 不开。手动切换(指示器 / 缩略图 / 键盘 / ref 方法)会重置自动轮换计时——刚选中的 slide 享受完整 autoplayDelay,不会被临近的下一次 tick 立即换走。

<SwCarousel :items="banners" :autoplay-delay="5000" :pause-on-hover="true" />
<SwCarousel :items="banners" :autoplay="false" />  <!-- 关闭自动播放 -->

循环与单图降级

  • loop(默认 true):无缝循环。:loop="false" 时到首 / 尾停住不绕。
  • 单图自动降级:items.length <= 1 时不渲克隆、不开 autoplay、不显指示器。

触摸翻页

移动端手指滑动超过 dragThreshold(占容器宽比例,默认 0.15)即翻页,否则回弹。

行为手感:不循环 / initial-index 起始定位 / speed 切换时长 / drag-threshold 拖拽阈值

加载中...

指示器(dots / 缩略图 / 自定义)

indicator 选 'dots'(默认)/ 'thumbnails' / 'none';位置由 indicatorPlacement 统一控制(dots 与缩略图共用):

placement效果
bottom-center(默认)底部居中,水平
bottom-left / bottom-right底部左 / 右对齐
left / right左 / 右侧竖排
outsidebanner 下方正常流(不叠加)

dots 摆位:右侧竖排 / 底部右对齐 / outside

加载中...

dots 样式

默认 dots 是白色圆点 + 投影(任意背景上都可见,不绑主色);激活点变成变长条并带动画(横排变长 / 竖排变高)。dots 还带放大的点击热区(透明,不改视觉),方便触摸点击。全部样式由 CSS 变量定制:

CSS 变量定制 dots(激活长条 / 主色 / 间距)

加载中...
<SwCarousel
  :items="banners"
  style="--sw-carousel-dot-active-color: var(--primary); --sw-carousel-dot-size: 0.625rem"
>
  <template #default="{ item }"><SwImage :src="item.img" ratio="16/9" /></template>
</SwCarousel>

缩略图导航

indicator="thumbnails" + :thumbnails(图源数组,长度需与 items 一致)渲染缩略图条。激活缩略图用白色 box-shadow 描边(非硬边框,reflow-safe);非激活项用 brightness 压暗、hover 时回亮。缩略图较多时自动变成可横向滚动的缩略图条,切换时把激活项自动滚进可视区(鼠标滚轮也能滚动条带)。

thumbnailTrigger 控制缩略图的切换触发方式:'click'(默认,点击切换)/ 'hover'(悬停即切换)。

thumbnailLabels 为每个缩略图外层按钮提供完整可访问名称,例如 :thumbnail-labels="movies.map(m => m.title)"。即使视觉片名省略,或 #thumbnail 自定义了内容,按钮仍使用完整标签。标签会去掉首尾空白;未传、空白或缺少的项回退本地化“第 n 张”。请与 thumbnails 保持等长同序;长度不齐时开发环境警告,已有标签照常使用、缺项按序号回退。aria-current、键盘与触摸行为不变。

缩略图导航(底部居中,点击切换)

加载中...

缩略图导航(悬停即切换)

加载中...

缩略图默认渲染为裸 <img>(不经 SwImage、无解密能力)。要自定义单张缩略图的渲染——最典型是 .bnc 加密图(裸 img 必裂)——用 #thumbnail 作用域插槽({ thumb, index, active })塞 SwImage,不传插槽时行为与旧版完全一致:

#thumbnail 插槽:SwImage 渲染缩略图(.bnc 加密图同款姿势)

加载中...
<SwCarousel :items="movies" indicator="thumbnails" :thumbnails="movies.map((m) => m.cover)">
  <template #default="{ item }"><SwImage :src="item.cover" alt="" fill /></template>
  <!-- 兜底裸 img 不解密;SwImage fill 填满缩略图按钮盒,.bnc 自动解密 -->
  <template #thumbnail="{ thumb }"><SwImage :src="thumb" alt="" fill /></template>
</SwCarousel>
<!-- 缩略图导航(右侧竖排,悬停切换) -->
<SwCarousel
  :items="list"
  indicator="thumbnails"
  :thumbnails="list.map(x => x.thumb)"
  indicator-placement="right"
  thumbnail-trigger="hover"
>
  <template #default="{ item }"><SwImage :src="item.img" ratio="16/9" /></template>
</SwCarousel>

自定义指示器

要完全自定义指示器(进度条 / 数字 / 带标题缩略图等)用 #indicator 插槽:

#indicator 插槽:n / total 计数器

加载中...
<SwCarousel :items="list">
  <template #default="{ item }"><img :src="item.img"></template>
  <template #indicator="{ current, count, goTo }">
    <div class="my-pager">{{ current + 1 }} / {{ count }}</div>
  </template>
</SwCarousel>

CSS 变量

指示器(dots / 缩略图)样式全部走 CSS 变量定制,无需覆盖内部选择器:

变量默认值说明
--sw-carousel-indicator-z2指示器层级(导航 chrome;兄弟浮层放 1 即不挡圆点)
--sw-carousel-indicator-offset0.75rem指示器距边距离(两轴共用)
--sw-carousel-indicator-inline-offset回退 --sw-carousel-indicator-offset横轴(left / right)单独距边距离;满宽轮播嵌 max-width 布局时用它把指示器对齐内容容器而非视口。bottom-center(横轴居中)不适用。缩略条(thumbnails)的 max-width 随同一条回退链派生(100% − offset×2)
--sw-carousel-dot-size0.5rem圆点尺寸
--sw-carousel-dot-active-size1.25rem激活点的「长条」尺寸(横排变长 / 竖排变高)
--sw-carousel-dot-gap0.5rem圆点间距
--sw-carousel-dot-color白 rgb(255 255 255)圆点色
--sw-carousel-dot-active-color白激活点色
--sw-carousel-dot-shadow0 0 0.125rem rgb(0 0 0 / 35%)圆点投影(白底也可见)
--sw-carousel-thumb-width3.5rem缩略图宽
--sw-carousel-thumb-height2.25rem缩略图高
--sw-carousel-thumb-gap0.5rem缩略图间距
--sw-carousel-thumb-radius0.5rem缩略图圆角
--sw-carousel-thumb-dim0.5非激活缩略图压暗亮度(brightness)
--sw-carousel-thumb-hover0.8缩略图 hover 亮度(brightness)
--sw-carousel-thumb-active-shadow0 0 0 2px rgb(255 255 255 / 90%)激活缩略图描边(box-shadow 实现,reflow-safe)

命令式控制

<script setup>
const carouselRef = ref()
</script>

<template>
  <SwCarousel ref="carouselRef" :items="list"><template #default="{ item }">…</template></SwCarousel>
  <button @click="carouselRef.next()">下一张</button>
</template>

一屏多卡(slidesPerView)

推荐位 / 创作者位「一行 N 卡自动轮播」形态——slidesPerView 定一屏张数(支持语义断点响应式)、gap 定卡间距;步进恒为 1 张,loop / autoplay / 拖拽 / dots 照常工作。与 SwRail(自由横滚、无 loop/autoplay)互补:要自动轮播选本组件,要自由滚动选 SwRail。

一屏多卡:桌面 3 / 平板 2 / 移动 1 + gap + 自动逐张轮播

加载中...

Props

名称类型必填默认值说明
itemsT[]✅—轮播数据源
loopbooleantrue无缝循环
autoplaybooleantrue自动轮播
autoplayDelaynumber4000每张停留 ms(带文案 banner 够阅读;纯图可传 3000)
initialIndexnumber0初始定位逻辑索引
speednumber300切换动画 ms
pauseOnHoverbooleantruehover 与键盘焦点期间暂停(同开关同语义)
dragThresholdnumber0.15拖拽翻页阈值(占容器宽比例)
indicator'dots' | 'thumbnails' | 'none''dots'指示器类型
indicatorPlacement'bottom-center' | 'bottom-left' | 'bottom-right' | 'left' | 'right' | 'outside''bottom-center'指示器摆位(dots / 缩略图共用;left / right 为竖排)
thumbnailsstring[]undefinedindicator='thumbnails' 时的缩略图条图源(长度需与 items 一致)
thumbnailLabelsstring[]undefined与 thumbnails 等长同序的完整按钮名称;去首尾空白后使用,缺省/空白/缺项回退本地化序号
thumbnailTrigger'click' | 'hover''click'缩略图切换触发方式('hover' = 悬停即切换)
ariaLabelstringundefined传了才输出 role="region" + aria-label
trackCarouselTrackPropundefined内置逐张曝光埋点(opt-in,不传零行为),见下「逐张曝光埋点」
slidesPerViewnumber | { mobile?, tablet?, desktop? }1一屏张数(#220):数字=全断点同值;对象=语义断点(mobile <768 / tablet 768–1023 / desktop ≥1024,缺档回退相邻较小档)。多卡时逐张步进、loop 首尾各克隆 max(spv) 张、dots 数 = items 数(每点=最左张)、change 携带最左张逻辑索引。初始化后非响应式(mount 后改本 prop 不生效;断点档位切换是响应式的)
gapstring'0px'slide 间距(CSS 长度如 '1rem'),配合 slidesPerView 多卡使用。初始化后非响应式(同 slidesPerView)

逐张曝光埋点(:track)

轮播的曝光语义与 SwLink 的「进视口一次」不同:loop / autoplay 下非当前 slide 被 transform 挪出可视区,slot 内 SwLink 的 IntersectionObserver 判定不可靠——「轮到哪张」只有轮播引擎自己知道,所以由组件级 :track 承担:

<SwCarousel :items="banners" :track="{
  exposure: (item, index) => ({
    event: 'appPageitemExposure',
    payload: {
      item_id: (item as Banner).id, item_type: 'banner', item_name: (item as Banner).title,
      parent_item_id: '', position: index + 1, page_key: 'home', page_name: '首页',
    },
  }),
}">
  <template #default="{ item }">
    <SwLink :to="item.link"><SwImage :src="item.img" ratio="16/9" /></SwLink>
  </template>
</SwCarousel>
  • 时机:每张轮到且容器进过视口(IO ≥50% 一次性)时上报;实例生命周期内每张只报一次(autoplay 多圈不重复刷曝光);items 引用变化(换内容)后重新计。
  • exposure(item, index) 由业务把轮播 item 映射为埋点指令,返回 undefined = 该张不上报。
  • 点击埋点不内置——slide 内容是业务 slot,点击语义归内容上的 SwLink :track.click(见上方示例)。
  • 上报走 useTracker(tracker gate 关闭时静默 no-op)。

Events

事件名参数说明
changeindex: number当前张变化,携带真实逻辑索引

Slots

插槽名作用域参数说明
default{ item, index, active }每张内容(index 为真实逻辑索引)。必写——缺失会渲染空白帧并 dev 期警告
thumbnail{ thumb, index, active }自定义单张缩略图内容(如用 SwImage 渲染 .bnc 加密图);不传时兜底为裸 <img>。插槽内容需自带尺寸(SwImage fill / 裸 img 均已适配填满按钮盒)
indicator{ current, count, goTo }自定义指示器(兜底非标场景,如进度条 / 数字 / 带标题缩略图);整条替换会丢内置滚轮横滚 / 激活滚入视野等能力——只想换缩略图渲染用 #thumbnail 即可

暴露方法(defineExpose)

成员签名说明
next / prev() => void下一张 / 上一张(loop 无缝)
goTo(index: number, opts?: { immediate?: boolean }) => void跳到第 N 张(immediate 无动画)
play / pause() => voidautoplay 续播 / 暂停
currentReadonly<Ref<number>>当前真实逻辑索引

注意事项

  • items 制 + 必写 #default:每张内容走 #default="{ item, index }" 作用域插槽;index 是真实逻辑索引(克隆帧也映射回真实索引)。传了 :items 却没写 #default 会渲染空白帧,dev 期 console 警告。
  • banner 链接来自 item 数据:组件不内置链接;纯图 banner 用 SwLink 包整张,带按钮的 banner 把 CTA(SwLink / SwButton)放进 slide 内部,别把整张再套 <a>。
  • 自动轮播必静音媒体:若 slide 内是视频,autoplay 场景遵循浏览器策略(静音)。
  • 横排滚动用 SwRail:SwCarousel 是一次一屏的轮播,不是自由横排。

active 仅当前真实项为 true,首尾克隆始终 false。autoplay 支持响应式切换;视频准备/播放期间设为 false,失败回退图片后设为 true,恢复时重新计完整间隔。参见 SwVideoBackground。