'Inline-block remove bottom space to add text

I have the below code to begin an animation for an acronym that makes the code transform to a vertical form. I'd like to have it where it will type the rest of the acronym out next to the letters on the button click. However doing so adds far to much space between all the type I want them to basically line up how they do before the button is pressed you can see what it currently does here:

var a = 0;
var b = 0;
var c = 0;
var d = 0;
var e = 0;
var f = 0;
var balance = 'alance';
var execution = 'xecution';
var teamwork = 'eamwork';
var training = 'raining';
var experience = 'xperience';
var results = 'esults';
var speed = 50;

function typeWriter() {
  while (a < balance.length) {
    document.getElementById("balance").innerHTML += balance.charAt(a);
    a++;
    setTimeout(typeWriter, speed);
  }
  while (b < execution.length) {
    document.getElementById("execution").innerHTML += execution.charAt(b);
    b++;
    setTimeout(typeWriter, speed);
  }
  while (c < teamwork.length) {
    document.getElementById("teamwork").innerHTML += teamwork.charAt(c);
    c++;
    setTimeout(typeWriter, speed);
  }
  while (d < training.length) {
    document.getElementById("training").innerHTML += training.charAt(d);
    d++;
    setTimeout(typeWriter, speed);
  }
  while (e < experience.length) {
    document.getElementById("experience").innerHTML += experience.charAt(e);
    e++;
    setTimeout(typeWriter, speed);
  }
  while (f < results.length) {
    document.getElementById("results").innerHTML += results.charAt(f);
    f++;
    setTimeout(typeWriter, speed);
  }
}

function scroller() {
  var move = document.querySelectorAll(".move");
  var fade = document.querySelectorAll(".fade");

  for (var i = 0; i < move.length; i++) {
    var windowHeight = window.innerHeight;
    var elementTop = move[i].getBoundingClientRect().top;
    var elementVisible = 0;

    if (elementTop < windowHeight - elementVisible) {
      move[i].classList.add("active");
    } else {
      move[i].classList.remove("active");
    }
  }

  for (var i = 0; i < fade.length; i++) {
    var windowHeight = window.innerHeight;
    var elementTop = fade[i].getBoundingClientRect().top;
    var elementVisible = 0;

    if (elementTop < windowHeight - elementVisible) {
      fade[i].classList.add("active");
    } else {
      fade[i].classList.remove("active");
    }
  }
}

window.addEventListener("scroll", scroller);
.move {
  font-size: 105px;
  position: relative;
}

.move.active {
  font-size: 105px;
  position: relative;
  animation: mover 5s ease 0s normal forwards;
}

.fade {
  font-size: 105px;
  position: relative;
}

.fade.active {
  font-size: 105px;
  position: relative;
  animation: fader 2s ease 0s normal forwards;
}

.move.active span {
  margin: 0px;
  position: relative;
  display: inline-block;
  animation: rotate 5s ease 0s normal forwards;
}

@keyframes mover {
  0.0% {
    transform: scale(1) translate(-0px, 0) skew(0deg);
  }
  100% {
    transform: scale(1) translate(-20%, 300px) skew(0deg) rotate(90deg);
  }
}

@keyframes rotate {
  0.0% {
    transform: scale(1) translate(-0px, 0) skew(0deg);
  }
  100% {
    transform: scale(1) translate(0px, 0px) skew(0deg) rotate(-90deg);
  }
}

@keyframes fader {
  0.0% {
    transform: scale(1) translate(-0px, 0) skew(0deg);
  }
  100% {
    opacity: 0;
  }
}

@keyframes typing {
  0% {
    width: 0%
  }
  100% {
    width: 100%
  }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<CENTER>
  <h2 class="fade">IT'S </h2>
  <h2 class="move">
    <span id="balance">B</span>
    <span id="execution">E</span>
    <span id="teamwork">T</span>
    <span id="training">T</span>
    <span id="experience">E</span>
    <span id="results">R</span>
  </h2>
  <h2 class="fade">TOGETHER </h2>
</CENTER>

<button onclick="typeWriter()">Click me</button>

https://noahark.w3spaces.com/saved-from-Tryit-2022-04-29.html

Any and all help will be extremely appreciated.



Solution 1:[1]

In your particular example, the spacing is coming from the lengths of the words. When I undo the rotation caused by the animation, we can see this:

as the words rotate, they fill in the gaps

So, if you force the length of the word to be one character (regardless of how many characters are actually there), then you no longer have the spacing problem caused by the words' length.

.move span {
    display: inline-block;
    width: 1.5ch;
}

the words are aligned vertically with expected spacing


Although forcing the span's width works, it can feel a little naughty. We're now relying on nice overflow behaviour, and hopefully we don't need the true width of the element for anything else.

I found that I didn't have to force width if we start at the end rather than the beginning. By default (no transform applied), it is already arranged as a column of words exactly like what you want at the end of the animation.

Then, you apply the 90 degree rotation to get the horizontal "BETTER" text. The animation undoes the rotation, meaning you always get what you expect at the end.

const betterAcronym = document.querySelector('#better-acronym')
const revealButton = document.querySelector('#reveal-button')

const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms))

