Sliding Letters Animation!

A little something I was fiddling with earlier today:

It isn’t overly complex, and the bulk of the code is in shifting the letters back every iteration to give the illusion of an infinite scroll.

Here is the code:

<!DOCTYPE html>
<html>
 
<head>
  <title>Sliding Letters</title>
 
  <style>
    body {
      margin: 0px;
      padding: 50px;
    }

    #container {
      font-size: 0;
      width: 380px;
    }

    .letterGroup {
      width: 100px;
      height: 100px;
      margin: 5px;
      border: #EEE solid 1px;

      background-color: yellow;
      overflow: hidden;
      display: inline-block;
    }
    .letter {
      background-color: azure;
      margin-top: 0px;
      user-select: none;

      animation: slide 2s infinite ease-in-out;
    }
    .letter p {
      font-family: sans-serif;
      font-weight: bold;
      font-size: 48px;

      display: flex;
      align-items: center;
      justify-content: center;

      height: 100px;

      padding: 0;
      margin: 0;
    }

    @keyframes slide {
      0% {
        transform: translateY(0px);
      }
      50% {
        transform: translateY(-100px);
      }
      100% {
        transform: translateY(-100px);
      }
    }
  </style>
</head>
 
<body>
  <div id="container">
    <div class="letterGroup">
      <div class="letter">
        <p>A</p>
        <p>A</p>
      </div>
    </div>

    <div class="letterGroup">
      <div class="letter">
        <p>N</p>
        <p>N</p>
      </div>
    </div>

    <div class="letterGroup">
      <div class="letter">
        <p>I</p>
        <p>I</p>
      </div>
    </div>

    <div class="letterGroup">
      <div class="letter">
        <p>M</p>
        <p>M</p>
      </div>
    </div>

    <div class="letterGroup">
        <div class="letter">
          <p>A</p>
          <p>A</p>
        </div>
      </div>
  
      <div class="letterGroup">
        <div class="letter">
          <p>T</p>
          <p>T</p>
        </div>
      </div>

      <div class="letterGroup">
        <div class="letter">
          <p>I</p>
          <p>I</p>
        </div>
      </div>
  
      <div class="letterGroup">
        <div class="letter">
          <p>O</p>
          <p>O</p>
        </div>
      </div>
  
      <div class="letterGroup">
        <div class="letter">
          <p>N</p>
          <p>N</p>
        </div>
      </div>
    
  </div>
 
  <script>
    var letters = document.querySelectorAll(".letter");
    var container = document.querySelector("#container");

    var h_range = [0, 360];
    var s_range = [0, 50];
    var l_range = [60, 100];
    var a_range = [1, 1];

    function setup() {
      for (var i = 0; i < letters.length; i++) {
        var letter = letters[i];
  
        var colorOne = getRandomColor(h_range, s_range, l_range, a_range);
        var colorTwo = getRandomColor(h_range, s_range, l_range, a_range);
  
        letter.color = {
          one: colorOne,
          two: colorTwo
        };
  
        letter.children[0].style.backgroundColor = colorOne.hslaValue;
        letter.children[1].style.backgroundColor = colorTwo.hslaValue;
  
        letter.style.animationDelay = getRandomNumber(0, 500) / 500 + "s";
        letter.style.animationDuration = getRandomNumber(20, 40) / 10 + "s";
      }
    }
    setup();

    container.addEventListener("animationiteration", loopLetter, false);

    function loopLetter(e) {
      var el = e.target;

      var colorOne = el.color.two;
      var colorTwo = getRandomColor(h_range, s_range, l_range, a_range);

      el.color = {
        one: colorOne,
        two: colorTwo
      }

      el.children[0].style.backgroundColor = colorOne.hslaValue;
      el.children[1].style.backgroundColor = colorTwo.hslaValue;
    }

    function getRandomColor(h, s, l, a) {
      var hue = getRandomNumber(h[0], h[1]);
      var saturation = getRandomNumber(s[0], s[1]);
      var lightness = getRandomNumber(l[0], l[1]);
      var alpha = getRandomNumber(a[0] * 100, a[1] * 100) / 100;

      return {
        h: hue,
        s: saturation,
        l: lightness,
        a: alpha,
        hslaValue: getHSLAColor(hue, saturation, lightness, alpha)
      }
    }

    function getRandomNumber(low, high) {
      var r = Math.floor(Math.random() * (high - low + 1)) + low;
      return r;
    }

    function getHSLAColor(h, s, l, a) {
      return `hsl(${h}, ${s}%, ${l}%, ${a})`;
    }
        
  </script>
