Метод setInterval(), создающий странное перекрытие частей текста

При моей попытке построить простую текстовую карусель, я столкнулся с проблемой, которую трудно понять.
Макет простой. У меня есть оболочка и текст, который я хочу, чтобы карусель перебирала.
Однако проблема, с которой я столкнулся, насколько я могу судить, исходит от метода setInterval.
После того, как анимация проходит по всему тексту и возвращается к началу, появляется странное перекрытие между первым и вторым отображаемым текстом.

Первый текст будет анимирован, но затем вернется, чтобы временно заменить второй текст.

let animateSlide = setInterval(moveSlide, 1200);

function moveSlide() {
  let carousel = document.getElementById("wordCarousel");
  let firstSlide = carousel.children[0];
  let createID = document.createAttribute("id");

  createID.value = "active";

  firstSlide.setAttributeNode(createID);

  carousel.appendChild(carousel.firstChild);

  carousel.children[carousel.children.length - 1].removeAttribute("id");
}
/* Carousel Styles */

#wordCarousel {
  height: 36px;
  overflow: hidden;
}

  .carouselSlide {
    color: #555;
    font-size: 36px;
  }

#active {
  margin-top: 0px;
  animation-name: example;
  animation-duration: 1.2s;
  animation-timing-function: ease;
}

@keyframes example {
  from {
    margin-top: 0px;
  }
  to {
    margin-top: -40px;
  }
}
<div id="wordCarousel">
  <div class="carouselSlide">
    Item 1
  </div>
  <div class="carouselSlide">
    Item 2
  </div>
  <div class="carouselSlide">
    Item 3
  </div>
  <div class="carouselSlide">
    Item 4
  </div>

</div>

Любая помощь в понимании того, что вызывает эту ошибку, будет принята с благодарностью.

Свободный перевод вопроса setInterval() Method creating strange overlap от участника @Just Trying to Learn JS.


Ответы (1 шт):

Автор решения: Alexandr_TT

Не полагайтесь на setInterval при работе с CSS-анимацией. У вас никогда не будет идеальной синхронизации. Лучше учитывать такие события, как animationiteration / animationend / animationstart

Вот лучшая идея: меньше кода будет легче обрабатывать.

let carousel = document.querySelector('#wordCarousel div');

carousel.addEventListener('animationiteration', () => {
  carousel.appendChild(carousel.children[0]);
});
#wordCarousel {
  height: 36px;
  overflow: hidden;
}

.carouselSlide {
  color: #555;
  font-size: 36px;
  line-height:100%; /* line-height гарантирует, что высота равна 36px, одного font-size недостаточно  */
}

#wordCarousel > div {
  height:100%;
  animation: example 1s infinite;
}

@keyframes example {
  to {
    transform: translateY(-100%);
  }
}
<div id="wordCarousel">
  <div>
    <div class="carouselSlide">
      Item 1
    </div>
    <div class="carouselSlide">
      Item 2
    </div>
    <div class="carouselSlide">
      Item 3
    </div>
    <div class="carouselSlide">
      Item 4
    </div>
  </div>
</div>

Свободный перевод ответа от участника @Temani Afif.

→ Ссылка