撸了一个 HarmonyOS 翻页时钟组件,治好了我的"动画焦虑症"
各位 HarmonyOS 开发者兄弟姐妹们,大家好!
最近在折腾 HarmonyOS NEXT 的应用开发,发现一个有意思的现象:系统的基础组件虽然很全,但一旦涉及到拟物化 或者复杂动效,还是得自己动手丰衣足食。
比如,最近我想给我的工具类 App 加一个"翻页时钟"的效果。原本以为网上随便找个轮子就能用,结果发现要么是 API 不兼容,要么是动画僵硬得像是在放 PPT。
作为一名有强迫症的程序员,我不能忍。于是,我花了几个通宵,把这个功能封装成了一个通用的组件库 ------ FlipClock。

今天就来跟大家安利一下这个组件,希望能帮大家在赶项目的时候少掉几根头发。
为什么要用这个组件?
在移动端 UI 设计中,"时间"的展示往往是静态的。但如果你想做一款番茄钟 、床头闹钟 或者秒杀页面,静态数字就显得太单薄了。
FlipClock 的核心价值就在于:它让时间的流逝变得"肉眼可见"。
核心亮点:
- 丝滑的机械质感:基于 ArkTS 原生动画能力,还原了物理时钟叶片下落的重力感,不是简单的图片替换。
- 智能适配 :不管你是要把时钟放在角落里做挂件,还是全屏展示做主界面,它都能自动计算字号和宽高,不用你去手动写
media query。 - 零逻辑负担 :
- 做时钟?它自己会同步系统时间。
- 做倒计时?它自动处理
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 动画开发的经验,分享给大家:
-
关于 Stack 布局 :
翻页效果的本质是"遮罩"。我使用了 4 层 Stack 堆叠(上层旧字、下层旧字、上层新字、下层新字)。在 HarmonyOS 中,
Stack布局对于处理这种重叠动画非常高效,比计算绝对定位坐标要稳得多。 -
性能优化 :
一开始我担心每一秒都触发 UI 刷新会不会卡顿。后来发现 ArkUI 的渲染性能相当不错。为了保险起见,我在组件内部做了 diff 校验------只有数字真正发生变化的那一位才会触发翻页动画 。比如从
10:59到11:00,分和时都会动;但平时只有"秒"在动。这样大大降低了 GPU 的负载。 -
圆角的处理 :
大家在使用时,建议给组件设置一个
borderRadius。我在组件内部做了处理,让翻页的卡片继承这个圆角,这样看起来不会像老式电子表那么生硬,更符合现代 App 的圆润风格。
拿去用吧,不客气!
为了方便大家使用,我已经把组件集成到了组件市场,大家可以在组件市场下载使用。

在Tools->Component Market

在搜索中输入"翻页时钟"就可以找到我的组件了,直接安装就可以在项目中使用。
并且我也把包发到 OHPM 了。
安装方法:
bash
ohpm install flip_clock
仓库地址 :
(https://gitcode.com/qiaomu8559968/FlipClock.git)
如果你在使用过程中发现任何 Bug,或者有更酷的想法(比如加个翻页音效?),欢迎在评论区留言或者去仓库提 Issue。
做开源组件不易,如果这个小组件帮你在老板面前装到了,或者帮你早下班了半小时,求个 Star ⭐️ 不过分吧? 😉
让我们一起把 HarmonyOS 的生态建得更好玩!