Thursday, February 15, 2024

Create a Rotating Text Animation Effect With CSS Variables and JavaScript

Create a Rotating Text Animation Effect With CSS Variables and JavaScript

In this new tutorial, we’ll learn how to create a rotating text animation effect using CSS variables and JavaScript. Although we can certainly build a simple text-changing animation in pure CSS, we’ll put in the loop JavaScript to make things more maintainable and effective. 

Our Text Animation

Here’s what we’re going to create. It’s a great addition to portfolio websites or hero sections to highlight things.

We’ll start from a basic text animation and finish with the target effect.

Basic Text Rotating Animation

We’ll define a h1 element where we’ll put as spans all the words that we want to animate. By default, all spans will have the data-bg-color and data-color custom attributes that will determine their text and background colors accordingly. Plus, initially, only the span with the current class will be visible—the first one in our case.

Here’s the required markup format:

1
<h1 class="words-wrapper">
2
  I want to learn <span class="css">CSS</span> and
3
  <span class="words">
4
    <span class="current" data-bg-color="#ffc703" data-color="#000">React</span>
5
    <span data-bg-color="#004e98" data-color="#fff">TypeScript</span>
6
    <span data-bg-color="#8cb369" data-color="#000">Python</span>
7
    <span data-bg-color="#104911" data-color="#fff">PrestaShop</span>
8
    <span data-bg-color="#b8c0ff" data-color="#000">Ruby</span>
9
    <span data-bg-color="#e71d36" data-color="#fff">Angular</span>
10
    <span data-bg-color="#e2c044" data-color="#000">WordPress</span>
11
    <span data-bg-color="#065a82" data-color="#fff">Node</span>
12
  </span>.
13
</h1>

As we’ve done in many other tutorials in the past, we’ll use CSS Grid to place all words on top of each other. But remember that each time, only the word with the current class will appear. 

Here are all the required styles:

