一、引言人是靠期待活着的。生日、纪念日、旅行、毕业、节日——这些未来事件给我们时间感让平凡的日常有了方向。倒数日这种 App 之所以流行原因在于它把抽象的时间变成了具象的数字还有 3 天比下周三更有紧迫感已过 12 天比两周前更有纪念意义。从技术角度看倒数日涉及的核心问题是日期运算——两个日期之间相差多少天、今天是哪个日期、如何判断某个事件是将来还是过去、如何按距离今天的远近排序。这些运算不依赖后端全部可以在前端用 JavaScript 的 Date API 完成。本文将用 ArkUI 从零构建一个倒数日追踪器。功能包括添加事件名称 图标 目标日期、自动计算剩余天数、四色距离标记今天/明天红色、3 天内品红、7 天内蓝色、更远绿色、已过事件灰色标记、自动按距离排序最近的排最前。配色严格避免了黄色系红/品红/蓝/绿四色方案确保所有文字与背景保持足够对比度。阅读完本文你将能够使用DateAPI 进行日期差值计算实现四色距离标记系统根据剩余天数动态着色构建按距离自动排序的列表处理已过事件的特殊显示逻辑构建图标选择器 日期输入的表单弹窗二、数据模型与日期运算2.1 事件实体interfaceCountdownEvent{id:number;name:string;icon:string;date:string;// YYYY-MM-DD}日期用YYYY-MM-DD格式存储——与日记本相同策略。8 个预设图标涵盖常见事件类型生日、纪念日、毕业、旅行✈️、搬家、节日、庆祝、自定义。2.2 核心日期函数两个函数构成日期系统的全部基础functiontodayStr():string{constdnewDate();return${d.getFullYear()}-${(d.getMonth()1).toString().padStart(2,0)}-${d.getDate().toString().padStart(2,0)};}functiondaysBetween(from:string,to:string):number{constfnewDate(from);consttnewDate(to);returnMath.floor((t.getTime()-f.getTime())/(1000*60*60*24));}todayStr()返回当天日期的字符串如2026-06-11用于构建今天的参照点。daysBetween()计算两个日期之间相差的整天数。核心公式(t.getTime() - f.getTime()) / (1000 * 60 * 60 * 24)。getTime()返回毫秒时间戳差值除以 86400000一天的毫秒数得到天数。Math.floor向下取整——因为我们只关心完整的天不关心小时和分钟。正值表示目标日期在未来“还有 N 天”负值表示目标日期已过“已过 N 天”零表示今天。2.3 动态排序事件列表按距今天的距离排序——最近的排在最前sortedEvents():CountdownEvent[]{return[...this.events].sort((a,b){constdadaysBetween(todayStr(),a.date);constdbdaysBetween(todayStr(),b.date);if(da0db0)return1;// a 已过b 将来 → a 排后if(db0da0)return-1;// b 已过a 将来 → a 排前returnda-db;// 都将来或都已过 → 按距离});}排序规则将来的事件排在已过事件前面将来事件按距离近 → 远排列今天排第一28 天后排最后已过事件按距离近 → 远排列昨天排最前1 年前的排最后用户打开页面后最紧迫的事件今天、明天自然出现在第一位不需要手动调整。三、四色距离标记3.1 颜色设计用四种颜色标记事件的距离距离颜色色值语义已过灰色#888899“这件事已经发生了”0-1 天红色#FF4D4F“就是今天/明天紧迫”2-3 天品红#EB2F96“快到了做好准备”4-7 天蓝色#1677FF“还有一周心里有数”8 天以上绿色#52C41A“还早轻松等待”四种颜色构成了一个情感梯度红色 紧迫感品红 关注蓝色 平常绿色 从容。用户不需要阅读还有 X 天的文字光看颜色就能感知每件事的紧迫程度。注意这里完全避免了黄色/金色——红色 → 品红 → 蓝色 → 绿色的渐变中没有黄色参与。品红#EB2F96在 2-3 天的位置替代了本来可能使用橙色/黄色的角色且白字对比度远优于黄色。3.2 动态文字标签除了颜色每个事件还有对应的文字标签proximityLabel(days:number):string{if(days0)return已过${Math.abs(days)}天;if(days0)return今天;if(days1)return明天;return还有${days}天;}今天“和明天是两个特殊情况——它们给人最强的紧迫感和期待感所以用特殊的文字而非简单的还有 0 天或还有 1 天”。感叹号增加了情感色彩——今天发生的事值得一个感叹号。3.3 半透明背景色每个事件卡片的图标区域也有对应的半透明背景色proximityBg(days:number):string{if(days0)return#88889915;if(days1)return#FF4D4F15;if(days3)return#EB2F9615;if(days7)return#1677FF15;return#52C41A15;}15十六进制 约 8% 不透明度。这是一个非常淡的背景色——刚好能感知到色相但不会和白色卡片背景产生冲突。如果背景色太深比如 30-50% 不透明度会和卡片文字争夺视觉注意力。四、事件管理4.1 添加事件点击右下角 FAB添加事件蓝色按钮弹出底部表单包含三个输入区图标选择器8 个 emoji 图标排列成行。选中态蓝底浅蓝边框#1677FF22 1.5vp 边框未选中态透明。与日记本的心情选择器不同这里用边框区分选中态而非不透明度——因为图标都需要完整辨认不适合降低不透明度。名称输入TextInput组件占位文字事件名称如’生日派对’。限制单行。日期输入TextInput组件占位文字日期如 2026-12-25。使用自由文本输入而非 DatePicker 的原因是DatePicker 选择未来几个月甚至几年的日期需要多次滑动不如直接输入YYYY-MM-DD快。底部两个按钮——取消灰色和确认添加蓝色输入为空时灰色禁用。4.2 删除事件每条事件卡片右侧有 × 按钮。点击直接删除——与日记本的删除逻辑相同“可见即确认”不需要额外的确认弹窗。五、UI 设计5.1 深色标题栏 白色卡片列表与记账本类似的布局架构深色标题栏#1a1a2e标题⏳ 倒数日 事件计数浅灰背景#F2F3F5白色事件卡片列表蓝色 FAB右下角悬浮5.2 事件卡片布局每张卡片水平排列四个元素图标区56×56vp 圆角方块淡色背景大号 emoji信息区flex 1事件名称加粗深色 日期浅灰小字天数标签动态文字 动态颜色红色/品红/蓝色/绿色/灰色删除按钮浅灰 ×卡片之间用 1vp 的细微间隙分隔第一张卡片顶部与列表顶部对齐borderRadius特殊处理。5.3 空状态当事件列表为空时显示空状态引导 emoji “还没有倒数事件” “点击右下角 添加一个”。空状态是用户教育的一部分——告诉用户这个页面是做什么的、怎么开始使用。六、完整代码结构CountdownPage ├── Stack根容器 │ ├── Column主界面 │ │ ├── Row标题栏⏳ 倒数日 N 个事件 │ │ └── Scroll事件列表 │ │ ├── if 空态 → 空状态引导 │ │ └── ForEach → Row事件卡片 │ │ ├── Column图标 半透明色底 │ │ ├── Column名称 日期 │ │ ├── Text还有/已过 N 天动态色 │ │ └── Text删除 × │ ├── ButtonFAB 添加事件 │ └── if showAdd → Column底部弹窗 │ ├── Row8 个图标选择 │ ├── TextInput事件名称 │ ├── TextInput日期 YYYY-MM-DD │ └── Row取消 确认添加七、总结本文从零构建了一个倒数日追踪器。与前几篇的功能型应用不同倒数日的核心是日期运算 视觉距离编码。核心要点回顾日期运算todayStr()返回当天日期字符串daysBetween()计算两个日期的整天差。全部使用原生DateAPI无需第三方库。四色距离标记红色0-1 天 紧迫品红2-3 天 关注蓝色4-7 天 平常绿色8 天 从容。已过事件灰色。颜色构成情感梯度用户不需要读文字就能感知紧迫程度。自动排序sortedEvents()将将来事件排在已过事件前面同类事件按距离升序排列。用户打开页面后最近的事件自然出现在第一位。配色安全四色方案中完全没有黄色/金色——红色 → 品红 → 蓝色 → 绿色的情感梯度中品红替代了传统方案中的橙色/黄色位置。所有颜色与白字对比度均达标。动态标签今天和明天使用特殊文字而非数字增加情感色彩和紧迫感。图标选择器用边框区分选中态而非不透明度因为所有图标都需要完整辨认。倒数日是小而美的典型——一个界面、一个列表、几个事件卡片。但日期运算、距离排序、颜色编码、情感标签这些细节组合在一起形成了一个用户愿意每天打开看的工具。
鸿蒙原生开发——从零构建倒数日追踪器
发布时间:2026/6/12 7:53:04
一、引言人是靠期待活着的。生日、纪念日、旅行、毕业、节日——这些未来事件给我们时间感让平凡的日常有了方向。倒数日这种 App 之所以流行原因在于它把抽象的时间变成了具象的数字还有 3 天比下周三更有紧迫感已过 12 天比两周前更有纪念意义。从技术角度看倒数日涉及的核心问题是日期运算——两个日期之间相差多少天、今天是哪个日期、如何判断某个事件是将来还是过去、如何按距离今天的远近排序。这些运算不依赖后端全部可以在前端用 JavaScript 的 Date API 完成。本文将用 ArkUI 从零构建一个倒数日追踪器。功能包括添加事件名称 图标 目标日期、自动计算剩余天数、四色距离标记今天/明天红色、3 天内品红、7 天内蓝色、更远绿色、已过事件灰色标记、自动按距离排序最近的排最前。配色严格避免了黄色系红/品红/蓝/绿四色方案确保所有文字与背景保持足够对比度。阅读完本文你将能够使用DateAPI 进行日期差值计算实现四色距离标记系统根据剩余天数动态着色构建按距离自动排序的列表处理已过事件的特殊显示逻辑构建图标选择器 日期输入的表单弹窗二、数据模型与日期运算2.1 事件实体interfaceCountdownEvent{id:number;name:string;icon:string;date:string;// YYYY-MM-DD}日期用YYYY-MM-DD格式存储——与日记本相同策略。8 个预设图标涵盖常见事件类型生日、纪念日、毕业、旅行✈️、搬家、节日、庆祝、自定义。2.2 核心日期函数两个函数构成日期系统的全部基础functiontodayStr():string{constdnewDate();return${d.getFullYear()}-${(d.getMonth()1).toString().padStart(2,0)}-${d.getDate().toString().padStart(2,0)};}functiondaysBetween(from:string,to:string):number{constfnewDate(from);consttnewDate(to);returnMath.floor((t.getTime()-f.getTime())/(1000*60*60*24));}todayStr()返回当天日期的字符串如2026-06-11用于构建今天的参照点。daysBetween()计算两个日期之间相差的整天数。核心公式(t.getTime() - f.getTime()) / (1000 * 60 * 60 * 24)。getTime()返回毫秒时间戳差值除以 86400000一天的毫秒数得到天数。Math.floor向下取整——因为我们只关心完整的天不关心小时和分钟。正值表示目标日期在未来“还有 N 天”负值表示目标日期已过“已过 N 天”零表示今天。2.3 动态排序事件列表按距今天的距离排序——最近的排在最前sortedEvents():CountdownEvent[]{return[...this.events].sort((a,b){constdadaysBetween(todayStr(),a.date);constdbdaysBetween(todayStr(),b.date);if(da0db0)return1;// a 已过b 将来 → a 排后if(db0da0)return-1;// b 已过a 将来 → a 排前returnda-db;// 都将来或都已过 → 按距离});}排序规则将来的事件排在已过事件前面将来事件按距离近 → 远排列今天排第一28 天后排最后已过事件按距离近 → 远排列昨天排最前1 年前的排最后用户打开页面后最紧迫的事件今天、明天自然出现在第一位不需要手动调整。三、四色距离标记3.1 颜色设计用四种颜色标记事件的距离距离颜色色值语义已过灰色#888899“这件事已经发生了”0-1 天红色#FF4D4F“就是今天/明天紧迫”2-3 天品红#EB2F96“快到了做好准备”4-7 天蓝色#1677FF“还有一周心里有数”8 天以上绿色#52C41A“还早轻松等待”四种颜色构成了一个情感梯度红色 紧迫感品红 关注蓝色 平常绿色 从容。用户不需要阅读还有 X 天的文字光看颜色就能感知每件事的紧迫程度。注意这里完全避免了黄色/金色——红色 → 品红 → 蓝色 → 绿色的渐变中没有黄色参与。品红#EB2F96在 2-3 天的位置替代了本来可能使用橙色/黄色的角色且白字对比度远优于黄色。3.2 动态文字标签除了颜色每个事件还有对应的文字标签proximityLabel(days:number):string{if(days0)return已过${Math.abs(days)}天;if(days0)return今天;if(days1)return明天;return还有${days}天;}今天“和明天是两个特殊情况——它们给人最强的紧迫感和期待感所以用特殊的文字而非简单的还有 0 天或还有 1 天”。感叹号增加了情感色彩——今天发生的事值得一个感叹号。3.3 半透明背景色每个事件卡片的图标区域也有对应的半透明背景色proximityBg(days:number):string{if(days0)return#88889915;if(days1)return#FF4D4F15;if(days3)return#EB2F9615;if(days7)return#1677FF15;return#52C41A15;}15十六进制 约 8% 不透明度。这是一个非常淡的背景色——刚好能感知到色相但不会和白色卡片背景产生冲突。如果背景色太深比如 30-50% 不透明度会和卡片文字争夺视觉注意力。四、事件管理4.1 添加事件点击右下角 FAB添加事件蓝色按钮弹出底部表单包含三个输入区图标选择器8 个 emoji 图标排列成行。选中态蓝底浅蓝边框#1677FF22 1.5vp 边框未选中态透明。与日记本的心情选择器不同这里用边框区分选中态而非不透明度——因为图标都需要完整辨认不适合降低不透明度。名称输入TextInput组件占位文字事件名称如’生日派对’。限制单行。日期输入TextInput组件占位文字日期如 2026-12-25。使用自由文本输入而非 DatePicker 的原因是DatePicker 选择未来几个月甚至几年的日期需要多次滑动不如直接输入YYYY-MM-DD快。底部两个按钮——取消灰色和确认添加蓝色输入为空时灰色禁用。4.2 删除事件每条事件卡片右侧有 × 按钮。点击直接删除——与日记本的删除逻辑相同“可见即确认”不需要额外的确认弹窗。五、UI 设计5.1 深色标题栏 白色卡片列表与记账本类似的布局架构深色标题栏#1a1a2e标题⏳ 倒数日 事件计数浅灰背景#F2F3F5白色事件卡片列表蓝色 FAB右下角悬浮5.2 事件卡片布局每张卡片水平排列四个元素图标区56×56vp 圆角方块淡色背景大号 emoji信息区flex 1事件名称加粗深色 日期浅灰小字天数标签动态文字 动态颜色红色/品红/蓝色/绿色/灰色删除按钮浅灰 ×卡片之间用 1vp 的细微间隙分隔第一张卡片顶部与列表顶部对齐borderRadius特殊处理。5.3 空状态当事件列表为空时显示空状态引导 emoji “还没有倒数事件” “点击右下角 添加一个”。空状态是用户教育的一部分——告诉用户这个页面是做什么的、怎么开始使用。六、完整代码结构CountdownPage ├── Stack根容器 │ ├── Column主界面 │ │ ├── Row标题栏⏳ 倒数日 N 个事件 │ │ └── Scroll事件列表 │ │ ├── if 空态 → 空状态引导 │ │ └── ForEach → Row事件卡片 │ │ ├── Column图标 半透明色底 │ │ ├── Column名称 日期 │ │ ├── Text还有/已过 N 天动态色 │ │ └── Text删除 × │ ├── ButtonFAB 添加事件 │ └── if showAdd → Column底部弹窗 │ ├── Row8 个图标选择 │ ├── TextInput事件名称 │ ├── TextInput日期 YYYY-MM-DD │ └── Row取消 确认添加七、总结本文从零构建了一个倒数日追踪器。与前几篇的功能型应用不同倒数日的核心是日期运算 视觉距离编码。核心要点回顾日期运算todayStr()返回当天日期字符串daysBetween()计算两个日期的整天差。全部使用原生DateAPI无需第三方库。四色距离标记红色0-1 天 紧迫品红2-3 天 关注蓝色4-7 天 平常绿色8 天 从容。已过事件灰色。颜色构成情感梯度用户不需要读文字就能感知紧迫程度。自动排序sortedEvents()将将来事件排在已过事件前面同类事件按距离升序排列。用户打开页面后最近的事件自然出现在第一位。配色安全四色方案中完全没有黄色/金色——红色 → 品红 → 蓝色 → 绿色的情感梯度中品红替代了传统方案中的橙色/黄色位置。所有颜色与白字对比度均达标。动态标签今天和明天使用特殊文字而非数字增加情感色彩和紧迫感。图标选择器用边框区分选中态而非不透明度因为所有图标都需要完整辨认。倒数日是小而美的典型——一个界面、一个列表、几个事件卡片。但日期运算、距离排序、颜色编码、情感标签这些细节组合在一起形成了一个用户愿意每天打开看的工具。