Search code examples
javascriptanimationwhitespace

Why won't JS rotating text animation recognize white spaces


I am trying to alter this animation https://codepen.io/FTP-Group/pen/GoEZGZ. I would like to display a two word reveal phrase instead of a single word. It is basically not recognizing a space and smooshes the words together (twowords instead of two words). Here is the JS:

var words = document.getElementsByClassName('word');
var wordArray = [];
var currentWord = 0;

words[currentWord].style.opacity = 1;
for (var i = 0; i < words.length; i++) {
  splitLetters(words[i]);
}


function changeWord() {
  var cw = wordArray[currentWord];
  var nw = currentWord == words.length-1 ? wordArray[0] : wordArray[currentWord+1];
  for (var i = 0; i < cw.length; i++) {
    animateLetterOut(cw, i);
  }
  
  for (var i = 0; i < nw.length; i++) {
    nw[i].className = 'letter behind';
    nw[0].parentElement.style.opacity = 1;
    animateLetterIn(nw, i);
  }
  
  currentWord = (currentWord == wordArray.length-1) ? 0 : currentWord+1;
}

function animateLetterOut(cw, i) {
  setTimeout(function() {
        cw[i].className = 'letter out';
  }, i*80);
}

function animateLetterIn(nw, i) {
  setTimeout(function() {
        nw[i].className = 'letter in';
  }, 340+(i*80));
}

function splitLetters(word) {
  var content = word.innerHTML;
  word.innerHTML = '';
  var letters = [];
  for (var i = 0; i < content.length; i++) {
    var letter = document.createElement('span');
    letter.className = 'letter';
    letter.innerHTML = content.charAt(i);
    word.appendChild(letter);
    letters.push(letter);
  }
  
  wordArray.push(letters);
}

changeWord();
setInterval(changeWord, 2000);
delay(7000);

When using a space to separate two words, the animation did not recognize the space as a character. I tried using a non-breaking space, but it just spelled out the code. I am very new to JS and any help is greatly appreciated.


Solution

  • What to do:
    Add white-space: pre; to the css rules for .letter.

    Explanation:
    .letter has display: inline-block, and for this and a few other values of display, by default the leading and trailing spaces are ignored.

    This is also the same reason why a <div> element like the following:

    <div>
       some text here
    </div>
    

    will be displayed as if it were just <div>some text here</div> (unless you set the values of some css properties).

    In your case <span class="letter"> </span>, was originally displayed just like <span class="letter"></span> (i.e. without space).

    But if you add white-space: pre, then the spaces and new line characters will not be ignored, but will be displayed just like the input.


    I've copied the code from your codepen here, but I replaced some text and added .white-space: pre. Also, your delay is undefined so I commented it out.

    var words = document.getElementsByClassName('word');
    var wordArray = [];
    var currentWord = 0;
    
    words[currentWord].style.opacity = 1;
    for (var i = 0; i < words.length; i++) {
      splitLetters(words[i]);
    }
    
    
    function changeWord() {
      var cw = wordArray[currentWord];
      var nw = currentWord == words.length-1 ? wordArray[0] : wordArray[currentWord+1];
      for (var i = 0; i < cw.length; i++) {
        animateLetterOut(cw, i);
      }
      
      for (var i = 0; i < nw.length; i++) {
        nw[i].className = 'letter behind';
        nw[0].parentElement.style.opacity = 1;
        animateLetterIn(nw, i);
      }
      
      currentWord = (currentWord == wordArray.length-1) ? 0 : currentWord+1;
    }
    
    function animateLetterOut(cw, i) {
      setTimeout(function() {
            cw[i].className = 'letter out';
      }, i*80);
    }
    
    function animateLetterIn(nw, i) {
      setTimeout(function() {
            nw[i].className = 'letter in';
      }, 340+(i*80));
    }
    
    function splitLetters(word) {
      var content = word.innerHTML;
      word.innerHTML = '';
      var letters = [];
      for (var i = 0; i < content.length; i++) {
        var letter = document.createElement('span');
        letter.className = 'letter';
        letter.innerHTML = content.charAt(i);
        word.appendChild(letter);
        letters.push(letter);
      }
      
      wordArray.push(letters);
    }
    
    changeWord();
    setInterval(changeWord, 2000);
    // delay(7000);
    @import url(https://fonts.googleapis.com/css?family=Open+Sans:600);
    
    body {
      font-family: 'Open Sans', sans-serif;
      font-weight: 600;
      font-size: 40px;
    }
    
    .text {
      position: absolute;
      width: 450px;
      left: 50%;
      margin-left: -225px;
      height: 40px;
      top: 50%;
      margin-top: -20px;
    }
    
    p {
      display: inline-block;
      vertical-align: top;
      margin: 0;
    }
    
    .word {
      position: absolute;
      width: 220px;
      opacity: 0;
    }
    
    .letter {
      display: inline-block;
      position: relative;
      float: left;
      transform: translateZ(25px);
      transform-origin: 50% 50% 25px;
      white-space: pre;
    }
    
    .letter.out {
      transform: rotateX(90deg);
      transition: transform 0.42s cubic-bezier(0.55, 0.055, 0.675, 0.19);
    }
    
    .letter.behind {
      transform: rotateX(-90deg);
    }
    
    .letter.in {
      transform: rotateX(0deg);
      transition: transform 0.48s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    }
    
    .wisteria {
      color: #8e44ad;
    }
    
    .belize {
      color: #2980b9;
    }
    
    .pomegranate {
      color: #c0392b;
    }
    
    .green {
      color: #16a085;
    }
    
    .midnight {
      color: #2c3e50;
    }
    <div class="text">
      <p>Nachos are</p>
      <p>
        <span class="word wisteria">very tasty.</span>
        <span class="word belize">very good.</span>
        <span class="word pomegranate">very tasty.</span>
        <span class="word green">very good.</span>
        <span class="word midnight">so great.</span>
      </p>
      <p></p>
    </div>