【HarmonyOS组件开发征集活动-翻页时钟和计时器组件】

撸了一个 HarmonyOS 翻页时钟组件,治好了我的"动画焦虑症"

各位 HarmonyOS 开发者兄弟姐妹们,大家好!

最近在折腾 HarmonyOS NEXT 的应用开发,发现一个有意思的现象:系统的基础组件虽然很全,但一旦涉及到拟物化 或者复杂动效,还是得自己动手丰衣足食。

比如,最近我想给我的工具类 App 加一个"翻页时钟"的效果。原本以为网上随便找个轮子就能用,结果发现要么是 API 不兼容,要么是动画僵硬得像是在放 PPT。

作为一名有强迫症的程序员,我不能忍。于是,我花了几个通宵,把这个功能封装成了一个通用的组件库 ------ FlipClock

今天就来跟大家安利一下这个组件,希望能帮大家在赶项目的时候少掉几根头发。


为什么要用这个组件?

在移动端 UI 设计中,"时间"的展示往往是静态的。但如果你想做一款番茄钟床头闹钟 或者秒杀页面,静态数字就显得太单薄了。

FlipClock 的核心价值就在于:它让时间的流逝变得"肉眼可见"。

核心亮点:

  1. 丝滑的机械质感:基于 ArkTS 原生动画能力,还原了物理时钟叶片下落的重力感,不是简单的图片替换。
  2. 智能适配 :不管你是要把时钟放在角落里做挂件,还是全屏展示做主界面,它都能自动计算字号和宽高,不用你去手动写 media query
  3. 零逻辑负担
    • 做时钟?它自己会同步系统时间。
    • 做倒计时?它自动处理 00:00:00 的补零逻辑。
    • 时间短?不足1小时自动隐藏小时位,拒绝尴尬的 00: 前缀。

实战案例:它能怎么玩?

光说不练假把式,给大家展示几个实际的开发场景,看看这个组件怎么帮你省时间。

场景一:极简复古床头钟

需求:一个全屏显示的数字时钟,晚上放在床头看时间,风格要酷,要暗黑模式。

代码实现

typescript 复制代码
import { FlipClock } from 'flip_clock';
import window from '@ohos.window';
import common from '@ohos.app.ability.common';

@Entry
@Component
struct BedsideClock {
  private context = getContext(this) as common.UIAbilityContext;

  aboutToAppear() {
    // 设置横屏
    window.getLastWindow(this.context).then((win) => {
      win.setPreferredOrientation(window.Orientation.LANDSCAPE);
      // 开启全屏沉浸式
      win.setWindowSystemBarEnable([]);
    });
  }

  aboutToDisappear() {
    // 恢复竖屏和系统栏
    window.getLastWindow(this.context).then((win) => {
      win.setPreferredOrientation(window.Orientation.PORTRAIT);
      win.setWindowSystemBarEnable(['status', 'navigation']);
    });
  }

  build() {
    Stack() {
      // 全屏深色背景
      Column()
        .width('100%')
        .height('100%')
        .backgroundColor('#000000')

      Column() {
        // 时钟组件
        FlipClock({ 
          is24Hour: true,             // 24小时制
          flipBgColor: '#1A1A1A',     // 极深灰色卡片,比背景稍亮
          flipTextColor: '#C0C0C0'    // 银灰色文字,不刺眼
        })
          .width('85%')               // 横屏下占比稍微收一点,避免过于撑满
          .height('60%')              // 使用百分比高度,自动适配屏幕
      }
      .justifyContent(FlexAlign.Center)
      .width('100%')
      .height('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#000000')
  }
}

心得:你看,根本不需要去计算字体大小,只要给它一个宽高,它自己就会把数字填满,非常省心。

场景二:电商限时秒杀

需求:双十一活动页,需要一个红红火火的倒计时,营造紧迫感。

代码实现

typescript 复制代码
import { FlipTimer } from 'flip_clock';

@Entry
@Component
struct FlashSale {
  @State isRunning: boolean = true;

  build() {
    Column() {
      // 增加一些装饰元素,营造活动氛围
      Column() {
        Text('🔥 11.11 限时秒杀 🔥')
          .fontSize(28)
          .fontWeight(FontWeight.Bold)
          .fontColor('#D93025')
          .margin({ bottom: 20 })
          .textShadow({ radius: 5, color: '#FFD700', offsetX: 2, offsetY: 2 }) // 金色阴影

        Text('距离本场结束仅剩')
          .fontSize(16)
          .fontColor('#666666')
          .margin({ bottom: 15 })

        FlipTimer({
          isCountDown: true,          // 开启倒计时模式
          initialDuration: 3600,      // 倒计时 1 小时
          isRunning: $isRunning,      // 绑定状态控制开关
          flipBgColor: '#D93025',     // 深红色背景
          flipTextColor: '#FFFFFF',   // 白色文字
          onFinish: () => {
            // 倒计时结束
            console.info('手慢无!活动结束');
            this.isRunning = false;
          }
        })
          .height(80)                 // 稍微调大一点,更显眼
          .width('80%')               // 宽度自适应
          .margin({ bottom: 30 })
          .shadow({ radius: 10, color: 'rgba(217, 48, 37, 0.4)', offsetY: 5 }) // 增加投影,提升立体感

        Button('立即抢购')
          .width(200)
          .height(50)
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .backgroundColor('#D93025')
          .fontColor('#FFFFFF')
          .shadow({ radius: 5, color: 'rgba(0,0,0,0.3)', offsetY: 3 })
          .onClick(() => {
            console.info('点击抢购');
          })
      }
      .width('100%')
      .padding(20)
      .backgroundColor('#FFF5F5') // 淡红色背景底板
      .borderRadius(20)
      .shadow({ radius: 20, color: 'rgba(0,0,0,0.1)' }) // 卡片投影
      .alignItems(HorizontalAlign.Center)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center) // 垂直居中
    .alignItems(HorizontalAlign.Center) // 水平居中
    .backgroundColor('#FFFFFF')
  }
}

心得onFinish 回调非常实用,不用自己去写 setInterval 还要担心内存泄漏,组件内部都处理好了。

场景三:专注力番茄钟

需求:一个 25 分钟的倒计时,开始/暂停可控。

代码实现

typescript 复制代码
import { FlipTimer } from 'flip_clock';

@Entry
@Component
struct PomodoroTimer {
  @State isRunning: boolean = false;

