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 自訂內容,按鈕仍使用完整標籤。標籤會去除首尾空白;未傳、空白或缺少的項目回退本地化序號。請與 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)互補。

一屏多卡:桌面 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。