Last update: February 18, 2021

How to make an animated mobile menu with GSAP

One animation

This tutorial will show you how to create animated mobile menus with GSAP.
They are basic examples, that you can easily customize.
The first one uses the same GSAP tween for the entrance and exit animation. It is just reversed when we close the overlay.

The Overlay

First, let's add a Div, that will cover the entire viewport. Change his ID to #overlay.

Set his position to fixed, change the z-index (so it will be on top of everything) and set the overflow to hidden:

Inside that Div, we add a menu, and an icon that will be use to close the overlay. The class name of the icon will be .close-icon

When it's done, set his visibility to hidden:

The Icon

Let's add an icon, ideally in the header but you can put it anywhere. It will be use to trigger the animation, so we will add the class .open-icon.

The Animation

For this first example, we just create a simple entrance animation:

gsap.set("#overlay", { y: "100vh" });

var overlayAnim = gsap.to("#overlay", {
    duration: 1,
    ease: "power4.out",
    y: 0,
    autoAlpha: 1,
    paused: true
});

What we do here, it's to set the vertical position of the overlay to 100vh, in order to hide it first.
And we will simply move it to y:0 (his original position).

Note the paused property. It's set to true, so it won't run automatically.

The Code

Add this Javascript in a Code Block:

(function($) {
	if(window.angular) return;
  	var isOpen = false;

	// Set the GSAP animation
    gsap.set("#overlay", { y: "100vh" });
    var overlayAnim = gsap.to("#overlay", {
        duration: 1,
        ease: "power4.out",
        y: 0,
        autoAlpha: 1,
        paused: true
    });

	// Play the animation on click
    $('.open-icon, .close-icon').on("click", function() {
        isOpen = !isOpen; //toggle

      	if (isOpen) {
            overlayAnim.play();
        } else {
            overlayAnim.reverse();
        }
    });
})(jQuery);

What does it do ?

  • if(window.angular) return;
    This line means that the following code won't be execute inside Oxygen. It can become quickly messy (and buggy) when you run some JS code inside the editor, especially when there are some animations.
  • We set a variable isOpen to false.
  • We create the GSAP animation.
  • We add an onClick event on the icons.
    Each time we click on one of the icon, the variable isOpen will turn to true, then false, then true, and so on.
    If isOpen is true, then we play the animation
    If isOpen is false, we reverse the animation.

The Menu

Now it's time to animate the menu. We just create a simple stagger animation:

gsap.fromTo("#overlay nav a", {
    xPercent: -100,
    opacity: 0
}, {
    duration: 2,
    opacity: 1,
    xPercent: 0,
    stagger: 0.1,
    ease: "power4.out"
});

More info about the stagger property : how to create animated sequences with GSAP

The full code

Here is the full code of the first example (top of this page).

(function($) {
    if (window.angular) return;
    var isOpen = false;

    gsap.set("#overlay", { y: "100vh" });
    var overlayAnim = gsap.to("#overlay", {
        duration: 1,
        ease: "power4.out",
        y: 0,
        autoAlpha: 1,
        paused: true
    });


    $(".open-icon, .close-icon").on("click", function() {
        isOpen = !isOpen; //toggle

        if (isOpen) {
            overlayAnim.play();

            gsap.fromTo("#overlay nav a", {
                xPercent: -100,
                opacity: 0
            }, {
                duration: 2,
                opacity: 1,
                xPercent: 0,
                stagger: 0.1,
                ease: "power4.out",
            });

        } else {
            overlayAnim.reverse();
        }
    });
})(jQuery);
Two animations

Two animations

In this example, we have a different animation for the entrance and for the exit.

(function($) {
    if (window.angular) return;
    var isOpen = false;

    var entrance = gsap.fromTo("#overlay", {
        x: 0,
        y: "100vh"
    }, {
        duration: 2,
        ease: "elastic.out(1, 0.3)",
        x: 0,
        y: 0,
        autoAlpha: 1,
        paused: true,
    });


    var exit = gsap.to("#overlay", {
        duration: .8,
        ease: "power4.in",
        x: "100vw",
        autoAlpha: 0,
        paused: true
    });

    $(".open-icon, .close-icon").on("click", function() {

        isOpen = !isOpen; //toggle

        if (isOpen) {
            entrance.restart();

            gsap.fromTo("#overlay nav a", {
                xPercent: -100,
                opacity: 0
            }, {
                duration: 2,
                opacity: 1,
                xPercent: 0,
                stagger: 0.1,
                ease: "elastic.out(1, 0.3)",
            });

        } else {
            exit.restart();
        }
    });
})(jQuery);

Tips

You might want to disable the page scroll when the menu is open.
This lib will do the job for you : Body Scroll Lock

My main menu has a hash link (Contact), that targets the footer of the current page.
In order to close the menu when we click on that link, we can add this code :

$("#overlay nav a[href^='#']").on("click", function() {
    isOpen = !isOpen; //toggle
    exit.restart();
});
closealign-justifyarrow-rightchevron-downbarscaret-up