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

倒计时翻转动画异常:每3-4秒重复错乱问题求助

倒计时翻转动画重复错乱问题解决

问题根源

  1. 定时器每250ms就调用一次flipAll,即使倒计时数值未变化也会触发检查,且未判断卡片是否处于动画状态,导致动画叠加错乱。
  2. CSS中存在语法错误:.card \>div的反斜杠多余,导致卡片子元素的圆角样式不生效。
  3. 未利用previousTimeBetweenDates判断倒计时数值是否变化,造成不必要的函数调用。

修复后的代码

JavaScript

let countDownDate = new Date().setHours(new Date().getHours() + 24);
let previousTimeBetweenDates;

setInterval(() => {
  let now = new Date();
  let distance = Math.ceil((countDownDate - now) / 1000);
  
  // 仅当数值变化时才执行翻转动画
  if (distance !== previousTimeBetweenDates) {
    flipAll(distance);
    previousTimeBetweenDates = distance;
  }
}, 250);

function flip(card, nextNum) {
  let topHalf = card.querySelector('.top');
  let cardnum = parseInt(topHalf.textContent);
  
  // 如果数值未变化或卡片正在动画,直接返回
  if (nextNum === cardnum || card.classList.contains('animating')) return;
  
  // 添加动画状态标记
  card.classList.add('animating');

  let flipTop = document.createElement('div');
  flipTop.classList.add('flip-top');

  let flipBottom = document.createElement('div');
  flipBottom.classList.add('flip-bottom');

  let bottomHalf = card.querySelector('.bottom');

  bottomHalf.textContent = cardnum;
  flipBottom.textContent = cardnum;
  flipTop.textContent = nextNum;
  topHalf.textContent = cardnum;

  flipTop.addEventListener('animationstart', e => {
    topHalf.textContent = nextNum;
  });
  
  // 动画结束后移除元素并清除状态标记
  flipTop.addEventListener('animationend', e => {
    flipTop.remove();
    card.classList.remove('animating');
  });
  
  flipBottom.addEventListener('animationend', e => {
    bottomHalf.textContent = nextNum;
    flipBottom.remove();
  });

  card.append(flipTop, flipBottom);
}

function flipAll(date) {
  let seconds = date % 60;
  let minutes = Math.floor(date / 60) % 60;
  let hours = Math.floor(date / 3600);
  
  flip(document.querySelector('.hours-tens'), Math.floor(hours / 10));
  flip(document.querySelector('.hours-units'), hours % 10);
  flip(document.querySelector('.minutes-tens'), Math.floor(minutes / 10));
  flip(document.querySelector('.minutes-units'), minutes % 10);
  flip(document.querySelector('.seconds-tens'), Math.floor(seconds / 10));
  flip(document.querySelector('.seconds-units'), seconds % 10);
}

CSS

*,
*::before,
*::after {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
  font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
}

body {
  background-color: #f2f1ed;
  display: grid;
  place-items: center;
  height: 100vh;
}

.card {
  position: relative;
  width: 0.5em;
  display: flex;
  flex-direction: column;
  font-size: 8rem;
  color: #de4848;
  font-weight: 600;
  box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.2);
  border-collapse: separate;
  border-radius: .1em;
}

/* 修复语法错误:移除多余的反斜杠 */
.card > div {
  border-radius: .1em;
}

.part,
.card .flip-top,
.card .flip-bottom {
  height: .75em;
  padding: .25em;
  line-height: 1;
  display: flex;
  justify-content: center;
}

.top,
.card .flip-bottom {
  background-color: #f7f7f7;
  border-bottom: 1px solid rgb(201, 201, 201);
}

.card .flip-bottom {
  position: absolute;
  width: 100%;
  height: 50%;
  background: #f7f7f7;
  overflow: hidden;
  text-align: center;
  padding-top: .25em;
  line-height: 1;
  animation: flip-down 250ms ease-in;
  transform-origin: bottom;
}

.card .flip-top {
  position: absolute;
  width: 100%;
  bottom: 0;
  animation: flip-up 250ms ease-out 250ms;
  transform-origin: top;
  transform: rotateX(90deg);
}

@keyframes flip-down {
  100% {
    transform: rotateX(90deg);
  }
}

@keyframes flip-up {
  100% {
    transform: rotateX(0deg);
  }
}

.bottom,
.card .flip-top {
  display: flex;
  align-items: flex-end;
  background-color: white;
}

.big-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  place-items: center;
  gap: 3em;
}

.container {
  display: flex;
  flex-direction: column;
  gap: 1em;
}

.title {
  text-align: center;
  font-weight: 400;
}

.inner-container {
  display: flex;
  gap: 1em;
}

HTML

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Countdown Timer</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div class="big-container">
    <div class="hours-container container">
      <div class="inner-container">
        <div class="hours card flip hours-tens">
          <div class="top part">2</div>
          <div class="bottom part">2</div>
        </div>
        <div class="hours card flip hours-units">
          <div class="top part">4</div>
          <div class="bottom part">4</div>
        </div>
      </div>
      <h1 class="hours-title title">Hours</h1>
    </div>
    <div class="minutes-container container">
      <div class="inner-container">
        <div class="minutes card flip minutes-tens">
          <div class="top part">0</div>
          <div class="bottom part">0</div>
        </div>
        <div class="minutes card flip minutes-units">
          <div class="top part">0</div>
          <div class="bottom part">0</div>
        </div>
      </div>
      <h1 class="minutes-title title">Minutes</h1>
    </div>
    <div class="seconds-container container">
      <div class="inner-container">
        <div class="seconds card flip seconds-tens">
          <div class="top part">0</div>
          <div class="bottom part">0</div>
        </div>
        <div class="seconds card flip seconds-units">
          <div class="top part">0</div>
          <div class="bottom part">0</div>
        </div>
      </div>
      <h1 class="seconds-title title">Seconds</h1>
    </div>
  </div>
  <script src="script.js"></script>
</body>

</html>

修复要点

  • 新增卡片动画状态标记animating类,确保同一卡片在动画未完成时不会被重复触发。
  • 仅当倒计时数值distance发生变化时才调用flipAll,避免无效调用。
  • 修复CSS中的语法错误,保证卡片子元素的圆角样式正常生效。
  • 动画结束后及时移除临时元素并清除状态标记,避免DOM堆积。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 06:54:56