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

循环文本缩放淡入淡出动画异常:首次循环后文本突兀显示

问题描述

我想要实现三个短语依次缩放淡入、淡出的无限循环动画,但首次循环完成后,文本会以100%不透明度突兀显示数秒,动画结束后突然消失;仅播放一次动画时无此异常。

原始代码

HTML

<div class="anim-box">
  <h4 class="loop-phrase" id="move">Move Better</h4>
  <h4 class="loop-phrase" id="heal">Heal Faster</h4>
  <h4 class="loop-phrase" id="live">Live Longer</h4>
</div>

CSS

.anim-box {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 200px;
}

.loop-phrase {
  font-size: 24px;
  opacity: 0;
  position: absolute;
  color: #fff;
}

#move {
  animation: move 2s linear;
}

#heal {
  animation: heal 2s linear;
  animation-delay: 2s;
}

#live {
  animation: live 2s linear;
  animation-delay: 4s;
}

@keyframes move {
  0% {
    transform: scale(1);
    opacity: 0;
  }
  50% {
    transform: scale(2);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 0;
  }
}

@keyframes heal {
  0% {
    transform: scale(1);
    opacity: 0;
  }
  50% {
    transform: scale(2);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 0;
  }
}

@keyframes live {
  0% {
    transform: scale(1);
    opacity: 0;
  }
  50% {
    transform: scale(2);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 0;
  }
}

JavaScript

function animateText() {
  const moveText = document.getElementById('move');
  const healText = document.getElementById('heal');
  const liveText = document.getElementById('live');

  let counter = 0;

  setInterval(() => {
    if (counter === 0) {
      moveText.style.opacity = 0;
      setTimeout(() => {
        moveText.style.opacity = 1;
        moveText.style.animation = 'none';
        void moveText.offsetWidth;
        moveText.style.animation = 'move 2s linear';
        counter++;
      }, 1000);
    } else if (counter === 1) {
      healText.style.opacity = 0;
      setTimeout(() => {
        healText.style.opacity = 1;
        healText.style.animation = 'none';
        void healText.offsetWidth;
        healText.style.animation = 'heal 2s linear';
        counter++;
      }, 1000);
    } else if (counter === 2) {
      liveText.style.opacity = 0;
      setTimeout(() => {
        liveText.style.opacity = 1;
        liveText.style.animation = 'none';
        void liveText.offsetWidth;
        liveText.style.animation = 'live 2s linear';
        counter = 0;
      }, 1000);
    }
  }, 3000);
}

window.onload = function() {
  animateText();
};

问题分析
  1. 手动opacity冲突:JS中手动设置opacity:1会强制覆盖动画结束后的opacity:0状态,导致文本在间隔期突兀显示
  2. 定时器时间错位:setInterval(3000)和内部setTimeout(1000)的时间组合,与动画时长(2秒)不匹配,导致循环衔接混乱

解决方案

推荐两种修复方式,优先选择纯CSS方案,更简洁稳定:

方案1:纯CSS实现无限循环(无JS)

直接通过CSS动画周期和延迟实现无缝循环,完全避免JS的时间控制问题:

.anim-box {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 200px;
}

.loop-phrase {
  font-size: 24px;
  opacity: 0;
  position: absolute;
  color: #fff;
  /* 总周期6秒,对应3个元素各2秒动画 */
  animation: scaleFadeLoop 6s linear infinite;
}

#move {
  animation-delay: 0s;
}

#heal {
  animation-delay: 2s;
}

#live {
  animation-delay: 4s;
}

@keyframes scaleFadeLoop {
  /* 0%、33.33%、100%状态保持透明,对应动画间隔 */
  0%, 33.33%, 100% {
    transform: scale(1);
    opacity: 0;
  }
  /* 16.66%为动画峰值,对应单个元素的淡入缩放顶点 */
  16.66% {
    transform: scale(2);
    opacity: 1;
  }
}

方案2:优化JS逻辑(保留JS控制)

去掉手动opacity设置,让动画完全控制元素可见性,同时调整定时器时间匹配动画时长:

/* 先合并重复动画,减少冗余 */
.anim-box {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 200px;
}

.loop-phrase {
  font-size: 24px;
  opacity: 0;
  position: absolute;
  color: #fff;
}

#move {
  animation: scaleFade 2s linear;
}

#heal {
  animation: scaleFade 2s linear;
  animation-delay: 2s;
}

#live {
  animation: scaleFade 2s linear;
  animation-delay: 4s;
}

@keyframes scaleFade {
  0% {
    transform: scale(1);
    opacity: 0;
  }
  50% {
    transform: scale(2);
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 0;
  }
}
function animateText() {
  const phrases = [
    document.getElementById('move'),
    document.getElementById('heal'),
    document.getElementById('live')
  ];
  let currentIndex = 0;

  // 等待首次6秒初始动画完成后启动循环
  setTimeout(() => {
    // 每2秒触发下一个元素的动画重置,匹配动画时长
    setInterval(() => {
      const phrase = phrases[currentIndex];
      // 重置动画状态
      phrase.style.animation = 'none';
      void phrase.offsetWidth; // 触发重绘,重置动画
      phrase.style.animation = 'scaleFade 2s linear';

      currentIndex = (currentIndex + 1) % phrases.length;
    }, 2000);
  }, 6000);
}

window.onload = animateText;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 18:12:49