前言

这次做的是一个 HarmonyOS 7 国庆主题应用。首页用祝福弹幕营造节日氛围,点击按钮进入贺卡页后,可以查看国庆倒计时、播放烟花动画,也可以通过摇一摇手机触发烟花。两台设备满足条件时,还能使用碰一碰分享祝福。

项目规模不大,但覆盖了 ArkUI 状态管理、页面路由、动画编排、加速度传感器和设备协同等能力。本文不罗列完整源码,只挑几个对实现和调试真正有帮助的片段展开说明。

项目结构与实现思路

页面代码位于:

entry/src/main/ets/pages/
├── Index.ets
└── Views/
    └── NationalDayFestival.ets

两个页面各自负责一段完整体验:

页面主要职责
Index.ets渐变背景、祝福弹幕和页面跳转
Views/NationalDayFestival.ets国庆倒计时、烟花粒子、触摸光效、传感器监听和碰一碰分享

项目使用 HarmonyOS 7 的 ArkTS 声明式 UI。页面中的动画开关、粒子位置和文案显隐都由 @State 驱动,再配合 animateTo 完成过渡。

这个案例没有一开始就拆出大量服务类。原因很简单:当前核心逻辑只服务于两个页面,过早分层反而会增加跳转成本。先把交互链路和生命周期处理清楚,后续能力继续增加时,再把传感器、分享和动画控制器独立出去更合适。

准备项目与开发素材

先创建 HarmonyOS 7 空项目,再把提示词、页面草图、功能清单和验收条件放进项目的素材目录。这样码道可以直接结合项目上下文工作,减少反复解释需求的次数。

国庆献礼项目分层架构

准备好基础骨架后,将项目托管到代码平台,方便保留修改记录和回滚有问题的实现。

image

这里要注意,需求文档中的架构图表达的是预期模块关系,不代表源码里已经存在所有同名模块。检查实现时,应以当前页面文件、实际导入和调用链为准,避免出现“文档写了、代码却没有”的情况。

将码道安装到本地

码道既可以通过网页使用,也可以安装到本地。我的主要开发环境在本地,因此更习惯让它直接读取项目目录并配合编辑器完成修改。网页端界面如下:

image

在 macOS 终端中执行安装命令:

curl -fsSL https://raw.atomgit.com/atomgit_atomcode/atomcode/raw/main/scripts/install.sh | sh

image

不同操作系统的安装方式可能不同,本文使用的是 macOS。安装结束后,先检查版本号:

atomcode --version

image

能够正常输出版本号后,执行 atomcode 打开运行面板,再根据提示输入 /login 完成登录。

image

首页:祝福弹幕与页面路由

先看首页效果:

展示祝福语

Index.ets 使用 ForEach 创建多条祝福弹幕。每条弹幕都有独立的行号、启动延迟、滚动时间和透明度,因此不会整齐地挤在同一条轨道上。

ForEach(IDX_DANMAKU, (item: IDanmaku, index: number) => {
  IdxDanmakuItem({ item: item })
}, (item: IDanmaku, index: number) => index.toString())

弹幕组件内部通过 alive 标记判断页面是否仍然可见,避免组件退出后继续递归调度定时任务。弹幕层还设置了不参与命中测试,否则全屏弹幕容器可能会拦截底部按钮的点击事件。

点击“进入国庆贺卡”后,通过 router.pushUrl 跳转到主题页:

Button('进入国庆贺卡')
  .onClick(() => {
    this.btnGlow = !this.btnGlow;
    router.pushUrl({ url: 'pages/Views/NationalDayFestival' });
  })

路由代码本身很短,真正容易出错的是配置:页面路径、目录大小写和路由表必须保持一致。此类问题通常不会在编辑阶段直接暴露,而是运行后才表现为跳转失败,因此需要在预览器或真机上实际点击验证。

贺卡页:用状态编排烟花动画

贺卡页的烟花和祝福语消散效果如下:

祝福语化成粒子消散

NationalDayFestival.ets 没有把烟花做成一段不可控的连续动画,而是拆成几个可以独立修改的状态:

  • fireworksOn:决定是否渲染烟花层;
  • fireScale:控制粒子向外扩散;
  • fireFade:控制烟花余晖淡出;
  • blessingOpacity:控制祝福语显隐;
  • glowX、glowY:记录手指移动后的光斑位置。

触发烟花时,页面先生成粒子数据,再分阶段更新缩放和透明度。上一轮动画尚未结束时会直接返回,防止用户连续点击造成粒子叠加、定时任务堆积和明显掉帧。动画结束后再恢复初始状态,下一次触发就能从干净状态重新开始。

这种写法的价值在于可调试:如果某一段效果异常,可以分别检查粒子生成、扩散、淡出或祝福语浮层,而不是在一条长动画链里逐行排查。

处理国庆倒计时的日期边界

倒计时并不只是计算“今年 10 月 1 日减去今天”。如果当前日期已经过了国庆节,目标日期必须切换到下一年,否则页面会得到负数。JavaScript 的月份从 0 开始,所以 9 代表 10 月。

