首页/日程页:当前时间红线改为定时器驱动,不再冻结在冷启动时刻 (#81)

用户:首页今天的日程,那个红线,不会动态更新,只有刚打开应用的时候更新,
如果不清后台,就一直不动了。查一下这个更新机制,需要能够动态更新。

根因:nowMin: this.nowMinutes() 是**在 build 里直接调方法** ——
ArkUI 不会因为时间流逝重算任何东西,那个方法只在首次 build 时求值一次,
红线永远停在打开 App 那一刻。与 pitfalls 里 ForEach(this.xxx(), ...) 追踪不到
是同一条规则:build 里只有字段(@Local/@Param)建依赖,方法调用不建。

改法:@Local nowClockMin + setInterval 30 秒一跳改它;传参改成读该字段。
- 30 秒而非 1 分钟:1 分钟在整分附近显得迟一拍,30 秒只改一个 number,成本极低;
- 先立即刷一次,否则首屏红线停在默认 0(=00:00,跑到视窗外面);
- aboutToAppear 起、aboutToDisappear 停 —— 首页切模块走 if/else 分发,
  组件会销毁重建,成对钩子必然触发;
- 绝不在 build() 里起定时器(build 可能多次调用,定时器会越叠越多)。

顺带修 FbCalendarPage 完全相同的两处(月视图 + 议程视图)——
用户从首页切过去看到同样的死线。

验证(真机 5NC0225B27004718,红线 #FF3B30 像素扫描):
09:57:22 红线 y=1359.0,10:00:24 y=1366.5(下移 7.5px,
理论 3.03 分钟 = 6.9px,吻合);
切到别的模块再切回后仍在走:1368.5 → 90 秒后 1372.5(4px≈1.75 分钟,吻合),
说明 aboutToDisappear 清掉旧定时器、aboutToAppear 起了新的,无泄漏无停摆。
hvigorw assembleHap BUILD SUCCESSFUL。
This commit is contained in:
2026-11-06 10:02:45 +08:00
parent 6a8a97286a
commit b0e12f14fe
2 changed files with 107 additions and 3 deletions
@@ -205,6 +205,16 @@ export struct FbCalendarPage {
@Local showMonth: number = new Date().getMonth() + 1; @Local showMonth: number = new Date().getMonth() + 1;
/** 选中的日期 `YYYY-MM-DD` */ /** 选中的日期 `YYYY-MM-DD` */
@Local pickedIso: string = ''; @Local pickedIso: string = '';
/**
* 当前时刻(当天第几分钟)—— 驱动时间轴的**当前时间红线**。
*
* 🔴 2026-11-06:原来是 `nowMin: this.nowMinutes()`(build 里调方法),
* 不建立响应依赖 ⇒ 红线停在进页面那一刻。改成 @Local + 定时器。
* 与首页 `FbHomePage.nowClockMin` 完全同一套做法。
*/
@Local nowClockMin: number = 0;
/** 定时器句柄(0 = 没在跑) */
private nowTimer: number = 0;
// ---- 浮层状态 ---- // ---- 浮层状态 ----
/** 详情:正在看的条目(列表里那份) */ /** 详情:正在看的条目(列表里那份) */
@@ -339,6 +349,8 @@ export struct FbCalendarPage {
*/ */
aboutToDisappear(): void { aboutToDisappear(): void {
FB_BACK.unregister('calendar'); FB_BACK.unregister('calendar');
/* 🔴 2026-11-06:停掉当前时间刷新(见 `startNowTicker()`)。 */
this.stopNowTicker();
} }
private backOnce(): boolean { private backOnce(): boolean {
@@ -376,6 +388,11 @@ export struct FbCalendarPage {
this.applyCardJump(); this.applyCardJump();
this.load(); this.load();
this.loadBooks(); this.loadBooks();
/* 🔴 2026-11-06:当前时间红线要自己走。
用户报的是首页,但这里**完全同一个毛病** —— `nowMin: this.nowMinutes()`
在 build 里调方法,不建立响应依赖 ⇒ 红线停在进页面那一刻。
既然修了就一起修,否则从首页切到日程页还是同一条死线。 */
this.startNowTicker();
} }
/** /**
@@ -1521,7 +1538,8 @@ export struct FbCalendarPage {
events: this.eventsOf(this.pickedIso), events: this.eventsOf(this.pickedIso),
style: this.style, style: this.style,
isToday: this.pickedIso === this.todayIso(), isToday: this.pickedIso === this.todayIso(),
nowMin: this.nowMinutes(), /* 🔴 2026-11-06:读 @Local(定时器驱动),不要调方法(不建立依赖) */
nowMin: this.nowClockMin,
onPick: (e: FbCalEvent): void => { onPick: (e: FbCalEvent): void => {
this.openEvent(e); this.openEvent(e);
} }
@@ -1534,6 +1552,28 @@ export struct FbCalendarPage {
return d.getHours() * 60 + d.getMinutes(); return d.getHours() * 60 + d.getMinutes();
} }
/**
* 启动「当前时间」刷新(与首页 `FbHomePage.startNowTicker` 同一套)。
*
* · 30 秒一跳:红线是"现在"的指示,1 分钟一跳在整分附近会显得迟一拍;
* · 先立即刷一次,避免首屏红线停在默认值 0(= 00:00,跑到视窗外面);
* · 只在页面存在期间跑,`aboutToDisappear` 里清掉。
*/
private startNowTicker(): void {
this.stopNowTicker();
this.nowClockMin = this.nowMinutes();
this.nowTimer = setInterval((): void => {
this.nowClockMin = this.nowMinutes();
}, 30 * 1000);
}
private stopNowTicker(): void {
if (this.nowTimer !== 0) {
clearInterval(this.nowTimer);
this.nowTimer = 0;
}
}
/** 周视图:7 列日期条 + 选中日日程 */ /** 周视图:7 列日期条 + 选中日日程 */
@Builder @Builder
weekBody() { weekBody() {
@@ -1661,7 +1701,7 @@ export struct FbCalendarPage {
events: g.events, events: g.events,
style: this.style, style: this.style,
isToday: g.iso === this.todayIso(), isToday: g.iso === this.todayIso(),
nowMin: this.nowMinutes(), nowMin: this.nowClockMin,
onPick: (e: FbCalEvent): void => { onPick: (e: FbCalEvent): void => {
this.openEvent(e); this.openEvent(e);
} }
+65 -1
View File
@@ -337,6 +337,27 @@ export struct FbHomePage {
@Local mvCinemas: FbCinemaItem[] = []; @Local mvCinemas: FbCinemaItem[] = [];
@Local mvLoading: boolean = false; @Local mvLoading: boolean = false;
/**
* 当前时刻(当天第几分钟)—— 驱动「今天的日程」时间轴的**当前时间红线**。
*
* 🔴 用户 2026-11-06:「首页今天的日程,那个红线,不会动态更新,只有刚打开
* 应用的时候更新,如果不清后台,就一直不动了。」
*
* 根因:原来是 `nowMin: this.nowMinutes()` —— **在 build 里直接调用方法**。
* ArkUI 不会因为"时间流逝"重算任何东西,那个方法只在首次 build(以及
* 别的状态变化引发重绘)时求值一次 ⇒ 红线永远停在打开 App 的那一刻。
*
* ⇒ 改成 `@Local` 字段 + 定时器主动改它:字段一变,`FbDayEventPanel` 的
* `@Param nowMin` 就更新,红线跟着走。这是项目里唯一能让时间自动刷新的
* 办法(`@Builder` 里调方法不建立依赖,与 `pitfalls.md` 记的那条同源)。
*
* ⚠️ 不叫 `nowMin` 而是 `nowClockMin`:`nowMin` 已经被 `nowMinutes()`
* 方法占着语义,两个同名字段/方法挨着放容易误改。
*/
@Local nowClockMin: number = 0;
/** 红线的定时器句柄(0 = 没在跑) */
private nowTimer: number = 0;
/** 「新增成员」表单开着 */ /** 「新增成员」表单开着 */
@Local pfCreating: boolean = false; @Local pfCreating: boolean = false;
/** 联系人卡要显示的人(本地库前 N 个) */ /** 联系人卡要显示的人(本地库前 N 个) */
@@ -384,10 +405,16 @@ export struct FbHomePage {
⚠️ 必须**不 await**:本地库首次打开或库很大时读一次要几百毫秒, ⚠️ 必须**不 await**:本地库首次打开或库很大时读一次要几百毫秒,
串在上面会让首页首屏白等(而这两张卡是锦上添花)。 */ 串在上面会让首页首屏白等(而这两张卡是锦上添花)。 */
this.loadSideCards(); this.loadSideCards();
/* 🔴 2026-11-06:当前时间红线要自己走 —— 见 `startNowTicker()` 的说明。
⚠️ 放在这里而不是 build 里:`build()` 可能被调用很多次,
在里面起定时器会**越叠越多**(每次都新开一个,旧的没人清)。 */
this.startNowTicker();
} }
aboutToDisappear(): void { aboutToDisappear(): void {
FB_BACK.unregister('home'); FB_BACK.unregister('home');
/* 页面销毁 ⇒ 停掉时间刷新,别让已销毁的页面继续收定时器回调(白耗电)。 */
this.stopNowTicker();
} }
/** 返回手势:先关详情卡,再交给 `Index`(返回 true = 已消费) */ /** 返回手势:先关详情卡,再交给 `Index`(返回 true = 已消费) */
@@ -1389,6 +1416,40 @@ export struct FbHomePage {
return d.getHours() * 60 + d.getMinutes(); return d.getHours() * 60 + d.getMinutes();
} }
/**
* 启动「当前时间」刷新。
*
* 🔴 用户 2026-11-06:「红线不会动态更新,只有刚打开应用的时候更新,
* 如果不清后台,就一直不动了。」
*
* 设计取舍:
* · **30 秒一跳**,不是 1 分钟:红线是"现在"的视觉指示,1 分钟一跳的话
* 用户在整分时刻附近盯着看会觉得"迟了一拍";30 秒的成本极低
* (只改一个 number 字段,且值没变时设成同值不会触发重绘)。
* · **先立即刷一次**:`aboutToAppear` 里直接调一次,避免首屏红线
* 停留在默认值 0(= 00:00,跑到视窗顶部外面去了)。
* · **只在页面存在期间跑**(`aboutToDisappear` 里清掉):
* 首页切走(`Index` 换模块)会走 `aboutToDisappear`,
* 留着定时器就是纯粹的耗电,而且会让已销毁的页面继续收状态更新。
* · **不判断"值没变就跳过"**:`@Local` 赋同值本来就不触发重绘,
* 多一句判断反而容易写错(比如把比较写成 `>=`)。
*/
private startNowTicker(): void {
this.stopNowTicker();
this.nowClockMin = this.nowMinutes();
this.nowTimer = setInterval((): void => {
this.nowClockMin = this.nowMinutes();
}, 30 * 1000);
}
/** 停掉刷新(可重复调用) */
private stopNowTicker(): void {
if (this.nowTimer !== 0) {
clearInterval(this.nowTimer);
this.nowTimer = 0;
}
}
/** /**
* 今天 → `YYYY-MM-DD`(`FbEventEditSheet` 的 `presetDate` 要这个格式)。 * 今天 → `YYYY-MM-DD`(`FbEventEditSheet` 的 `presetDate` 要这个格式)。
* *
@@ -1615,7 +1676,10 @@ export struct FbHomePage {
events: this.todayEvents, events: this.todayEvents,
style: this.calStyle, style: this.calStyle,
isToday: true, isToday: true,
nowMin: this.nowMinutes(), /* 🔴 2026-11-06:读 `@Local nowClockMin`(定时器驱动),
不要写 `this.nowMinutes()` —— 在 build 里调方法不建立依赖,
红线会停在打开 App 那一刻(用户报的就是这个)。 */
nowMin: this.nowClockMin,
showHead: false, showHead: false,
emptyTitle: '今天没有安排', emptyTitle: '今天没有安排',
emptyHint: '过得轻松一点', emptyHint: '过得轻松一点',