skip to Main Content

I have seen this effect on multiple websites so for reference here are two sites that show the desired result. Locomotive and TUX are two of these, Locomotive might be the best to view as you can instantly see the desired effect hovering over the nav items – although the effect is used throughout the site.

So you have a phrase like "Careers", which on hover scrambles and animations back to the original order of the letters.

I have a version of this I’ve been playing with (attached) but it’s jQuery which I don’t use anywhere else and also it doesn’t seem as ‘scrambled’ as the first letter doesn’t really change – not to mention the width of the word changes.

I’d really appreciate some help creating an effect closer to the examples!

jQuery('document').ready(function($) {
  // Set effect velocity in ms
  var velocity = 50;

  var shuffleElement = $('.shuffle');

  $.each(shuffleElement, function(index, item) {
    $(item).attr('data-text', $(item).text());
  });

  var shuffle = function(o) {
    for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
    return o;
  };

  var shuffleText = function(element, originalText) {
    var elementTextArray = [];
    var randomText = [];

    for (i = 0; i < originalText.length; i++) {
      elementTextArray.push(originalText.charAt([i]));
    };

    var repeatShuffle = function(times, index) {
      if (index == times) {
        element.text(originalText);
        return;
      }

      setTimeout(function() {
        randomText = shuffle(elementTextArray);
        for (var i = 0; i < index; i++) {
          randomText[i] = originalText[i];
        }
        randomText = randomText.join('');
        element.text(randomText);
        index++;
        repeatShuffle(times, index);
      }, velocity);
    }
    repeatShuffle(element.text().length, 0);
  }

  shuffleElement.mouseenter(function() {
    shuffleText($(this), $(this).data('text'));
  });
});
body {
  font-family: helvetica;
  font-size: 16px;
  padding: 48px;
}

a {
  color: black;
  text-decoration: none;
}

ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

li {
  display: inline-block;
  margin: 0 16px 8px 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li>
    <a class="shuffle" href="#">shuffle</a>
  </li>
  <li>
    <a class="shuffle" href="#">texts</a>
  </li>
  <li>
    <a class="shuffle" href="#">hover</a>
  </li>
</ul>

2

Answers


  1. The first letter already changes(slow down the velocity to see it), to change width it is as easy as adding some letter-spacing. You could always play around with font-size or font-weight.

    I added transition all to the anchors just to demonstrate what it looks like but you can be more specific when defining it.

    In the js, I added a class and removed it on "animation" end, the letter spacing is added on that class.

    If you want to make this more scrambled, you could always call the shuffle 2 times so the length of shuffling seems longer. As a matter of fact, after looking at this a bit more, you are replacing the text when setting it back to the original. It would look better if you could arrange them back to the original text after scrambling it

    jQuery('document').ready(function($) {
      // Set effect velocity in ms
      var velocity = 50;
    
      var shuffleElement = $('.shuffle');
    
      $.each(shuffleElement, function(index, item) {
        $(item).attr('data-text', $(item).text());
      });
    
      var shuffle = function(o) {
        for (var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
        return o;
      };
    
      var shuffleText = function(element, originalText) {
        var elementTextArray = [];
        var randomText = [];
    
        for (i = 0; i < originalText.length; i++) {
          elementTextArray.push(originalText.charAt([i]));
        };
    
        var repeatShuffle = function(times, index) {
          if (index == times) {
            element.text(originalText);
            element.removeClass("active");
            return;
          }
    
          setTimeout(function() {
            randomText = shuffle(elementTextArray);
            for (var i = 0; i < index; i++) {
              randomText[i] = originalText[i];
            }
            randomText = randomText.join('');
            element.text(randomText);
            index++;
            repeatShuffle(times, index);
          }, velocity);
        }
        repeatShuffle(element.text().length, 0);
      }
    
      shuffleElement.mouseenter(function() {
        $(this).addClass("active");
        shuffleText($(this), $(this).data('text'));
      });
    });
    body {
      font-family: helvetica;
      font-size: 16px;
      padding: 48px;
    }
    
    a {
      color: black;
      text-decoration: none;
      transition: all 0.2s;
    }
    a.active {
      letter-spacing: 2px;
    }
    
    ul {
      list-style: none;
      margin: 0;
      padding: 0;
    }
    
    li {
      display: inline-block;
      margin: 0 16px 8px 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <ul>
      <li>
        <a class="shuffle" href="#">shuffle</a>
      </li>
      <li>
        <a class="shuffle" href="#">texts</a>
      </li>
      <li>
        <a class="shuffle" href="#">hover</a>
      </li>
    </ul>
    Login or Signup to reply.
  2. To avoid layout shifts you also need to disable kerning and ligatures – otherwise the word width will change depending on different letter combinations e.g fi (ligature) or Av (kerning).

    .shuffle{
      font-kerning: none;
      font-feature-settings: "liga" 0;
    }
    

    Here’s a simple vanilla JS function

    let lis = document.querySelectorAll(".shuffle");
    let duration = 50;
    let framesMax = 7
    
    lis.forEach((li) => {
      let textOrig = li.textContent;
      let inter;
    
      li.addEventListener("mouseover", (e) => {
        let text = li.textContent;
        let charArr = text.split("");
        let frame = 0;
    
        // shuffle at given speed
        inter = setInterval(() => {
          if(frame<framesMax){
            let charArrShuff = shuffleArr(charArr);
            li.textContent = charArrShuff.join("");
            frame++
          }else{
            clearInterval(inter);
            li.textContent = textOrig;
          }
        }, duration);
    
      });
    
      // stop
      li.addEventListener("mouseleave", (e) => {
        li.textContent = textOrig;
        clearInterval(inter);
      });
    });
    
    function shuffleArr(arr) {
      return arr.reduce(
        ([a, b]) => (
          b.push(...a.splice((Math.random() * a.length) | 0, 1)), [a, b]
        ),
        [[...arr], []]
      )[1];
    }
    body {
      font-family: helvetica;
      font-size: 10vmin;
      padding: 48px;
    }
    
    a {
      color: black;
      text-decoration: none;
    }
    
    ul {
      list-style: none;
      margin: 0;
      padding: 0;
    }
    
    li {
      display: inline-block;
      margin: 0 16px 8px 0;
    }
    
    .shuffle{
      font-kerning: none;
      font-feature-settings: "liga" 0;
    }
    <ul>
      <li>
        <a class="shuffle" href="#">shuffle</a>
      </li>
      <li>
        <a class="shuffle" href="#">texts</a>
      </li>
      <li>
        <a class="shuffle" href="#">hover</a>
      </li>
    </ul>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search