private computeDaysLeft(): void {
  const now: Date = new Date();
  const year: number = now.getMonth() > 8
    ? now.getFullYear() + 1
    : now.getFullYear();
  const target: Date = new Date(year, 9, 1, 0, 0, 0);
  const diff: number = target.getTime() - now.getTime();
  this.daysLeft = diff > 0 ? Math.ceil(diff / 86400000) : 0;
}

这里使用设备本地时间,适合节日展示类应用。若业务面向多个时区,或倒计时必须与服务器保持一致,就不能只依赖设备时间,需要统一时区和时间来源。

还有一个容易忽略的细节:这段逻辑在 10 月 1 日零点之后会直接计算下一年的国庆。如果产品希望国庆当天始终显示“0 天”或“今天”,需要单独增加当天判断,而不是照搬当前规则。

摇一摇触发烟花

为了让交互更自然,页面除了保留“点我放烟花”按钮,还监听了加速度传感器。回调中先计算三轴加速度的模长,再与重力基准比较;只有偏离超过阈值,并且与上一次触发间隔足够长,才会播放烟花。

sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {
  const magnitude: number = Math.sqrt(
    data.x * data.x + data.y * data.y + data.z * data.z
  );
  const deviation: number = Math.abs(magnitude - ND_GRAVITY);
  const now: number = Date.now();

  if (deviation > ND_SHAKE_DEVIATION && now - this.lastShakeTs > ND_SHAKE_INTERVAL) {
    this.lastShakeTs = now;
    this.burstFireworks();
  }
});

这里同时做了两层限制:

  1. 幅度阈值:过滤拿起手机、转动屏幕等普通动作;
  2. 时间去抖:避免一次连续摇动触发多轮烟花。

传感器阈值没有通用答案,不同设备的采样和用户握持方式都会影响结果。调试时不要只在模拟器中验证,应当在目标设备上多试几次,再调整 ND_SHAKE_DEVIATION 和 ND_SHAKE_INTERVAL。

按钮入口也不应删除。它既是传感器不可用时的降级方案,也方便开发阶段快速测试动画。页面退出时还要注销传感器监听,并清理仍在运行的定时器,否则返回页面后可能出现重复订阅和多次触发。

如何让码道更稳定地完成开发任务

与其一次性要求码道完成整个页面,更稳妥的方式是按可验收的功能拆分任务:

  1. 完成首页视觉效果、弹幕和路由;
  2. 完成倒计时和点击放烟花;
  3. 增加传感器监听、生命周期清理和碰一碰分享;
  4. 检查路由路径、状态复位以及真机兼容性。

提示词中最好同时写清项目路径、技术版本、素材位置和验收条件:

在 /xxxxxxx/entry 项目中完成以下需求:
1. 项目基于 HarmonyOS 7 和 ArkTS;
2. 需求说明和素材位于 /xxxxxx/物料;
3. 完成首页、贺卡页、倒计时和烟花交互;
4. 检查页面退出时的传感器和定时器清理;
5. 修改后说明涉及的文件和真机验证步骤。

image

第一轮编码结束后,不要只看“任务已完成”的回复,可以继续让码道按需求清单检查文件,并明确列出尚未验证的设备能力。传感器和碰一碰分享依赖真实设备环境,代码生成完成不等于功能已经验收。

image

碰一碰分享的使用前提

碰一碰分享效果如下:

碰一碰分享

两台设备都安装应用并满足系统能力条件时,可以通过碰一碰发送国庆祝福。发送端负责组装分享数据,接收端根据应用内约定处理内容。

接收端效果如下:

分享接收端

这类能力不能只验证“监听是否注册成功”,还要检查异常路径:目标设备未安装应用、系统能力未开启、分享中断或数据解析失败时,页面应该给出明确提示,而不是静默无响应。页面退出时同样要取消监听,避免重新进入页面后注册多次。

真机调试重点

传感器、碰一碰分享、动画时序和页面生命周期都依赖运行环境,真机阶段出现问题并不意外。反馈问题时,尽量同时提供:

  • 可以稳定复现的操作步骤;
  • 完整错误信息和错误码;
  • 出问题的页面、函数或日志位置;
  • 设备型号与系统版本;
  • 预期行为和实际行为的差异。

image

不同设备和不同模型生成的实现可能存在差异。每次调整阈值、动画时间或生命周期逻辑后,都应该重新编译并在目标设备复测,不能只依据静态代码判断功能是否完成。

总结

这个案例表面上是一个节日主题页面,实际串联了 ArkUI 状态驱动、动画生命周期、日期边界、传感器去抖和设备协同。最值得保留的开发习惯有三个:先把需求拆成可以验收的小任务;为设备能力准备可操作的降级入口;任何涉及传感器和跨设备交互的功能,都以真机结果为准。

项目源码已开源,可通过以下地址查看:

点击查看 CityHappy 项目

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