JSFiddle - React, Tailwind, and code Playground

by Dogbert

HTML

<div id="words"></div>

CSS

body {
  font-family: Lucida Grande;
  line-height: 4;
  padding: 20px;
}

#words span {
  color: #e9e9e9;
  transition: all .4s;
  width: 20%;
  display: inline-block;
  height: 4em;
  overflow: hidden;
}

#words span.current {
  color: #000;
}

JavaScript

function run(el, instructions) {
  var words = [];
  for (var i = 0; i < 15; i++) {
    words.push($("<span>").addClass("word").appendTo(el));
  }
  step(0);

  function step(i) {
    console.log(i, instructions, instructions.length);
    if (i >= instructions.length) {
      return;
    }
    var ins = instructions[i];
    switch (ins[0]) {
      case "set":
        if ($.isArray(ins[1])) {
          ins[1].forEach(function(text, index) {
            words[index].html(text);
          });
        } else {
          words[ins[1]].html(ins[2]);
        }
        step(i + 1);
        break;
      case "sentence":
        var opts = ins[2];
        staggeredFadeIn(ins[1], 0, opts.betweenWords, function() {
          if (opts.flip) {
            flip(opts.flip.index, opts.flip.times, opts.betweenFlips, next);
          } else {
            next();
          }

          function next() {
            sleep(opts.forSentence, function() {
              ins[1].forEach(function(index) {
                words[index].removeClass("current");
              });
              sleep(opts.betweenSentences, function() {
                step(i + 1);
              });
            });
          }
        });
        break;
      case "replace":
        words[ins[1]].fadeOut("slow", function() {
          words[ins[1]].text(ins[2]).fadeIn("slow");
        });
        // We don't wait to continue to the next step in "replace".
        step(i + 1);
      case "continue":
        step(0);
        break;
    }
  }

  function staggeredFadeIn(indices, i, delay, callback) {
    if (i >= indices.length) {
      return callback();
    }
    words[indices[i]].addClass("current");
    setTimeout(function() {
      staggeredFadeIn(indices, i + 1, delay, callback)
    }, delay);
  }

  function sleep(delay, callback) {
    setTimeout(callback, delay);
  }

  function flip(index, times, sleepBetweenFlips, callback) {
    if (times <= 1) {
      return callback();
    }
    sleep(sleepBetweenFlips,...