一个基于 Swiper.js 实现的响应式固定宽度卡片轮播组件。
它与常见的“一屏固定显示 3 张 / 4 张”轮播不同:本项目先为卡片定义合适的自然宽度,再通过 slidesPerView: 'auto' 让浏览器根据当前视口决定实际可见数量,并刻意露出下一张卡片的一部分,形成清晰的横向浏览暗示。
适合产品系列、案例展示、服务项目、作品集、区域方案、车型目录等需要突出大图视觉,同时又希望保留连续浏览感的页面场景。
- 固定宽度卡片,而不是按容器平均分栏
- 使用
slidesPerView: 'auto'自然决定可见数量 - 右侧保留“下一张预览”,提示内容仍可继续浏览
- 桌面端提供上一张 / 下一张方向按钮
- 手机端隐藏方向按钮,优先使用触控滑动
- 支持分类 Tabs,并为每个分类维护独立 Swiper
- 隐藏分类首次打开时再初始化,避免隐藏容器尺寸计算异常
- 卡片保持统一竖版比例
- 图片底部渐变文字覆盖层
- 轻量图片 Hover 放大效果
- 首次显示使用错位上浮动画
- 支持键盘方向键切换 Tabs
- 支持
prefers-reduced-motion - Swiper JS / CSS 全部本地加载,运行时不依赖 CDN
普通多卡片轮播经常使用:
slidesPerView: 4这意味着卡片宽度通常由容器宽度反推:
容器宽度 ÷ 4 = 单张卡片宽度
本项目采用另一种思路:
先确定卡片最舒服的自然尺寸
↓
Swiper 使用 slidesPerView: 'auto'
↓
视口决定当前能够看到几张
↓
最后一张允许局部露出
↓
形成继续横向浏览的视觉提示
核心初始化参数非常简单:
new Swiper(swiperEl, {
slidesPerView: 'auto',
spaceBetween: 24,
speed: 650,
grabCursor: true,
watchOverflow: true
});真正决定布局效果的是 Swiper 与 CSS 卡片宽度共同配合,而不是不断增加 breakpoints 去规定每个屏幕必须显示几张。
| 项目 | 桌面端 | 手机端 |
|---|---|---|
| 卡片宽度 | 370px |
300px |
| 卡片比例 | 370 / 497 |
保持相同比例 |
| 卡片间距 | 24px |
24px |
| 页面左右留白 | 80px |
20px |
| 方向按钮 | 显示 | 隐藏 |
| 主要交互 | 按钮 + 拖拽 | Touch Swipe |
项目通过 CSS 变量集中维护主要尺寸:
:root {
--card-width: 370px;
--card-gap: 24px;
--page-padding: 80px;
}手机端只需要调整核心变量:
@media (max-width: 960px) {
:root {
--card-width: 300px;
}
}
@media (max-width: 600px) {
:root {
--page-padding: 20px;
}
}这种方式比为不同设备分别维护 1 / 2 / 3 / 4 张卡片数量更直观,也更容易长期维护。
以常见的 375px 手机视口为例:
左侧留白 20px
第一张卡片 300px
卡片间距 24px
-----------------
第二张起点 344px
因此当前视口还剩:
375 - 344 = 31px
第二张卡片会自然露出约 31px。
这种设计比单纯使用 width: 100% 的单卡片轮播更容易让用户意识到页面可以继续横向滑动。
“下一张预览”并不是通过特殊 Swiper Effect 实现,而是由两层不同的溢出规则共同完成:
Section / Viewport
└── overflow: hidden
Swiper
└── overflow: visible
.carousel-viewport {
overflow: hidden;
}
.model-swiper {
overflow: visible;
}Swiper 轨道可以继续向右延伸,但最终由外层视口负责裁切,于是自然形成卡片局部露出的效果。
页面演示包含:
- 全部
- 布局
- 交互
- 响应式
- 细节
每个 Tab 对应一个独立的 tabpanel 和 Swiper 容器。
项目没有在页面加载时一次性初始化所有隐藏轮播,而是:
页面首次加载
↓
只初始化当前可见分类
↓
点击其他 Tab
↓
显示对应 Panel
↓
首次进入时创建 Swiper
↓
再次进入直接复用已有实例
这样可以避免隐藏元素在 display: none 状态下初始化时出现宽度、位置或边界判断异常,同时保持逻辑简单。
单张卡片结构保持非常轻量:
<div class="swiper-slide">
<a class="model-card" href="#">
<span class="model-thumbnail">
<img src="images/01.svg" alt="">
</span>
<span class="model-box">
<strong class="model-title">标题</strong>
<span class="model-subtitle">描述文字</span>
</span>
</a>
</div>图片负责视觉内容,标题和描述始终保持为真实 HTML 文本,因此后期替换内容、添加链接、接入 CMS 或 WordPress 数据都比较方便。
卡片使用:
aspect-ratio: 370 / 497;图片:
object-fit: cover;因此实际项目中不要求所有图片严格使用同一像素尺寸,只要构图适合竖版卡片即可。
为了保证白色文字在不同图片上都有稳定可读性,底部使用伪元素增加渐变遮罩:
.model-card::after {
height: 58%;
background: linear-gradient(
0deg,
rgba(0, 0, 0, .94) 0%,
rgba(0, 0, 0, .72) 31%,
rgba(0, 0, 0, 0) 100%
);
}文字本身并不写进图片中,因此更适合真实网站长期维护。
桌面端只做轻微图片放大:
.model-card:hover .model-thumbnail img {
transform: scale(1.02);
}放大幅度刻意保持克制,避免大尺寸图片卡片出现明显跳动。
卡片首次显示时使用轻量上浮动画,并通过 --slide-delay 制造简单的错位进入效果。
同时提供:
@media (prefers-reduced-motion: reduce)当用户系统设置为“减少动态效果”时,会自动降低动画和过渡强度。
fixed-width-peek-carousel/
├── index.html
├── README.md
├── css/
│ └── style.css
├── js/
│ └── main.js
├── images/
│ ├── 01.svg
│ ├── 02.svg
│ ├── ...
│ └── 12.svg
└── vendor/
└── swiper/
├── swiper-bundle.min.css
├── swiper-bundle.min.js
├── LICENSE
└── README.md
HTML、项目 CSS、项目 JS、演示图片和第三方依赖完全分离,方便后续单独复制组件或替换内容。
项目没有构建流程,也不依赖 npm。
直接打开:
index.html
即可预览。
如果浏览器或后续功能涉及本地资源安全限制,也可以通过任意本地 HTTP Server 运行。
项目使用本地 Swiper Bundle:
<link rel="stylesheet" href="vendor/swiper/swiper-bundle.min.css">
<script src="vendor/swiper/swiper-bundle.min.js"></script>
<script src="js/main.js"></script>运行页面时不会请求远程 CDN。
Swiper 自身的许可证文件保留在:
vendor/swiper/LICENSE
第三方库建议保持独立目录,不要与项目自己的 css/style.css、js/main.js 混在一起,方便以后升级或替换。
把:
<img src="images/01.svg" alt="">替换为真实项目图片即可。
<strong class="model-title">产品名称</strong>
<span class="model-subtitle">产品或案例说明</span><a class="model-card" href="product-detail.html">复制一个完整的:
<div class="swiper-slide">...</div>Swiper 会自动重新计算轨道长度,不需要修改 slidesPerView。
桌面端只需要修改:
--card-width: 370px;例如改为:
--card-width: 420px;卡片会变宽,同时视口中自然显示更少的卡片。
这也是本组件最重要的设计原则:
不优先决定“一屏显示几张”,而是优先决定“单张卡片应该多宽”。
当前比例:
aspect-ratio: 370 / 497;如果用于横向案例图,可以直接修改,例如:
aspect-ratio: 4 / 3;或者:
aspect-ratio: 16 / 10;Swiper 逻辑不需要同步修改。
适合家具、地板、墙板、建材、机械设备、消费品等视觉型产品目录。
适合建筑、室内设计、网站设计、品牌设计、工程案例和 Portfolio。
适合:
Web Design
Development
SEO
Hosting
Maintenance
AI Integration
适合按照国家、区域或市场展示不同解决方案。
顶部 Tabs + 下方独立轮播的结构可以直接用于按类别浏览作品。
| 对比项 | 普通多卡片轮播 | 本项目 |
|---|---|---|
| 核心参数 | slidesPerView: 1/2/4 |
slidesPerView: 'auto' |
| 卡片宽度 | 随容器平均变化 | 保持自然固定宽度 |
| 可见数量 | 人工规定 | 由视口自然决定 |
| 下一张提示 | 通常没有 | 刻意局部露出 |
| 手机端 | 经常整屏单卡 | 300px 卡片 + 下一张预览 |
| 响应逻辑 | 多个 breakpoint | 主要调整卡片宽度与留白 |
| 适合方向 | 标准信息卡片 | 视觉型产品 / 案例展示 |
两种方案没有绝对优劣,关键是页面目标不同。
如果要求严格保持整齐网格感,固定 slidesPerView 更合适;如果希望卡片拥有稳定视觉尺寸,并强化连续浏览感,本项目这种方式更合适。
slidesPerView: 'auto'- CSS 控制
.swiper-slide宽度 spaceBetween: 24- 外层裁切、Swiper 内部允许可见溢出
- 桌面按钮 + 手机 Touch Swipe
- Swiper 依赖本地化
如果真实项目没有明确需求,不建议默认增加:
- Autoplay
- Pagination Bullets
- Loop
- 多层嵌套 Swiper
- 复杂 3D Effect
- 大量设备断点
这个组件的价值就在于:
用较少的结构和参数,获得稳定、自然、易理解的横向浏览体验。
当前演示已经包含:
- Tabs 使用
role="tablist" / role="tab" / role="tabpanel" aria-selectedaria-controls- 键盘左右方向键切换 Tabs
- Home / End 快速移动
- 轮播按钮包含
aria-label - Swiper A11y 模块开启
- Focus 状态可见
prefers-reduced-motion动画降级
真实项目中替换图片后,建议根据图片内容补充有意义的 alt 文本。
Fixed Width Card
+
slidesPerView: auto
+
spaceBetween
+
overflow hidden / visible
+
Touch Drag
+
Desktop Navigation
+
Responsive CSS Variables
可以把这一类效果理解为:
Fixed-Width Peek Carousel
即:
固定宽度卡片 + 下一张局部预览的响应式横向轮播。
如真实项目确有需要,可以在当前结构基础上继续扩展:
- WordPress / Gutenberg 动态数据
- 产品分类动态生成 Tabs
- API / JSON 数据驱动
- 图片懒加载策略优化
- 卡片数量较多时的性能优化
- 分类 URL 状态同步
- 深色主题
建议继续保持当前组件边界,不为了增加功能而破坏原本简单清晰的结构。