Swiper 轮播组件实现 3 页新用户引导
前言新用户引导是应用留存率的关键环节。“海风日记“的引导页通过 Swiper 轮播组件实现了 3 页功能展示配合渐变圆形插画、功能亮点卡片和分页圆点让新用户快速了解应用的核心功能。本文将从OnboardingPage.ets源码出发深入讲解 Swiper 组件的完整配置、分页控制、自定义指示器以及引导页的交互设计。一、Swiper 组件概述1.1 基本用法Entry Component struct OnboardingPage { private swiperController: SwiperController new SwiperController() build() { Swiper(this.swiperController) { ForEach(PAGES, (page: OnboardingPageData) { Column() { // 页面内容 } }) } .layoutWeight(1) .indicator(false) // 隐藏默认指示器 .loop(false) // 禁止循环 .onChange((index) { this.currentIndex index }) } }1.2 Swiper 的核心属性属性类型说明海风日记的值indicatorboolean是否显示默认指示器false自定义loopboolean是否循环播放falseautoPlayboolean是否自动播放未设置默认 falseintervalnumber自动播放间隔未设置durationnumber切换动画时长未设置默认值onChangecallback切换回调更新 currentIndexcontrollerSwiperController控制器用于编程式控制1.3 SwiperController 控制器private swiperController: SwiperController new SwiperController() // 编程式控制方法 this.swiperController.showNext() // 下一页 this.swiperController.showPrevious() // 上一页 this.swiperController.finishAnimation() // 结束动画二、引导页的数据结构2.1 页面数据模型interface FeatureItem { icon: string title: string desc: string iconBg: string } interface OnboardingPageData { bg: string // 页面背景色 circleGradient: string[] // 圆形渐变 title: string // 页面标题 desc: string // 页面描述 features: FeatureItem[] // 功能亮点列表 }2.2 三页数据定义const PAGES: OnboardingPageData[] [ { bg: #FFF8F0, circleGradient: [#FFD4A0, #FFCC90], title: 欢迎使用海风日记, desc: 让每一天随海风飘散记录生活中的点滴美好, features: [ { icon: ✍️, title: 轻松书写, desc: 简洁优雅的写作体验, iconBg: #FFF3E0 }, { icon: , title: 心情记录, desc: 用表情标记每日心情, iconBg: #FFF3E0 }, ] }, { bg: #FFF8F0, circleGradient: [#FFE4B0, #FFDCA0], title: 记录每一天的精彩, desc: 支持文字、图片、心情标签\n让每篇日记都有温度, features: [ { icon: , title: 图文并茂, desc: 一键添加照片让记忆更生动, iconBg: #FFF3E0 }, { icon: ️, title: 标签分类, desc: 自定义标签轻松归档整理, iconBg: #FFF3E0 }, ] }, { bg: #F0F7FF, circleGradient: [#C8E6FF, #D4EDFF], title: 回顾成长看见坚持, desc: 日历热力图 · 写作统计 · 情绪趋势\n每一天的记录都是珍贵的印记, features: [ { icon: , title: 日历视图, desc: 一眼看清哪些天有记录, iconBg: #E8F4FF }, { icon: , title: 写作统计, desc: 连续天数、总字数见证坚持, iconBg: #E8F4FF }, ] } ]三、引导页的布局结构3.1 整体布局Stack() { Column() { // ── Swiper 轮播 ──────────────────────────────── Swiper(this.swiperController) { ForEach(PAGES, (page: OnboardingPageData, idx: number) { this.buildPage(page, idx) }) } .layoutWeight(1) .indicator(false) .loop(false) .onChange((index: number) { this.currentIndex index }) // ── 底部固定区域 ──────────────────────────────── Column({ space: 16 }) { // 分页圆点 this.pageDots() // 底部按钮 this.bottomButton() } .padding({ top: 16, bottom: 40 }) .backgroundColor(PAGES[this.currentIndex].bg) } // ── 右上角跳过按钮 ──────────────────────────────── Button(跳过) .position({ top: 52, right: 20 }) .onClick(() { router.replaceUrl({ url: pages/Index }) }) }3.2 每页的内容构建Builder buildPage(page: OnboardingPageData, idx: number) { Column() { // 渐变圆形插画 Column() { Text(idx 0 ? : idx 1 ? : ).fontSize(80) } .width(260).height(260).borderRadius(130) .linearGradient({ angle: 135, colors: [[page.circleGradient[0], 0], [page.circleGradient[1], 1]] }) .justifyContent(FlexAlign.Center) .margin({ top: 64 }) // 标题 Text(page.title).fontSize(24).fontColor(COLOR_TEXT_MAIN) .fontWeight(FontWeight.Bold).margin({ top: 28 }) // 描述 Text(page.desc).fontSize(15).fontColor(COLOR_TEXT_SECONDARY) .textAlign(TextAlign.Center).lineHeight(24).margin({ top: 12 }) // 功能亮点卡片 Column({ space: 12 }) { ForEach(page.features, (feat: FeatureItem) { this.featureCard(feat) }) } .padding({ left: 24, right: 24, top: 24 }) } .width(100%).height(100%) .backgroundColor(page.bg) }四、自定义分页指示器4.1 分页圆点Builder pageDots() { Row({ space: 8 }) { ForEach(PAGES, (page: OnboardingPageData, idx: number) { Column() .width(this.currentIndex idx ? 20 : 6) // 选中态变宽 .height(6) .borderRadius(3) .backgroundColor( this.currentIndex idx ? COLOR_PRIMARY : #DDDDDD ) .animation({ duration: 200 }) // 动画过渡 }) } .width(100%) .justifyContent(FlexAlign.Center) }4.2 指示器动画选中圆点的宽度从 6vp 变为 20vp配合 200ms 动画状态宽度颜色圆角未选中6vp#DDDDDD灰色3vp选中20vpCOLOR_PRIMARY橙色3vp五、底部按钮与交互5.1 底部按钮Builder bottomButton() { Button(this.currentIndex 2 ? 立即开始 : 下一步) .width(85%).height(52) .fontSize(17).fontColor(#FFFFFF).fontWeight(FontWeight.Bold) .backgroundColor(COLOR_PRIMARY).borderRadius(26) .onClick(() { if (this.currentIndex 2) { // 最后一页进入首页 router.replaceUrl({ url: pages/Index }) } else { // 翻页 this.swiperController.showNext() } }) }5.2 跳过按钮// 右上角浮层跳过按钮 Button(跳过) .fontSize(14).fontColor(#999999) .backgroundColor(rgba(255,255,255,0.6)) .borderRadius(16).height(32).padding({ left: 14, right: 14 }) .position({ top: 52, right: 20 }) .onClick(() { router.replaceUrl({ url: pages/Index }) })5.3 交互逻辑┌─────────────────────────────────────────┐ │ 右上角跳过按钮 → 立即进入首页 │ │ │ │ 第 1 页下一步 → 翻到第 2 页 │ │ 第 2 页下一步 → 翻到第 3 页 │ │ 第 3 页立即开始 → 进入首页 │ │ │ │ 分页圆点点击无效仅展示当前进度 │ └─────────────────────────────────────────┘六、引导页的完整交互流程6.1 用户操作流程应用安装 → 首次打开 → 闪屏页 → 登录页 → 引导页 → 首页 ↓ 跳过 直接进入首页6.2 生命周期顺序引导页 → 点击下一步(3次) → router.replaceUrl(pages/Index) ↓ SplashPage 销毁 ↓ LoginPhonePage 销毁 ↓ OnboardingPage 销毁 ↓ Index 创建 → HomeTab 显示七、常见问题与排查7.1 Swiper 不显示问题Swiper 区域显示为空白。原因Swiper 没有设置宽高或子组件为空。解决方案为 Swiper 设置layoutWeight(1)或固定宽高。7.2 指示器重复问题同时显示了默认指示器和自定义指示器。原因未设置indicator(false)。解决方案Swiper() .indicator(false) // 隐藏默认指示器7.3 循环导致的问题问题最后一页可以继续翻到第一页。原因loop属性为true。解决方案Swiper() .loop(false) // 禁止循环总结本文通过“海风日记“引导页的源码深入讲解了 Swiper 轮播组件的完整用法Swiper 基础属性配置、控制器、事件回调数据驱动页面数据的模型定义和三页数据布局结构Swiper 底部固定区域 浮层按钮自定义指示器分页圆点的选中态动画交互逻辑下一页/立即开始/跳过三种操作完整流程从闪屏到引导页到首页的跳转链路下一篇文章将深入讲解SwiperController 编程式控制翻页与 showNext 的用法敬请期待。如果这篇文章对你有帮助欢迎点赞、收藏⭐、关注你的支持是我持续创作的动力相关资源Swiper 组件文档SwiperController 文档Router 路由文档海风日记项目源码[HarmonyOS 开发者官网](https://atomgit.com/openharmony/docs开源鸿蒙跨平台社区ArkUI 动画概述position 定位文档