  build() {
    Column() {
      // 标题
      Text('专注 25 分钟')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .fontColor('#333333')
        .margin({ bottom: 16 })

      Text('点击下方按钮开始 / 暂停倒计时')
        .fontSize(14)
        .fontColor('#888888')
        .margin({ bottom: 32 })

      // 25 分钟倒计时:25 * 60 = 1500 秒
      FlipTimer({
        isCountDown: true,
        initialDuration: 25 * 60,
        isRunning: $isRunning,
        flipBgColor: '#333333',
        flipTextColor: '#FFFFFF',
        onFinish: () => {
          console.info('专注结束,休息一下吧!');
          this.isRunning = false;
        }
      })
        .width('80%')
        .height(100)
        .borderRadius(16)
        .margin({ bottom: 32 })

      Button(this.isRunning ? '暂停专注' : '开始专注')
        .width(200)
        .height(44)
        .fontSize(18)
        .fontWeight(FontWeight.Medium)
        .backgroundColor(this.isRunning ? '#FF9F43' : '#00C853')
        .fontColor('#FFFFFF')
        .onClick(() => {
          this.isRunning = !this.isRunning;
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .backgroundColor('#FAFAFA')
  }
}

心得 :组件内部监听了 isRunning 的变化,暂停时动画会立即停止,恢复时继续走,状态管理非常符合 ArkUI 的响应式逻辑。


开发心得与避坑指南

在开发这个组件的过程中,我也积累了一些关于 HarmonyOS 动画开发的经验,分享给大家:

  1. 关于 Stack 布局

    翻页效果的本质是"遮罩"。我使用了 4 层 Stack 堆叠(上层旧字、下层旧字、上层新字、下层新字)。在 HarmonyOS 中,Stack 布局对于处理这种重叠动画非常高效,比计算绝对定位坐标要稳得多。

  2. 性能优化

    一开始我担心每一秒都触发 UI 刷新会不会卡顿。后来发现 ArkUI 的渲染性能相当不错。为了保险起见,我在组件内部做了 diff 校验------只有数字真正发生变化的那一位才会触发翻页动画 。比如从 10:5911:00,分和时都会动;但平时只有"秒"在动。这样大大降低了 GPU 的负载。

  3. 圆角的处理

    大家在使用时,建议给组件设置一个 borderRadius。我在组件内部做了处理,让翻页的卡片继承这个圆角,这样看起来不会像老式电子表那么生硬,更符合现代 App 的圆润风格。


拿去用吧,不客气!

为了方便大家使用,我已经把组件集成到了组件市场,大家可以在组件市场下载使用。

在Tools->Component Market

在搜索中输入"翻页时钟"就可以找到我的组件了,直接安装就可以在项目中使用。

并且我也把包发到 OHPM 了。

安装方法

bash 复制代码
ohpm install flip_clock

仓库地址
(https://gitcode.com/qiaomu8559968/FlipClock.git)

如果你在使用过程中发现任何 Bug,或者有更酷的想法(比如加个翻页音效?),欢迎在评论区留言或者去仓库提 Issue。

做开源组件不易,如果这个小组件帮你在老板面前装到了,或者帮你早下班了半小时,求个 Star ⭐️ 不过分吧? 😉

让我们一起把 HarmonyOS 的生态建得更好玩!

相关推荐
2501_948122632 小时前
React Native for OpenHarmony 实战:Steam 资讯 App 浏览历史页面
javascript·react native·react.js·游戏·ecmascript·harmonyos
lili-felicity2 小时前
React Native for OpenHarmony 实战:图片懒加载(LazyLoading) 详解
javascript·react native·harmonyos
lili-felicity2 小时前
React Native for OpenHarmony 实战:滑动验证码 (Slider Captcha) 验证功能 详解
react native·react.js·harmonyos
不爱吃糖的程序媛2 小时前
跨平台框架适配鸿蒙(OpenHarmony)信息汇总表
华为·harmonyos
南村群童欺我老无力.12 小时前
Flutter应用鸿蒙迁移实战:性能优化与渐进式迁移指南
javascript·flutter·ci/cd·华为·性能优化·typescript·harmonyos
水手冰激淋12 小时前
rn_for_openharmony狗狗之家app实战-领养完成实现
harmonyos
奔跑的露西ly16 小时前
【HarmonyOS NEXT】Stage模型
华为·harmonyos
威哥爱编程17 小时前
鸿蒙 APP 还是卡顿?API 21 性能优化这 3 招,立竿见影!
harmonyos·arkts·arkui
威哥爱编程17 小时前
List 组件渲染慢?鸿蒙API 21 复用机制深度剖析,一行代码提速 200%!
harmonyos·arkts·arkui