You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免非等宽字体倒计时器跳动移位(HTML/CSS/JS实现)

解决非等宽字体倒计时数字跳动问题

核心思路是给每个倒计时数字单独分配固定宽度的容器,让数字在容器内居中显示,这样不管数字本身宽度如何变化,容器的占位空间始终一致,彻底避免切换时的移位跳动。

步骤1:重构HTML结构

把原来的单个文本容器拆分为独立的数字容器和冒号,确保每个数字都有专属的固定空间:

<link href="https://fonts.googleapis.com/css2?family=Karla:ital,wght@0,200..800;1,200..800&display=swap" rel="stylesheet">

<div id="timer-container">
  <div id="time-left">
    <span class="digit">2</span>
    <span class="digit">5</span>
    <span class="colon">:</span>
    <span class="digit">0</span>
    <span class="digit">0</span>
  </div>
</div>

步骤2:添加CSS固定宽度样式

给每个数字容器设置固定宽度,同时让数字在容器内水平/垂直居中,冒号保持原有样式即可:

#timer-container {
  position: relative;
  margin-top: 0.5rem;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: transparent;
  cursor: pointer;
  border: 2px solid transparent;
  border-radius: 50px;
  transition: border-color 0.2s ease-in-out;
  overflow: hidden;
  max-height: 180px;
  padding: 0px 20px;
}

#timer-container:hover {
  border-color: black;
}

#time-left {
  font-family: "Karla", sans-serif;
  font-size: 10.375rem;
  font-weight: 600;
  margin-bottom: 1rem;
  user-select: none;
  position: relative;
  z-index: 1;
  opacity: 1;
  /* 用flex让数字和冒号整齐排列 */
  display: flex;
  gap: 0.2rem; /* 调整数字和冒号的间距 */
}

.digit {
  /* 设置固定宽度,根据字体大小调整,这里适配10.375rem的字号 */
  width: 8rem;
  display: inline-block;
  text-align: center;
  /* 确保数字垂直居中 */
  line-height: 1;
}

.colon {
  /* 冒号不需要固定宽度,保持原有紧凑感 */
  letter-spacing: -5px;
}

步骤3:修改JavaScript更新逻辑

不再直接修改整个文本内容,而是拆分分钟和秒的每一位数字,逐个更新对应的容器:

let timeLeft = 25 * 60; // seconds
let timerInterval;

// 获取所有数字容器
const digitElements = document.querySelectorAll('.digit');

// 启动计时器
function startTimer() {
  timerInterval = setInterval(() => {
    timeLeft--;
    updateTimeLeftDigits();
    if (timeLeft === 0) {
      clearInterval(timerInterval);
    }
  }, 1000);
}

// 逐个更新数字容器内容
function updateTimeLeftDigits() {
  const minutes = Math.floor(timeLeft / 60);
  const seconds = timeLeft % 60;
  // 把分钟和秒转为两位字符串,拆成单个字符数组
  const timeStr = `${minutes.toString().padStart(2, '0')}${seconds.toString().padStart(2, '0')}`;
  // 遍历更新每个数字容器
  digitElements.forEach((el, index) => {
    el.textContent = timeStr[index];
  });
}

startTimer()

关键说明

  1. 固定宽度容器:.digit的width值需要根据你的字体大小调整,确保能容纳Karla字体中最宽的数字(比如"8"),避免数字溢出。
  2. Flex布局:#time-left用flex布局可以让数字和冒号整齐排列,gap属性控制间距,比原来的letter-spacing更可控。
  3. 数字拆分更新:通过把时间转为字符串并拆分,精准控制每个数字容器的内容,保证切换时只有容器内的数字变化,容器本身位置完全不动。

之前尝试的ch单位失效是因为非等宽字体中,1ch等于字体中"0"的宽度,但其他数字(比如"1"、"8")的宽度和"0"不一致,所以无法保证所有数字占相同空间,固定宽度的容器是更可靠的方案。

内容的提问来源于stack exchange,提问作者sameetpatil5

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:05:22