Javascript and css animation keep getting out of sync

Viewed 252

So basically my CSS animation is supposed to emulate some text being typed, un-typed and finally changed to another word. Normally the CSS keyframe and JavaScript setInterval are in sync but on mobile devices they sometimes get out of sync or if I go to a new tab they de-synchronize.

<center style="display: flex; width:70vw">
     <h1 style="padding-right:20px;">We</h1>
     <h1 id="terminal-text">innovate.</h1>
</center>
<script type="text/javascript">
    var i = 0;
    words = ['architect.','build.','design.','code.','develop.','innovate.'];
    setInterval(function() {
      $("#terminal-text").text(words[i]);
      i++;
      if (i > words.length) {
        i=0;
      }
    },3000);
</script>
#header .banner h1 {
  margin: 1em 0 .5em -10%;
  padding: 0;
  color: white;
  text-align: left;
}
@keyframes terminal {
  0% {max-width:0}
  50% {max-width:100%}
  55% {max-width:100%}
  100% {max-width:0}
}
#header #terminal-text {
  font-weight: bold;
  margin: 1em 0 .5em 0;
  padding: 0;
  animation: terminal 3s infinite;
  overflow: hidden;
  white-space: nowrap;
  border-right: 4px solid white;
  text-align: left;
}
2 Answers

instead of having a setInterval() in javascript you could listen for animation end event. The problem with your approach is that you can not start both "timers" (css/js) at the same time (you could with another approach) but this could solve your issue

var wordIndex = 0;
words = ['architect.','build.','design.','code.','develop.','innovate.'];

var changeWord = function() {
  $("#terminal-text").text(words[wordIndex++]);
};

var element = document.querySelector("#terminal-text");
element.addEventListener("animationiteration", changeWord, false);

// you could also do something special on start event
// replace changeWord with whatever functionality you need
// element.addEventListener("animationstart", changeWord, false);

// animationend will never be triggered in your code at the moment
// but maybe you want to do something with it somewhen
// element.addEventListener("animationend", function() {console.log('animation ended')}, false);
#header .banner h1 {
  margin: 1em 0 .5em -10%;
  padding: 0;
  color: white;
  text-align: left;
}
@keyframes terminal {
  0% {max-width:0}
  50% {max-width:100%}
  55% {max-width:100%}
  100% {max-width:0}
}
#header #terminal-text {
  font-weight: bold;
  margin: 1em 0 .5em 0;
  padding: 0;
  animation: terminal 3s infinite;
  overflow: hidden;
  white-space: nowrap;
  border-right: 4px solid white;
  text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="header">
  <center style="display: flex; width:70vw">
    <h1 style="padding-right:20px;">We</h1>
    <h1 id="terminal-text">innovate.</h1>
  </center>
</div>

Not really a answer, but more of a suggestion, this remembered me about 2 simple JS functions I wrote for this a while ago, type_word() and erase_word(), and I was able to find it back. This does not really help solving your problem, but in the end it might help you. It is pretty cool, even uses a random time factor to simulate realistic typing !

var span = document.getElementById('text');

// add a right border to simulate cursor!
span.style.borderRight = "1px solid #000";

type_word('develop', span, function(){
  erase_word(span, function(){
    type_word('innovate', span, function(){
      // all finished, remove the fake cursor !
      span.style.borderRight = "0px";
    });
  });
});


function type_word(word, container, cback){
  container.innerHTML += word.substring(0,1);
  if(word.length > 1){
    var t = setTimeout(function(){type_word(word.substring(1), container, cback); }, 50+Math.random()*250);
  }
  else{
    cback();
  }
}

function erase_word(container, cback){
  container.innerHTML = span.innerHTML.slice(0, -1);
  if(container.innerHTML.length > 0){
    var t = setTimeout(function(){erase_word(container, cback); }, 50+Math.random()*50);
  }
  else{
    cback();
  }
}
We <span id="text"></span>

Related