</body>
 
</html>
1 Like

Clever and fun, but where is the example code for the other fiddling you did, displaying more than two items for each instance? :wink:

Haha. That was just me removing the overflow: hidden on just those two items :stuck_out_tongue:

You will always have two items that are moving. It is just that you only see one at any given time. I’ll post a tutorial on this in the near future that goes into more detail on how all of this works.

1 Like

Sure, but in this other example were you changing the numbers dynamically via JS to populate the two instances at each iteration, allowing it to handle more than two numbers as shown in the initial example? :face_with_monocle:

Oh! Here is the code for that:

<!DOCTYPE html>
<html>
 
<head>
  <title>Infinite Items</title>
 
  <style>
    body {
      margin: 0px;
      padding: 0px;
    }

    #container {
      font-size: 0;

      height: 100vh;
      width: 100vw;

      display: flex;
      align-items: center;
      justify-content: center;
    }

    .itemGroup {
      width: 100px;
      height: 100px;
      margin: 5px;
      border: #EEE solid 1px;

      overflow: hidden;
      display: inline-block;
    }

    .item {
      margin-top: 0px;
      user-select: none;

      animation: slide infinite ease-in-out;
    }

    .item p {
      font-family: sans-serif;
      font-weight: bold;
      font-size: 48px;

      display: flex;
      align-items: center;
      justify-content: center;

      height: 100px;

      padding: 0;
      margin: 0;
    }

    .itemGroup:nth-child(1) {
      background-color: #e57373;
    }

    .itemGroup:nth-child(1) .item {
      animation-duration: 400ms;
      animation-delay: 100ms;
    }

    .itemGroup:nth-child(2) {
      background-color: #4DD0E1;
    }

    .itemGroup:nth-child(2) .item {
      animation-duration: 300ms;
      animation-delay: 150ms;
    }

    .itemGroup:nth-child(3) {
      background-color: #FFEB3B;
    }

    .itemGroup:nth-child(3) .item {
      animation-duration: 600ms;
      animation-delay: 0ms;
    }

    .itemGroup:nth-child(4) {
      background-color: #9CCC65;
    }

    .itemGroup:nth-child(4) .item {
      animation-duration: 500ms;
      animation-delay: 200ms;
    }

    @keyframes slide {
      0% {
        transform: translateY(0px);
      }
      50% {
        transform: translateY(-100px);
      }
      100% {
        transform: translateY(-100px);
      }
    }
  </style>
</head>
 
<body>
  <div id="container">
    <div class="itemGroup">
      <div class="item">
        <p>1</p>
        <p>2</p>
      </div>
    </div>
    <div class="itemGroup">
      <div class="item">
        <p>1</p>
        <p>2</p>
      </div>
    </div>
    <div class="itemGroup">
      <div class="item">
        <p>1</p>
        <p>2</p>
      </div>
    </div>
    <div class="itemGroup">
      <div class="item">
        <p>1</p>
        <p>2</p>
      </div>
    </div>
  </div>
 
  <script>
    var items = document.querySelectorAll(".item");
    var container = document.querySelector("#container");

    var h_range = [0, 360];
    var s_range = [0, 50];
    var l_range = [60, 100];
    var a_range = [1, 1];

    function setup() {
      for (var i = 0; i < items.length; i++) {
        var item = items[i];

        item.state = {
          one: getRandomNumber(0, 10),
          two: getRandomNumber(0, 10)
        }

        item.children[0].innerText = item.state.one;
        item.children[1].innerText = item.state.two;
  
        //item.style.animationDelay = getRandomNumber(0, 500) / 500 + "s";
        //item.style.animationDuration = getRandomNumber(10, 30) / 20 + "s";
      }
    }
    setup();

    container.addEventListener("animationiteration", loopLetter, false);

    function loopLetter(e) {
      var item = e.target;

      item.state = {
        one: item.state.two,
        two: getRandomNumber(0, 10)
      }

      item.children[0].innerText = item.state.one;
      item.children[1].innerText = item.state.two;
    }

    function getRandomNumber(low, high) {
      var r = Math.floor(Math.random() * (high - low + 1)) + low;
      return r;
    }
        
  </script>
</body>
 
</html>
1 Like

Your use of the word near was on point. :wink:

1 Like