从需求到上线:我用 AI 辅助开发了一个在线倒计时器

最近在处理一个项目排期的时候,发现一个很实际的问题:团队里经常需要知道"距离某个截止日期还有多少天",但每次都要打开日历数日子,或者用命令行算时间差,实在不够直观。

问题背景

作为独立开发者,我经常需要跟踪各种截止日期------项目交付、域名续费、服务器证书到期等等。市面上确实有不少倒计时工具,但大多存在几个问题:

  1. 要么功能太复杂,打开页面就被各种设置项淹没
  2. 要么需要安装应用,杀鸡用牛刀
  3. 有的还带广告,体验很糟糕

我就是想要一个打开即用、设置目标时间就能看剩余时间的轻量工具。而且作为开发者,我希望能完全掌控代码,方便后续按需定制。

技术选型思考

一开始考虑过几个方案:

  • 用现成的 npm 包 :比如 countdown 库,但为了一个倒计时引入依赖,感觉有点重
  • 写个命令行脚本:能用,但不方便给团队其他人用
  • 做个网页工具:跨平台、无需安装、分享方便,但需要处理前端的一堆细节

最后决定做个单文件 HTML,纯 vanilla JS 实现。理由很简单:不需要构建工具,不需要服务器,双击就能打开,这符合"工具就该轻量"的原则。

AI 辅助开发过程

说实话,一开始我连 datetime-local 输入框的兼容性问题都懒得查,直接打开了 Claude 开始对话。

第一轮:需求描述

我给的 prompt 大概是这样的:

帮我写一个单文件 HTML 倒计时器,支持设置目标日期和时间、自定义事件名称、实时显示剩余天时分秒、每秒刷新,支持深色模式,中英文切换。

AI 很快就生成了一版,整体结构不错,但我发现几个问题:

问题 1:时间计算有 bug

AI 第一次用的是 Date.now()new Date(targetStr) 直接相减,这在跨时区时会出问题。比如用户在东八区设置了一个 UTC 时间,显示的剩余时间会差 8 小时。

javascript 复制代码
// AI 第一次的写法(有 bug)
var ms = new Date(targetStr) - new Date();

我的修正方案 :用 datetime-local 输入的值是本地时间字符串,直接 new Date(targetStr) 解析即可,但要注意 new Date('2024-01-01T00:00') 在不同浏览器解析行为可能不同,最好手动解析:

javascript 复制代码
function parseLocalDateTime(str) {
  var parts = str.split('T');
  var dateParts = parts[0].split('-');
  var timeParts = parts[1].split(':');
  return new Date(
    +dateParts[0], +dateParts[1] - 1, +dateParts[2],
    +timeParts[0], +timeParts[1]
  );
}

问题 2:i18n 实现太笨重

AI 第一次的国际化方案是每个元素都写 if (lang === 'zh') ... else ...,代码重复不说,还容易漏。我让它改成 key-value 映射 + t() 函数的方式,清爽多了。

问题 3:进度条逻辑没考虑边界

AI 写的进度条在"起始日期晚于目标日期"时会算出负百分比,需要 clamp 到 0-100。

第二轮:交互细节打磨

我发现 AI 生成的版本有几个交互问题:

  1. 页面标题不会更新 :我让它把倒计时同步到 document.title,这样浏览器标签页上就能直接看到剩余时间
  2. 快捷预设按钮是写死的 :比如"新年"预设,如果写死 2025-01-01,明年就失效了。我让 AI 改成动态计算:
javascript 复制代码
// 动态计算下一个新年
function getNextNewYear() {
  var now = new Date();
  return new Date(now.getFullYear() + 1, 0, 1); // 明年1月1日
}
  1. 到期后的表现:我要求到期后切换为正计时(显示已过去多久),并变红提示。AI 一开始只是显示"已过期",不够直观。

第三轮:视觉和细节

设计上我提了几个要求:

  • 数字用等宽字体(monospace),避免每秒跳动时宽度变化导致布局抖动
  • 数字切换时有简单的动画效果(用 CSS transition
  • 深色模式下所有颜色都要重新适配

AI 在这轮表现不错,CSS 变量切换深色模式的方式很干净:

css 复制代码
:root {
  --bg: #fff;
  --text: #111827;
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a2e;
    --text: #e2e8f0;
  }
}

