HarmonyOS 6.1 实战:Swiper 轮播图与页面滑动详解

前言

Swiper是 ArkUI 中专为轮播图、引导页、图片画廊等场景设计的滑动容器。它内置指示器、自动播放、循环滑动等能力,配合SwiperController可以实现编程式翻页。本文通过完整示例演示 Swiper 的核心特性。

运行效果

初始状态(Banner 轮播)

滑动至第二页

核心 API 一览

API说明
Swiper(controller?)轮播容器,子组件逐页显示
.loop(bool)是否循环播放,默认 true
.autoPlay(bool)是否自动播放
.interval(ms)自动播放间隔(毫秒)
.indicator(bool/DotIndicator/DigitIndicator)指示器样式
.index(n)当前页索引
.duration(ms)翻页动画时长
.curve(Curve)翻页动画曲线
.onChange(idx)页面切换回调
.itemSpace(n)相邻页的间距(用于“卡片流“效果)
controller.showNext()编程式切换到下一页
controller.showPrevious()编程式切换到上一页

完整示例代码

interface BannerItem { title: string subtitle: string bg: string icon: string } interface CardItem { name: string desc: string color: string } @Entry @Component struct Index { @State currentBanner: number = 0 @State currentCard: number = 0 @State autoPlay: boolean = true private bannerController: SwiperController = new SwiperController() private cardController: SwiperController = new SwiperController() private banners: BannerItem[] = [ { title: 'HarmonyOS 6.1', subtitle: '全新 ArkUI 组件体系正式上线', bg: '#0044cc', icon: '🚀' }, { title: '折叠屏适配', subtitle: '一套代码,完美适配多种屏幕形态', bg: '#cc3300', icon: '📱' }, { title: '性能优化', subtitle: 'Swiper 流畅帧率提升 40%', bg: '#006633', icon: '⚡' }, { title: '开发者工具', subtitle: 'DevEco Studio 全面升级', bg: '#6600aa', icon: '🛠' }, ] private cards: CardItem[] = [ { name: '组件库', desc: '200+ 内置 UI 组件', color: '#e8f0ff' }, { name: '动画引擎', desc: '60fps 流畅动效', color: '#fff0e8' }, { name: '跨平台', desc: '一次开发多端运行', color: '#e8fff0' }, { name: '状态管理', desc: 'MVVM 响应式架构', color: '#f8e8ff' }, ] build() { Scroll() { Column({ space: 20 }) { Text('Swiper 轮播 & 页面滑动') .fontSize(20) .fontWeight(FontWeight.Bold) .fontColor('#1a1a1a') .width('100%') .padding({ left: 16, top: 16 }) // ── 1. 全屏 Banner 轮播 ── Column({ space: 10 }) { Text('① 全屏 Banner(自动轮播)') .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor('#333') .padding({ left: 16 }) Swiper(this.bannerController) { ForEach(this.banners, (item: BannerItem) => { Column({ space: 8 }) { Text(item.icon).fontSize(48) Text(item.title) .fontSize(22) .fontWeight(FontWeight.Bold) .fontColor('#ffffff') Text(item.subtitle) .fontSize(13) .fontColor('rgba(255,255,255,0.85)') .textAlign(TextAlign.Center) .padding({ left: 24, right: 24 }) } .width('100%') .height(180) .backgroundColor(item.bg) .justifyContent(FlexAlign.Center) .borderRadius(12) }) } .loop(true) .autoPlay(this.autoPlay) .interval(2500) .duration(400) .curve(Curve.EaseOut) .indicator( new DotIndicator() .itemWidth(6) .itemHeight(6) .selectedItemWidth(20) .selectedItemHeight(6) .color('#88bbff') .selectedColor('#ffffff') ) .onChange((idx: number) => { this.currentBanner = idx }) .margin({ left: 16, right: 16 }) // 控制按钮 Row({ space: 10 }) { Button('上一张') .height(36) .fontSize(13) .backgroundColor('#0066ff') .onClick(() => { this.bannerController.showPrevious() }) Text('第 ' + (this.currentBanner + 1).toString() + ' / ' + this.banners.length.toString() + ' 张') .fontSize(13) .fontColor('#888') .layoutWeight(1) .textAlign(TextAlign.Center) Button('下一张') .height(36) .fontSize(13) .backgroundColor('#0066ff') .onClick(() => { this.bannerController.showNext() }) Button(this.autoPlay ? '停止' : '自动') .height(36) .fontSize(13) .backgroundColor(this.autoPlay ? '#ff6600' : '#00aa44') .onClick(() => { this.autoPlay = !this.autoPlay }) } .width('100%') .padding({ left: 16, right: 16 }) } .width('100%') // ── 2. 卡片流(itemSpace + displayCount)── Column({ space: 10 }) { Text('② 卡片流(部分预览相邻页)') .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor('#333') .padding({ left: 16 }) Swiper(this.cardController) { ForEach(this.cards, (card: CardItem) => { Column({ space: 8 }) { Text(card.name) .fontSize(18) .fontWeight(FontWeight.Bold) .fontColor('#333') Text(card.desc) .fontSize(13) .fontColor('#888') } .width('100%') .height(100) .backgroundColor(card.color) .borderRadius(14) .justifyContent(FlexAlign.Center) .border({ width: 1, color: '#e0e0e0' }) }) } .loop(false) .autoPlay(false) .itemSpace(12) .indicator(false) .curve(Curve.Smooth) .onChange((idx: number) => { this.currentCard = idx }) .margin({ left: 32, right: 32 }) Row({ space: 4 }) { ForEach(this.cards, (_c: CardItem, idx: number) => { Column() .width(this.currentCard === idx ? 20 : 6) .height(6) .backgroundColor(this.currentCard === idx ? '#0066ff' : '#d0d0d0') .borderRadius(3) }) } .justifyContent(FlexAlign.Center) } .width('100%') // ── 3. 数字指示器 ── Column({ space: 10 }) { Text('③ 数字指示器(DigitIndicator)') .fontSize(15) .fontWeight(FontWeight.Bold) .fontColor('#333') .padding({ left: 16 }) Swiper() { ForEach(['#ff6b6b', '#feca57', '#48dbfb', '#1dd1a1'], (color: string) => { Column() .width('100%') .height(100) .backgroundColor(color) .borderRadius(12) }) } .loop(true) .autoPlay(true) .interval(1800) .indicator( new DigitIndicator() .fontColor('#ffffff') .selectedFontColor('#ffffff') .digitFont({ size: 14, weight: FontWeight.Bold }) ) .margin({ left: 16, right: 16 }) } .width('100%') Column().height(20) } .width('100%') } .width('100%') .height('100%') .backgroundColor('#ffffff') } }

关键知识点

1. 指示器(Indicator)三种形式

// 布尔值:显示/隐藏默认圆点 .indicator(true) .indicator(false) // DotIndicator:自定义圆点样式 .indicator( new DotIndicator() .itemWidth(6).itemHeight(6) .selectedItemWidth(20).selectedItemHeight(6) // 选中时拉长为胶囊 .color('#cccccc') .selectedColor('#0066ff') ) // DigitIndicator:数字形式 "1 / 4" .indicator( new DigitIndicator() .fontColor('#ffffff') .selectedFontColor('#ffffff') )

2. 卡片流效果

通过.itemSpace(n)设置相邻卡片间距,并给 Swiper 左右各留 margin(大于间距),就能看到相邻卡片的“预览“效果:

Swiper() .itemSpace(12) // 相邻卡片间距 .indicator(false) // 通常卡片流不需要指示器 .margin({ left: 32, right: 32 }) // 留出边距,让相邻卡片露出

3. SwiperController 编程式控制

private controller: SwiperController = new SwiperController() Swiper(this.controller) { ... } // 翻页 this.controller.showNext() this.controller.showPrevious()

4. 动画曲线(Curve)

效果适合场景
Curve.Linear匀速少用
Curve.EaseOut减速结束Banner 轮播
Curve.Smooth平滑过渡卡片翻页
Curve.FastOutSlowInMaterial 风格通用

5. onChange 与 index 的关系

.onChange(idx)在每次翻页完成时触发,idx是目标页的索引(从 0 开始)。若需要初始化时指定展示页,用.index(n)属性:

Swiper() .index(2) // 从第 3 页(下标 2)开始显示 .onChange((idx: number) => { this.currentPage = idx })

小结

  • Swiper专为页面级滑动和轮播设计,内置指示器、自动播放、循环滑动
  • DotIndicator支持选中态拉伸为胶囊形,视觉上更优雅
  • SwiperController提供showNext/showPrevious用于外部按钮控制
  • itemSpace + margin组合实现卡片流“露出相邻页“的效果
  • 注意区分loop(逻辑循环)和autoPlay + interval(自动翻页)是独立配置项