javascript - setInterval() Metod skapar konstig överlappning


När jag höll på att leka med att bygga en enkel textkarusell stötte jag på ett problem som jag har svårt att förstå.

Layouten är enkel. Jag har en wrapper och texten jag vill att karusellen ska cykla igenom.

Problemet jag har, verkar dock, så vitt jag kan se, komma från setInterval-metoden. Efter att animationen har cyklat genom all text och återvänt till början, finns det en konstig överlappning mellan den första och den andra texten som visas. Den första texten kommer att animeras, men kommer sedan tillbaka för att tillfälligt ersätta den andra texten.

All hjälp med att förstå vad som orsakar att detta fel visas på detta sätt skulle vara mycket uppskattat.

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>

Svar #1

Lita inte på setInterval när du hanterar CSS-animationer. Du kommer aldrig att få en perfekt synkronisering. Överväg istället händelser som animationiteration/animationend/animationstart

Här är en bättre idé med mindre kod som är lättare att hantera.

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 will make sure the height is equal to 36px, font-size alone isn't enough  */
}

#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>