1. 先把需求看懂,再动手写HTML结构
做游戏社区或游戏平台运营页的时候,经常遇到这样的需求:某个“等级加速活动”或者“双倍经验”活动要上线了,运营要求在活动页醒目位置展示“双倍经验剩余2天”这样的倒计时,而且这个“剩余2天”不是写死的文字,要能跟着真实时间走,最好是“剩余2天 13:25:46”这种带时分秒的样式。
很多刚接触前端的同学一看到“HTML怎么显示等级加速活动倒计时”这种标题,就以为要用很复杂的框架,或者以为必须搭后端接口。其实不是。纯HTML加一点CSS、一段原生JavaScript就能完成,而且放在任何一个静态页面里都能跑,不需要引入jQuery,更不需要上Vue、React。这类需求我用原生三件套做过很多次,稳定、轻量、好维护,上线后基本不用管。
先理清这个需求到底在做什么。活动的结束时间通常是运营在后台配置好的一个固定时间点,比如“2025-06-01 23:59:59”。页面要做的事情,就是把这个固定时间点与用户当前时刻做差值计算,然后换算成“天、小时、分钟、秒”,渲染到页面上。只要这个差值大于0,就表示活动还在进行中,页面持续刷新展示;差值一旦小于等于0,说明活动已经结束,这个时候要展示“活动已结束”之类的文案,不能再硬显示“剩余0天 00:00:00”让玩家困惑。
再往深处想一层,这个需求真正要解决的问题不是“显示文字”,而是“让显示跟着时间走”。很多人写倒计时容易犯一个错,用setInterval每秒去“减1”,比如拿当前展示的秒数减一秒再显示。这样在页面一直开启的情况下勉强能看,但只要用户把页面切到后台,浏览器会节流定时器,回来的时候经常发现倒计时慢了半拍。正确做法是每次执行都重新拿“当前真实时间”去减“目标时间”,用两者差值去渲染,而不是基于上一次的结果做递减。这个思路会在后面代码里反复体现。
确定技术方案之后,还要考虑一个容易被忽略的点:页面要不要对接后端接口拿活动状态、剩余时间是否要跟服务器时间对齐。如果你的页面是纯静态页,且不要求特别精确,直接对比用户本地时间就够了,实现成本最低。但如果活动是面向全网玩家统一开服的,或者奖励发放严格按服务器时间为准,那本地时间会有一个隐患:用户改了系统时间,倒计时就会出错。这种场景下最稳的办法是让后端在下发活动配置时顺带带上服务器当前时间戳,页面计算一个“本地时钟与服务器时钟的偏移量”,之后每次都拿这个偏移量校准。这个我在后面第4章会专门展开讲,因为它是倒计时页面最常踩的坑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心逻辑:从“剩余2天”到实时运行的前端倒计时
2.1 时间计算的基本公式
不管需求文案写成“双倍经验剩余2天”还是“等级加速活动即将结束”,底层逻辑都逃不开一个公式:
剩余时间 = 活动结束时间戳 - 当前时间戳
有了这个毫秒级差值,就可以做格式化。常见的做法是先除以1000得到秒数,然后按单位逐级取整。网上能找到很多代码,但我见过不少版本把简单事情写复杂了,还容易出错。我用的是最直白的一组算式:
javascript复制const diff = Math.floor((endTime - Date.now()) / 1000);
const days = Math.floor(diff / 86400);
const hours = Math.floor((diff % 86400) / 3600);
const minutes = Math.floor((diff % 3600) / 60);
const seconds = diff % 60;
解释一下为什么这样拆。一天是86400秒,先把总秒数除以86400取整数部分,得到“天”。剩下的余数里包含的就是不足一天的秒数,再除以3600取整,得到“小时”。继续拿余数除以60取整得到“分钟”,最后剩下的余数就是“秒”。整个过程就是一层一层剥离单位,思路跟人民币面额拆分一个道理,先拿百元,再拿五十元,再拿十元,剩下的才是零钱。
这里有一个细节要注意,diff在计算之前必须保证是正数。如果用户是在活动结束后打开页面,diff会变成负数,这会导致Math.floor把负数向下取整,比如-1秒会被取成-1天,或者显示成负数、NaN之类奇怪的东西。所以每次计算之前先判断一下diff是否大于0,如果小于等于0就直接走“活动已结束”的分支,别再继续往下格式化时间了。
2.2 为什么用setInterval而不是setTimeout
倒计时需要每秒刷新一次页面上的数字,浏览器里做循环定时更新,无非两种选择:setInterval定时循环,或者setTimeout递归调用。两种都能实现,但我建议在倒计时场景里优先用setInterval,因为它在语义上更直接,你要做的就是“每隔1000毫秒重新渲染一次”,而且如果页面没有其他复杂的并发定时逻辑,setInterval不会有问题。
唯一要注意的是setInterval的关闭。永远不要创建一个永远不清理的定时器。倒计时一旦走到结束状态,记得clearInterval,否则定时器一直在后台空跑,既浪费性能又可能在页面逻辑变复杂后引发奇怪的bug。标准模板是:
javascript复制const timer = setInterval(updateCountdown, 1000);
然后在updateCountdown内部判断活动已经结束时调用“clearInterval(timer)”。这个“清理”动作很容易被忘掉,尤其是初学的时候,写完setInterval就不管了,页面一切正常也看不出毛病,直到某天同一个页面上还要跑轮播、请求数据、做其他交互,才发现定时器之间互相影响。
2.3 为什么不直接显示“剩余2天”这几个字
从运营文案角度看,“双倍经验剩余2天”确实是一句很常见的页面提示。但如果你直接把“2天”写死在HTML里,活动到期之后运营就得手动改页面重新上线,一天一次都算轻的。更常见的情况是活动持续一周,第五天要显示“剩余3天”,第六天要显示“剩余2天”,每天都让人手工改?那肯定不现实。
所以页面里真正要变的是那串数字,文案外壳则可以保留。