1
.words-wrapper {
2
  font-size: 40px;
3
  font-weight: bold;
4
  text-align: center;
5
}
6
7
.words-wrapper .css {
8
  color: #2ec4b6;
9
}
10
11
.words-wrapper .words {
12
  display: inline-grid;
13
  padding: 0 10px;
14
  border-radius: 6px;
15
  color: var(--color, #000);
16
  background: var(--color-bg, #ffc703);
17
}
18
19
.words-wrapper .words span {
20
  grid-area: 1/1;
21
  display: none;
22
}
23
24
.words-wrapper .words span.current {
25
  display: block;
26
}

To show a different word after a certain amount of time, we’ll follow this approach:

  • Every 1.5 seconds, we’ll target the visible (active) word.
  • Then, we’ll find its adjacent sibling word if it exists, otherwise, we’ll get the first word.
  • We’ll remove the current class from the active word and add it to the new element.
  • Finally, we’ll grab the colors of the new active word and update the corresponding CSS variables.

Here’s the required JavaScript:

1
const wrapper = document.querySelector(".words");
2
const CURRENT_CLASS = "current";
3
4
setInterval(() => {
5
  const currentWord = wrapper.querySelector("span.current");
6
  const nextWord = currentWord.nextElementSibling
7
    ? currentWord.nextElementSibling
8
    : wrapper.firstElementChild;
9
  currentWord.classList.remove(CURRENT_CLASS);
10
  nextWord.classList.add(CURRENT_CLASS);
11
  wrapper.style.setProperty("--color", nextWord.dataset.color);
12
  wrapper.style.setProperty("--color-bg", nextWord.dataset.bgColor);
13
}, 1500);

We'll end up with the following demo:

Complex Text Rotating Animation

Let’s now build on the previous example and create something more elegant!

The markup will remain the same apart from one change; this time, we’ll add the next class to the word that comes after the active one, like this:

1
<h1 class="words-wrapper">
2
  I want to learn <span class="css">CSS</span> and
3
  <span class="words">
4
    <span class="current" data-bg-color="#ffc703" data-color="#000">React</span>
5
    <span class="next" data-bg-color="#004e98" data-color="#fff">TypeScript</span>
6
    <span data-bg-color="#8cb369" data-color="#000">Python</span>
7
    <span data-bg-color="#104911" data-color="#fff">PrestaShop</span>
8
    <span data-bg-color="#b8c0ff" data-color="#000">Ruby</span>
9
    <span data-bg-color="#e71d36" data-color="#fff">Angular</span>
10
    <span data-bg-color="#e2c044" data-color="#000">WordPress</span>
11
    <span data-bg-color="#065a82" data-color="#fff">Node</span>
12
  </span>.
13
</h1>

Unlike the previous example, we won’t use CSS Grid to position the words, but instead, each word will be an absolute positioned element. Their immediate parent (i.e. the .words element) will have a width that will depend on the width of the active word. The next word will appear smoothly from bottom to top and then move to the top to disappear.

How each word is placed in the containerHow each word is placed in the containerHow each word is placed in the container

On small screens where the text splits into more than one line, to avoid line flickering that may occur depending on the active word’s width, we'll set all words’ widths equal to the width of the largest word. In this case, we’ll use the !important keyword to override the width of the .words element that's set via the width CSS variable.

Here are all the required styles:

1
.words-wrapper {
2
  font-size: 40px;
3
  font-weight: bold;
4
  text-align: center;
5
}
6
7
.words-wrapper .css {
8
  color: #2ec4b6;
9
}
10
11
.words-wrapper .words {
12
  display: inline-block;
13
  position: relative;
14
  vertical-align: bottom;
15
  width: var(--width);
16
  height: 60px;
17
  padding: 0 10px;
18
  border-radius: 6px;
19
  color: var(--color, #000);
20
  background: var(--color-bg, #ffc703);
21
  box-sizing: content-box;
22
  transition: all 0.7s;
23
}
24
25
.words-wrapper .words span {
26
  position: absolute;
27
  top: 0;
28
  left: 50%;
29
  opacity: 0;
30
  transform: translate(-50%, -100%);
31
  transition: transform 0.7s, opacity 0.25s 0.25s;
32
}
33
34
.words-wrapper .words span.current {
35
  opacity: 1;
36
  transform: translate(-50%, 0);
37
}
38
39
.words-wrapper .words span.next {
40
  transform: translate(-50%, 100%);
41
}
42
43
@media (max-width: 700px) {
44
  .words-wrapper .words {
45
    width: var(--width-mobile) !important;
46
  }
47
}

When the DOM is ready, we’ll specify the initial width of the .words wrapper by updating the values of the width and width-mobile CSS variable values.

Next, similar to the previous example, to show a different word after a certain amount of time, we’ll follow this approach:

  • Every 1.5 seconds, we’ll target the visible (active) and next active words.
  • Then, we’ll find the adjacent sibling word of the next active word if it exists, otherwise, we’ll get the first word.
  • We’ll remove the current class from the active word and the next class from the next active word.
  • We’ll add the current class to the next active word and the next class to its adjacent sibling word.
  • Finally, we’ll grab the width and colors of the new active word and update the corresponding CSS variables.

Here’s the required JavaScript:

1
const wrapper = document.querySelector(".words");
2
const words = wrapper.querySelectorAll("span");
3
const currentWord = wrapper.querySelector("span.current");
4
const wordsWidths = Array.from(words).map((word) => word.offsetWidth);
5
const maxWordsWidth = Math.max(...wordsWidths);
6
const CURRENT_CLASS = "current";
7
const NEXT_CLASS = "next";
8
9
wrapper.style.setProperty("--width", `${currentWord.offsetWidth}px`);
10
wrapper.style.setProperty("--width-mobile", `${maxWordsWidth}px`);
11
12
setInterval(() => {
13
  const currentWord = wrapper.querySelector("span.current");
14
  const nextWord = wrapper.querySelector("span.next");
15
  const nextNextWord = nextWord.nextElementSibling
16
    ? nextWord.nextElementSibling
17
    : wrapper.firstElementChild;
18
  currentWord.classList.remove(CURRENT_CLASS);
19
  nextWord.classList.remove(NEXT_CLASS);
20
  nextWord.classList.add(CURRENT_CLASS);
21
  nextNextWord.classList.add(NEXT_CLASS);
22
  wrapper.style.setProperty("--color", nextWord.dataset.color);
23
  wrapper.style.setProperty("--color-bg", nextWord.dataset.bgColor);
24
  wrapper.style.setProperty("--width", `${nextWord.offsetWidth}px`);
25
}, 1500);

I recommend you use the browser console to inspect the words and see how they behave. 

We'll end up with the following demo:

Here’s what we’re going to create. It’s a great addition to portfolio websites or hero sections to highlight things.

Conclusion

In this tutorial, we used CSS variables and JavaScript to build a text animation effect where certain words change after a specific period. This will hopefully inspire you to create even more exciting things by changing perhaps more than one word simultaneously, etc.


No comments:

Post a Comment