Last update: July 30, 2021

How to create your own Animated Headings animation with GSAP

This effect is quite nice pretty awesome just amazing !!!

Like usual, there are many ways to do this kind of effect. In this tutorial, we are going to use GSAP for his timeline feature, and some custom code.

The Structure

<h2>This effect is
<span class="heading-wrapper">
 <span class="heading-word">quite nice</span>
 <span class="heading-word">pretty awesome</span>
 <span class="heading-word">just amazing</span>
</span>
!!!</h2>

A lot of span for something so basic:

This effect is quite nice pretty awesome just amazing !!!

The CSS

We need a add some CSS to set the words at the right position.
Notice the transition, it will be used later.

.heading-wrapper {
	position:relative;
	display:inline-flex;
	overflow:visible;
  	transition:.3s ease width;
}
.heading-word {
	position:absolute;
	display:inline-block;
	white-space:nowrap;
}
.heading-word:first-child {
	position:relative;
}

So now it looks like this. It's ready for the animation.

This effect is quite nice pretty awesome just amazing !!!

The Animation

We need to create a timeline, so we can animate each word one by one.
Check this tutorial to learn more about how to create a timeline with GSAP : How to create animated sequences with GSAP

var animheading = gsap.timeline({
repeat: -1, defaults: { duration: 0.5, ease: "elastic.out(1,1)" }
});

animheading.fromTo(".heading-word:nth-child(1)",
    { yPercent: 200, opacity: 0 },
    { yPercent: 0, opacity: 1 })
.to(".heading-word:nth-child(1)",
    { opacity: 0, ease: "none", delay: 0.5 })

.fromTo(".heading-word:nth-child(2)",
    { yPercent: 200, opacity: 0 },
    { yPercent: 0, opacity: 1 })
.to(".heading-word:nth-child(2)",
    { opacity: 0, ease: "none", delay: 0.5 })

.fromTo(".heading-word:nth-child(3)",
    { yPercent: 200, opacity: 0 },
    { yPercent: 0, opacity: 1 })
.to(".heading-word:nth-child(3)",
    { opacity: 0, ease: "none", delay: 0.5 })

This effect is quite nice pretty awesome just amazing !!!

Optimization

Because we have the same entrance and exit animations for each word, we can optimize the code a little bit.
The following code will do exactly the same thing.
The difference is that we don't need to update the code if we add or remove some words.

var words = document.querySelectorAll(".heading-word");
var animheading = gsap.timeline({
repeat: -1, defaults: { duration: 0.5, ease: "elastic.out(1,1)" }});

words.forEach(function(element, index) {
    animheading.fromTo(element,
    { yPercent: 200, opacity: 0 },
    { yPercent: 0, opacity: 1 })
    
    animheading.to(element,
    { opacity: 0, ease: "none", delay: 0.5 })
})

Change the width

There is a little annoying problem here. The "!!!" just stays in place without moving and it doesn't look great.

So we are going to change dynamically the width of the container.

With GSAP, we can add our own function when the animation begins (the onStart property).
The variable newwidth will get the width of the current word, and then we apply this width to the container (.heading-wrapper).

Hence the transition:.3s ease width we have added in the CSS (so the width is animated too).

var words = document.querySelectorAll(".heading-word");
var animheading = gsap.timeline({
repeat: -1, defaults: { duration: 0.5, ease: "elastic.out(1,1)" }});

words.forEach(function(element, index) {
    animheading.fromTo(element,
    { yPercent: 200, opacity: 0 },
    { yPercent: 0, opacity: 1,
     
	onStart: function() {
	var newwidth = element.clientWidth;
	document.querySelector('.heading-wrapper').style.width=newwidth+"px";
    },
	})
    
  	animheading.to(element,
    { opacity: 0, ease: "none", delay: 0.5 })
})

This effect is quite nice pretty awesome just amazing !!!

Now you can create your own entrance and exit animations, and build your own custom animated headings effect.

closealign-justifychevron-downcaret-up