const typeElement = async (element) => {
  const word = element.dataset['word']
  const start = element.textContent.length
  
  for (let c = start; c < word.length; ++c) {
    element.textContent += word[c]
    await wait(100)
  }
}

revealButton.addEventListener('click', async () => {
  betterAcronym.classList.add('revealed')
  
  await wait(2750) // length of the animation plus some
  
  Array.from(betterAcronym.children).forEach(typeElement)
})
.acronym {
  font-size: 1.25rem;
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  transform: rotate(-90deg);
  transform-origin: 0.5em 0.5em;
  transition: transform 2.5s ease-in-out;
}

.acronym > li {
  display: inline-block;
  transform: rotate(90deg);
  transform-origin: 0.5em 0.5em;
  transition: transform 2.5s ease-in-out;
}

.acronym.revealed {
  transform: rotate(0deg);
}

.acronym.revealed > li {
  transform: rotate(0deg);
}

body {
  overflow: hidden;
}

button {
  font-size: 1.25rem;
}
<ol id="better-acronym" class="acronym">
  <li data-word="balance">B</li>
  <li data-word="execution">E</li>
  <li data-word="teamwork">T</li>
  <li data-word="training">T</li>
  <li data-word="experience">E</li>
  <li data-word="results">R</li>
</ol>
<button id="reveal-button">Reveal</button>

Solution 2:[2]

You just have to provide width: 73px to .move.active span. And after the animation ends adjust the parent height.
In the demo look for code comments. I've also fixed the typing effect code. View in full page mode.

const move = document.querySelector(".move");
const fade = document.querySelectorAll(".fade");

const words = ['balance', 'execution', 'teamwork', 'training', 'experience', 'results'];
let col = 1;
let row = 0;
const speed = 100;

function typeWriter() {
   const e = document.getElementById(words[row]);
   e.textContent += words[row][col];
   col++;
   if(col >= words[row].length){
     col = 1;
     row++;
   }
   if(row < words.length)
    setTimeout(typeWriter, speed);
   else
    typeWriter = () => console.log('can not rerun the animation');
}

function scroller() {
    var windowHeight = window.innerHeight;
    var elementTop = move.getBoundingClientRect().top;
    var elementVisible = 0;

    if (elementTop < windowHeight - elementVisible) {
      move.classList.add("active");
    } else {
      move.classList.remove("active");
    }

  for (var i = 0; i < fade.length; i++) {
    var windowHeight = window.innerHeight;
    var elementTop = fade[i].getBoundingClientRect().top;
    var elementVisible = 0;

    if (elementTop < windowHeight - elementVisible) {
      fade[i].classList.add("active");
    } else {
      fade[i].classList.remove("active");
    }
  }
}

// adjust parent height after the animation ends
// change the calculation as per your requirements
move.onanimationend  = (event)=>{
    if(move === event.target) {
      move.parentElement.style.height = move.parentElement.offsetHeight - move.offsetHeight + move.offsetWidth + - move.nextElementSibling.scrollHeight + 'px';
  }
};

window.addEventListener("scroll", scroller);
center{ border: 1px dashed gray;}
.move {
  font-size: 105px;
  position: relative;
}

.move.active {
  font-size: 105px;
  position: relative;
  animation: mover 5s ease 0s normal forwards;
  
  /* we need exact width to adjust the parent's height */
  width: fit-content;
}

.fade {
  font-size: 105px;
  position: relative;
}

.fade.active {
  font-size: 105px;
  position: relative;
  animation: fader 2s ease 0s normal forwards;
}

.move.active span {
  margin: 0px;
  position: relative;
  display: inline-block;
  animation: rotate 5s ease 0s normal forwards;

  /* specify width equal to one character in the font */
  width: 73px;
}

@keyframes mover {
  0.0% {
    transform: scale(1) translate(-0px, 0) skew(0deg);
  }
  100% {
    transform: scale(1) translate(-20%, 300px) skew(0deg) rotate(90deg);
  }
}

@keyframes rotate {
  0.0% {
    transform: scale(1) translate(-0px, 0) skew(0deg);
  }
  100% {
    transform: scale(1) translate(0px, 0px) skew(0deg) rotate(-90deg);
  }
}

@keyframes fader {
  0.0% {
    transform: scale(1) translate(-0px, 0) skew(0deg);
  }
  100% {
    opacity: 0;
  }
}

@keyframes typing {
  0% {
    width: 0%
  }
  100% {
    width: 100%
  }
}

button {
  position: fixed;
  top: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<CENTER>
  <h2 class="fade">IT'S </h2>
  <h2 class="move">
    <span id="balance">B</span>
    <span id="execution">E</span>
    <span id="teamwork">T</span>
    <span id="training">T</span>
    <span id="experience">E</span>
    <span id="results">R</span>
  </h2>
  <h2 class="fade">TOGETHER </h2>
</CENTER>

<button onclick="typeWriter()">Click me</button>

Adjust the calculations as per your requirements.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 the Hutt