# Sliding Letters Animation!

**URL:** https://forum.kirupa.com/t/sliding-letters-animation/639197
**Category:** web dev
**Created:** [December 31, 2018, 3:23am UTC](https://forum.kirupa.com/t/sliding-letters-animation/639197 "2018-12-31T03:23:03Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![kirupa](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/kirupa/32/11616_2.png) [@kirupa](https://forum.kirupa.com/u/kirupa)
#### Post date: [December 31, 2018, 3:23am UTC](https://forum.kirupa.com/t/sliding-letters-animation/639197/1 "2018-12-31T03:23:03Z")

</div>

A little something I was fiddling with earlier today:

> <https://twitter.com/kirupa/status/1079579340579397632>

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

---

<div class="post-metadata">

### Author: ![prg9](https://avatars.discourse-cdn.com/v4/letter/p/8dc957/32.png) [@prg9](https://forum.kirupa.com/u/prg9)
#### Post date: [January 7, 2019, 5:33pm UTC](https://forum.kirupa.com/t/sliding-letters-animation/639197/2 "2019-01-07T17:33:55Z")

</div>

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

> <https://twitter.com/kirupa/status/1081076377779003393>

---

<div class="post-metadata">

### Author: ![kirupa](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/kirupa/32/11616_2.png) [@kirupa](https://forum.kirupa.com/u/kirupa)
#### Post date: [January 7, 2019, 7:20pm UTC](https://forum.kirupa.com/t/sliding-letters-animation/639197/3 "2019-01-07T19:20:07Z")

</div>

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

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.

---

<div class="post-metadata">

### Author: ![prg9](https://avatars.discourse-cdn.com/v4/letter/p/8dc957/32.png) [@prg9](https://forum.kirupa.com/u/prg9)
#### Post date: [January 7, 2019, 8:00pm UTC](https://forum.kirupa.com/t/sliding-letters-animation/639197/4 "2019-01-07T20:00:25Z")

</div>

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? 🧐

---

<div class="post-metadata">

### Author: ![kirupa](https://yyz1.discourse-cdn.com/flex011/user_avatar/forum.kirupa.com/kirupa/32/11616_2.png) [@kirupa](https://forum.kirupa.com/u/kirupa)
#### Post date: [January 8, 2019, 3:22am UTC](https://forum.kirupa.com/t/sliding-letters-animation/639197/5 "2019-01-08T03:22:22Z")

</div>

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

---

<div class="post-metadata">

### Author: ![prg9](https://avatars.discourse-cdn.com/v4/letter/p/8dc957/32.png) [@prg9](https://forum.kirupa.com/u/prg9)
#### Post date: [January 9, 2019, 1:48pm UTC](https://forum.kirupa.com/t/sliding-letters-animation/639197/6 "2019-01-09T13:48:26Z")

</div>

> [@kirupa](#):
>
> I’ll post a tutorial on this in the near future

Your use of the word near was on point. 😉
