Carousel 轮播
内容轮播。支持位移与淡入、自动播放、拖拽切换。
基础用法
每个直接子元素是一屏——不需要包一层 SrCarouselItem。
当前第 1 屏
<script setup>
import { ref } from 'vue'
const index = ref(0)
</script>
<template>
<SrCarousel v-model:index="index">
<div>第一屏</div>
<div>第二屏</div>
<div>第三屏</div>
</SrCarousel>
</template>为什么不包 CarouselItem
其他组件库普遍提供 SrCarouselItem。本组件刻意不这么做——原因写在实现注释里:
那需要子组件向下注册自己,多一层
provide/inject与生命周期同步, 而这里只需要知道「有几屏」——插槽节点数组过滤掉空节点即可。
直接子元素才算一屏
v-for 产生的元素、以及注释节点会被正确过滤。但如果你在中间包了一层 <div>,那么整个 div 就是一屏,它里面有几个孩子都算一屏。
<!-- ❌ 这样只有一屏 -->
<SrCarousel>
<div>
<section>第一屏</section>
<section>第二屏</section>
</div>
</SrCarousel>
<!-- ✅ 两个直接子元素 = 两屏 -->
<SrCarousel>
<section>第一屏</section>
<section>第二屏</section>
</SrCarousel>切换效果
effect="fade":交叉淡入,不做位移
<SrCarousel effect="slide">…</SrCarousel>
<SrCarousel effect="fade">…</SrCarousel>| 效果 | 实现 | 适用 |
|---|---|---|
slide | 位移,用 transform: translateX() | 图片、卡片列表 |
fade | 交叉淡入,用 opacity | 高度不一的图文、Banner |
fade 用 opacity 而非 display 切换——后者无法产生过渡, 只能硬切。
纵向轮播
<SrCarousel direction="vertical">…</SrCarousel>自动播放
autoplay 配合 interval。默认开启 pauseOnHover—— 用户把鼠标停在轮播上时,多半是在看内容,此时翻页只会打断他。
<SrCarousel autoplay :interval="2000" pause-on-hover>…</SrCarousel>自动播放的可访问性
自动切换的内容会干扰认知障碍用户与读屏用户——内容还没读完就翻页了。
组件会在 prefers-reduced-motion: reduce 时自动关闭自动播放。 若你的场景对时效性没有要求,建议默认不开 autoplay, 或提供显式的暂停按钮(WCAG 2.2.2 的要求是「超过 5 秒的自动更新内容 必须可暂停」)。
拖拽切换
draggable 默认开启,触摸与鼠标一致。
dragThreshold 是切换所需的最小拖动距离(像素)——设太小会误触, 设太大又拖不动。
<SrCarousel :drag-threshold="30">…</SrCarousel>拖拽用 Pointer Events 统一处理
不用「touchstart + mousedown」两套监听:那需要分别处理触摸与鼠标, 两边的行为还容易有细微差异(触摸端没有 mousemove、 鼠标端不会触发 touchcancel)。
Pointer Events 把两者统一成一套,代码更少、行为也更一致。
指示器与箭头
悬停切换指示器 + 隐藏箭头
<SrCarousel indicator-trigger="hover" />
<SrCarousel :arrows="false" />
<SrCarousel progress :indicator="false" />循环
loop 控制到边界后是否回到另一端,默认开启。
关闭后,首屏时左箭头不可用、末屏时右箭头不可用—— 比循环更「诚实」,用户能感知到列表有边界。
<SrCarousel :loop="false">…</SrCarousel>Props
| 名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
index | number | 0 | 当前屏索引(v-model:index) |
effect | 'slide' | 'fade' | 'slide' | 切换效果 |
direction | 'horizontal' | 'vertical' | 'horizontal' | 轮播方向 |
loop | boolean | true | 是否循环 |
autoplay | boolean | false | 是否自动播放 |
interval | number | 3000 | 自动播放间隔(毫秒) |
pauseOnHover | boolean | true | 悬停时是否暂停 |
indicator | boolean | true | 是否显示指示器 |
indicatorTrigger | 'click' | 'hover' | 'click' | 指示器触发方式 |
arrows | boolean | true | 是否显示左右箭头 |
draggable | boolean | true | 是否可拖拽 |
dragThreshold | number | 50 | 切换所需的最小拖动距离(px) |
progress | boolean | false | 是否显示顶部进度条 |
事件
| 事件 | 参数 | 说明 |
|---|---|---|
update:index | number | 当前屏变化 |
change | number | 同上(语义化事件) |
无障碍
- 容器加
role="region"与aria-roledescription="轮播", 每屏role="group"并带aria-label="第 N 屏,共 M 屏" - 左右箭头是
<button>并带aria-label,键盘可聚焦 - 拖拽是增强而非唯一途径——箭头与指示器都能用键盘操作
- 自动播放在
prefers-reduced-motion下自动关闭(见上文) - 容器可聚焦,键盘用户能用左右方向键切换