设了个目标日期总忍不住看还有几天:做个倒计时小工具

举报
茉莉风铃 发表于 2026/09/17 10:20:59 2026/09/17
【摘要】 纯前端倒计时工具,输入目标日期自动算剩余天/时/分/秒,目标存本地刷新不丢,重点讲 Date 差值计算和后台标签页的计时漂移问题。

之前报名了个考试,心里老惦记还有多少天。每天打开手机日历数一遍,数完就忘,第二天又数。后来我干脆写了个倒计时网页:填个目标日期,它自己算还剩多少天多少小时,挂在浏览器新标签页上,想看一眼就一眼。

这个小工具没后端、没接口,单页 HTML 加一点 JS 就够。真正要搞清楚的就一件事:怎么算"还剩多久",以及怎么别让它在后台偷偷算错。

一、核心算法:用差值,别用累加

倒计时的本质,是"目标时间减去现在时间"。最直觉的写法是用一个计数器每秒减一,但那是错的——页面切到后台,浏览器的定时器会被节流,每秒减一变成了每几秒减一,回来就发现时间对不上了。

正确做法永远是:每次刷新都拿当前时间去减目标时间,差值本身就是真相。

function tick() {
  const target = new Date(localStorage.getItem('targetDate')).getTime();
  const diff = target - Date.now();        // 毫秒差值,永远准确
  if (diff <= 0) {
    render(0, 0, 0, 0, true);
    return;
  }
  const s = Math.floor(diff / 1000);
  const days = Math.floor(s / 86400);
  const hours = Math.floor((s % 86400) / 3600);
  const mins = Math.floor((s % 3600) / 60);
  const secs = s % 60;
  render(days, hours, mins, secs, false);
}
setInterval(tick, 1000);

Date.now() 取的是系统当前时间戳,跟页面有没有被节流没关系。所以哪怕后台跑了半小时,切回来那一瞬间算出来的差值也是准的。setInterval 在这里只是负责"多久看一眼",不参与计算。

二、目标日期存本地,刷新不丢

填一次目标日期,刷新页面就没了,体验很差。用 localStorage 存起来,启动时读回来。

const input = document.getElementById('target');
input.addEventListener('change', () => {
  // 存成 ISO 字符串,读的时候直接 new Date() 就能还原
  localStorage.setItem('targetDate', new Date(input.value).toISOString());
  tick();
});
// 启动时恢复
const saved = localStorage.getItem('targetDate');
if (saved) input.value = saved.slice(0, 16);

注意 input[type=datetime-local] 的值是 "2026-10-01T09:00" 这种格式,new Date() 能直接解析。存的时候转成 ISO 字符串,读的时候 slice(0,16) 截回输入框要的格式。

三、几个容易踩的坑

坑 现象 处理
用累加计数 后台回来时间变慢 改用 Date.now() 差值
没处理已过期 显示一堆负数 diff<=0 直接归零并提示"已到"
时区没注意 差了 8 小时 存之前用本地时间,读也用本地
刷新后目标丢 每次重填 存 localStorage

时区这点特别容易中招:如果你存的是 UTC 时间、显示时又按本地算,就会差 8 小时。本工具全程用本地时间(datetime-local 和 Date.now() 都是本地),不存在这个问题。

四、想更省心可以加的地方

想要数字跳动的动画、想要到期弹个通知,都是叠加层,不是核心。核心就前面那 30 行:差值计算 + 每秒刷新 + 本地持久化。

小结

倒计时工具的核心不是"计时",是"算差值"——永远拿目标时间减当前时间,别用累加计数器,否则后台一节流就失真。目标日期存 localStorage 保证刷新不丢,记得处理"已过期"和时区两个边界。整个工具一百来行,复制过去加点样式就能用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。