核心实现细节

时间差计算

这是倒计时的核心,要注意:

  • 不能用 Math.abs() 直接取绝对值,因为天、时、分、秒需要分别计算
  • Math.floor() 而不是 Math.round(),否则会出现"59秒显示为60秒"的问题
javascript 复制代码
function calcDiff(from, to) {
  var ms = to - from;
  var isPast = ms < 0;
  ms = Math.abs(ms);
  var d = Math.floor(ms / 86400000);
  ms -= d * 86400000;
  var h = Math.floor(ms / 3600000);
  ms -= h * 3600000;
  var m = Math.floor(ms / 60000);
  ms -= m * 60000;
  var s = Math.floor(ms / 1000);
  return { d, h, m, s, isPast };
}

每秒刷新

setInterval(tick, 1000),但要注意:如果用户切换到其他标签页,浏览器会节流 setInterval,导致时间不准。不过对于倒计时场景,1秒的误差完全可以接受,不需要用 requestAnimationFrame 或 Web Worker 来保证精度。

快捷预设

这里有个细节:周末的判定。如果今天是周六,那"本周末"应该是明天(周日),而不是今天。我用了个小技巧:

javascript 复制代码
var daysUntilSat = (6 - day + 7) % 7;
if (daysUntilSat === 0) daysUntilSat = 7; // 如果是周六,跳到下周六

踩坑记录

  1. datetime-local 的兼容性:Safari 15 以下不支持,需要 fallback。不过我做了渐进增强------不支持的浏览器会降级为文本输入框,用户手动输入日期格式。

  2. XSS 风险 :事件名称是用户输入,直接插到 innerHTML 里会有 XSS 风险。必须用 escapeHtml() 转义:

javascript 复制代码
function escapeHtml(s) {
  var d = document.createElement('div');
  d.textContent = s;
  return d.innerHTML;
}
  1. 移动端布局 :4 个数字盒子在 320px 宽度下会挤在一起。用 flex-wrapmin-width 解决,同时在小屏下减小字号。

对 AI 辅助编程的感受

这次开发过程最大的感受是:AI 是很好的结对编程伙伴,但你不能完全放手

  • AI 擅长:快速搭框架、写重复性代码、查 API 用法
  • AI 不擅长:理解业务边界条件、处理浏览器兼容性、做交互细节的取舍

比如 AI 第一次生成的代码完全没有考虑"用户没设置日期就点击开始"的情况,是我在测试时发现的。这种边界情况,需要开发者自己把关。

另外,给 AI 的 prompt 越具体,输出质量越高。比如"进度条需要 clamp 到 0-100"、"到期后切换为正计时并显示红色",比"加个进度条"要好得多。

适用场景

这个工具适合:

  • 团队项目截止日期追踪
  • 个人目标倒计时(考试、旅行、健身计划)
  • 产品上线倒计时(配合全屏模式投屏)
  • 日常提醒(比如服务器证书到期)

如果你也有类似需求,可以直接在线使用:倒计时器 - 在线日期倒计时计算工具

代码已经完整放在页面上,纯前端实现,所有计算都在浏览器本地完成,不会上传任何数据。你也可以直接右键查看源码,或者 F12 拷贝下来自己改。


相关推荐
Damai18 分钟前
Next.js 切换动态路由后,为什么页面还保留着上一个分类的数据?
前端·react.js
程序员鱼皮18 分钟前
爆火的《牛来》模型正式发布!DeepSeek 的排名又下降了。。
前端·后端·ai编程
MocMoc24 分钟前
从零落地全栈 Monorepo:Turbo + npm workspaces 实战(附一个记账小应用)
前端
灯澜忆梦26 分钟前
【基于GO的Web开发15】gin路由和路由组
前端·后端·golang·gin
Southern Wind31 分钟前
从一段文字到一部动态漫:如何打造全流程分镜创作工作台 StoryCanvas AI
前端·javascript·vue.js·人工智能
烂蜻蜓38 分钟前
Flask入门教程(七):请求与响应——Web应用的核心数据流
前端·python·flask
爱勇宝1 小时前
从前端到全栈,我最后学会的是:先做产品
前端·后端
console.log('npc')1 小时前
Grill-Me 技能使用教程
前端·大模型·产品·skill·需求