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>
banner 連結(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 | 左 / 右側豎排 |
outside | banner 下方正常流(不疊加) |
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-z | 2 | 指示器層級(導航 chrome;兄弟浮層放 1 即不擋圓點) |
--sw-carousel-indicator-offset | 0.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-size | 0.5rem | 圓點尺寸 |
--sw-carousel-dot-active-size | 1.25rem | 激活點的「長條」尺寸(橫排變長 / 豎排變高) |
--sw-carousel-dot-gap | 0.5rem | 圓點間距 |
--sw-carousel-dot-color | 白 rgb(255 255 255) | 圓點色 |
--sw-carousel-dot-active-color | 白 | 激活點色 |
--sw-carousel-dot-shadow | 0 0 0.125rem rgb(0 0 0 / 35%) | 圓點投影(白底也可見) |
--sw-carousel-thumb-width | 3.5rem | 縮圖寬 |
--sw-carousel-thumb-height | 2.25rem | 縮圖高 |
--sw-carousel-thumb-gap | 0.5rem | 縮圖間距 |
--sw-carousel-thumb-radius | 0.5rem | 縮圖圓角 |
--sw-carousel-thumb-dim | 0.5 | 非激活縮圖壓暗亮度(brightness) |
--sw-carousel-thumb-hover | 0.8 | 縮圖 hover 亮度(brightness) |
--sw-carousel-thumb-active-shadow | 0 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
| 名稱 | 型別 | 必填 | 預設值 | 說明 |
|---|---|---|---|---|
items | T[] | ✅ | — | 輪播資料源 |
loop | boolean | true | 無縫循環 | |
autoplay | boolean | true | 自動輪播 | |
autoplayDelay | number | 4000 | 每張停留 ms(帶文案 banner 夠閱讀;純圖可傳 3000) | |
initialIndex | number | 0 | 初始定位邏輯索引 | |
speed | number | 300 | 切換動畫 ms | |
pauseOnHover | boolean | true | hover 與鍵盤焦點期間暫停(同開關同語義) | |
dragThreshold | number | 0.15 | 拖曳翻頁閾值(佔容器寬比例) | |
indicator | 'dots' | 'thumbnails' | 'none' | 'dots' | 指示器型別 | |
indicatorPlacement | 'bottom-center' | 'bottom-left' | 'bottom-right' | 'left' | 'right' | 'outside' | 'bottom-center' | 指示器擺位(dots / 縮圖共用;left / right 為豎排) | |
thumbnails | string[] | undefined | indicator='thumbnails' 時的縮圖條圖源(長度需與 items 一致) | |
thumbnailLabels | string[] | undefined | 與 thumbnails 等長同序的完整按鈕名稱;去除首尾空白,未傳/空白/缺項回退本地化序號 | |
thumbnailTrigger | 'click' | 'hover' | 'click' | 縮圖切換觸發方式('hover' = 懸停即切換) | |
ariaLabel | string | undefined | 傳了才輸出 role="region" + aria-label | |
track | CarouselTrackProp | undefined | 內建逐張曝光埋點(opt-in,不傳零行為),見下「逐張曝光埋點」 | |
slidesPerView | number | { mobile?, tablet?, desktop? } | 1 | 一屏張數(#220):數字=全斷點同值;物件=語義斷點(mobile <768 / tablet 768–1023 / desktop ≥1024,缺檔回退相鄰較小檔)。多卡時逐張步進、loop 首尾各克隆 max(spv) 張、dots 數 = items 數(每點=最左張)、change 攜帶最左張邏輯索引。初始化後非響應式(mount 後改本 prop 不生效;斷點檔位切換是響應式的) | |
gap | string | '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
| 事件名 | 參數 | 說明 |
|---|---|---|
change | index: 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 | () => void | autoplay 續播 / 暫停 |
current | Readonly<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。