Shopify Section Banner Animations with GSAP


Ever wondered how you can make a custom section eye popping with custom animations? Enter GSAP (GreenSock Animation Platform), a JavaScript library renowned for its simplicity in creating captivating animations. Harnessing GSAP’s power, Shopify merchants can transform mundane banners into dynamic showcases of creativity. In this example, we use it to create a fun and playful carousel style section banner. A special thanks to Yudiz Solutions Limited for creating the beautiful codepen that this code is mostly from.

Github Link: https://github.com/ndrishinski/blogs/tree/master/section-banner-animations

GSAP Docs: https://gsap.com/docs/v3/

Prefer Video?

Enjoy this Youtube tutorial otherwise read on!

Load Third Party Libraries

Since we are utilizing two libraries in our code, we need to link to them in our theme.liquid file.

<head>
  ...
  <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>

The first is for the GSAP animation library, and the second is for font awesome which we will use for our button icons.

Add CSS File

There is a good chunk of CSS code we need to add to accomplish the look we are going for. Create a file in the assets folder called custom-banner.css and paste the following (follow link to Github here).

Create Section and Add HTML

Now under the sections folder, we can create a file called custom-banner.liquid and paste the following HTML:

{{ 'custom-banner.css' | asset_url | stylesheet_tag }} {% comment %} Reference to our stylesheet we created {% endcomment %}
<div class="container-all">
    <main>
        <div>
            <button style="z-index: 99" id="prevButton" class="wave"><i class="fa-solid fa-chevron-left"></i></button>
            <button style="z-index: 99" id="nextButton" class="wave"><i class="fa-solid fa-chevron-right"></i></button>
        </div>
        <div class="text">
            <h1 class="h1">{{section.settings.first_title }}</h1>
            <div class="cane-image">
                <img src="{{ section.settings.image_1 | image_url: width: 265 }}">
                <img src="{{ section.settings.image_2 | image_url: width: 795 }}" class="cane-labels">
            </div>
        </div>
        <div class="section-container-main">
            <div class="section-container">
              <section class="section" id="section1">
                <div class="fruit-images">
                  <div class="image-one fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/pear-one.png" alt="pear-image">
                  </div>
                  <div class="image-two fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/pear-two.png" alt="pear-image">
                  </div>
                  <div class="image-three fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/pear-three.png" alt="pear-image">
                  </div>
                  <div class="image-four fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/pear-four.png" alt="pear-image">
                  </div>
                </div>
              </section>
              <section class="section" id="section2">
                <div class="fruit-images">
                  <div class="image-one fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/apple-one.png" alt="apple-image">
                  </div>
                  <div class="image-two fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/apple-two.png" alt="apple-image">
                  </div>
                  <div class="image-three fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/apple-three.png" alt="apple-image">
                  </div>
                  <div class="image-four fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/apple-four.png" alt="apple-image">
                  </div>
                </div>
              </section>
              <section class="section" id="section3">
                <div class="fruit-images">
                  <div class="image-one fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/exotic-one.png" alt="exotic-image">
                  </div>
                  <div class="image-two fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/exotic-two.png" alt="exotic-image">
                  </div>
                  <div class="image-three fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/exotic-three.png" alt="exotic-image">
                  </div>
                  <div class="image-four fruit-image">
                    <img src="https://www.yudiz.com/codepen/fruity/exotic-four.png" alt="exotic-image">
                  </div>
                </div>
              </section>
            </div>
          </div>
    </main>
</div>

This is a big chunk of code but it should be pretty easy to follow along. First we define our buttons that will show up to move forward and backwards. Next we define our 2 main images that will be referencing images added via the theme customizer. The first is an SVG that makes the defining shape of our soda can, and the second image is the actual ‘cover’ of the can which is laid overtop. The rest of the HTML consists of the different sub sections containing the smaller floating images.

Add Javascript to Animate

Underneath the HTML from above we can add this script tag:

<script>
    document.addEventListener("DOMContentLoaded", () => {
        //............................................................... Script ...................................................................
        // Data for the sections
        let h1Texts = [
          "{{ section.settings.first_title }}",
          "{{ section.settings.second_title }}",
          "{{ section.settings.third_title }}", // We get these values from the schema / customizer
        ]; // Add your h1 texts here
        let logoColors = [
          "var(--pear-logo)",
          "var(--apple-logo)",
          "var(--exotic-logo)",
        ]; // Add your logo colors here
        let keyframes = [
          "wave-pear-effect",
          "wave-apple-effect",
          "wave-exotic-effect",
        ]; // Add your keyframes here
        // Normal GSAP animation.......
        gsap.from(".fruit-image ", { y: "-100vh", delay: 0.5 });
        gsap.to(".fruit-image img", {
          x: "random(-20, 20)",
          y: "random(-20, 20)",
          zIndex: 22,
          duration: 2,
          ease: "none",
          yoyo: true,
          repeat: -1,
        });
      
        // get the elements
        const waveEffect = document.querySelector(".wave");
        const sections = document.querySelectorAll(".section");
        const prevButton = document.getElementById("prevButton");
        const nextButton = document.querySelector("#nextButton");
        const caneLabels = document.querySelector(".cane-labels");
        const sectionContainer = document.querySelector(".section-container");
        // Set index and current position
        let index = 0;
        let currentIndex = 0;
        let currentPosition = 0;
      
        // Add event listeners to the buttons
        nextButton.addEventListener("click", () => {
          // Decrease the current position by 100% (to the left)
          if (currentPosition > -200) {
            currentPosition -= 100;
            // Update the left position of the cane-labels
            caneLabels.style.left = `${currentPosition}%`;
            sectionContainer.style.left = `${currentPosition}%`;
          }
          // Increment index and currentIndex
          currentIndex++;
          // Update the h1 text if currentIndex is less than the length of h1Texts
          if (currentIndex < h1Texts.length) {
            document.querySelector(".h1").innerHTML = h1Texts[currentIndex];
          }
          // Gasp animation for next section components
          gsap.to(".logo", {
            opacity: 1,
            duration: 1,
            color: logoColors[currentIndex],
          });
          gsap.from(".h1", { y: "20%", opacity: 0, duration: 0.5 });
          gsap.from(".fruit-image ", { y: "-100vh", delay: 0.4, duration: 0.4 });
      
          // Disable the nextButton if the last section is active
          if (currentIndex === h1Texts.length - 1) {
            nextButton.style.display = "none";
          }
          // Enable the prevButton if it's not the first section
          if (currentIndex > 0) {
            prevButton.style.display = "block";
          }
          // Button colors and animations
          nextButton.style.color = logoColors[currentIndex + 1];
          prevButton.style.color = logoColors[currentIndex - 1];
          nextButton.style.animationName = keyframes[currentIndex + 1];
          prevButton.style.animationName = keyframes[currentIndex - 1];
        });
        // Add event listeners to the buttons
        prevButton.addEventListener("click", () => {
          if (currentPosition < 0) {
            currentPosition += 100;
            // Update the left position of the cane-labels
            caneLabels.style.left = `${currentPosition}%`;
            sectionContainer.style.left = `${currentPosition}%`;
            sectionContainer.style.transition = `all 0.5s ease-in-out`;
          }
          // Decrement index and currentIndex
          currentIndex--;
          if (currentIndex >= 0) {
            document.querySelector(".h1").innerHTML = h1Texts[currentIndex];
          }
          // Gasp animation for previous section components
          gsap.to(".logo", { color: logoColors[currentIndex], duration: 1 });
          gsap.from(".h1", { y: "20%", opacity: 0, duration: 0.5 });
          gsap.from(".fruit-image ", { y: "100vh", delay: 0.5 });
          // Enable the nextButton if it was disabled
          nextButton.style.display = "block";
          // Disable the prevButton if it's the first section
          if (currentIndex === 0) {
            prevButton.style.display = "none";
          }
          // Button colors and animations
          nextButton.style.color = logoColors[currentIndex + 1];
          prevButton.style.color = logoColors[currentIndex - 1];
          nextButton.style.animationName = keyframes[currentIndex + 1];
          prevButton.style.animationName = keyframes[currentIndex - 1];
        });
      });
</script>

There is a lot of JS code to work through here. A way too short summary is that we define the values we want to show, create event listeners and elements to manipulate via query selectors, and animate via the GSAP library. I’d recommend reading through inline comments as well as checking out the GSAP documenation.

Add the Section Schema

Now we want to add the section schema so that our merchants can add the main text and images they would like to display front and center. Note that I am only adding 2 values here, but in reality you’d likely want to add a lot more configurable options to the schema so that the merchant can easily update images, animations, colors etc. Below the JavaScript add:


{% schema %}
  {
    "name": "Nicks-custom-banner",
    "tag": "section",
    "class": "section",
    "settings": [
      {
        "type": "image_picker",
        "id": "image_1",
        "label": "Add background SVG"
      },
      {
        "type": "image_picker",
        "id": "image_2",
        "label": "Add Main Image"
      },
      {
        "type": "text",
        "id": "first_title",
        "label": "First Slide Header"
      },
      {
        "type": "text",
        "id": "second_title",
        "label": "Second Slide Header"
      }, {
        "type": "text",
        "id": "third_title",
        "label": "Third Slide Header"
      }
    ]
  }
{% endschema %}

Now we can add this section to our index.json so it will appear when we visit our homepage.

{
    "sections": {
      "nicks-custom-banner": {
        "type": "custom-banner"
      },
      ...
    },
    "order": [
      "nicks-custom-banner",
      "featured_collection"
    ]
  }

Add Data via Customizer

Now if we save and view what we have currently, we should only see a styled background with some floating fruit and navigation arrows. Let’s go into our customizer and add some data. (To get the can images, download from the Codepen.)

Now when you save and refresh, you should have a stunning animated section banner displaying a soda can and some floating fruit!

Conclusion

I hope you enjoyed this tutorial on utilizing GSAP within Shopify for a custom section banner with animations. A special thanks to Yudiz Solutions Limited for creating the beautiful codepen that this code is mostly from. Please let me know in the comments what you’d like to see build in Shopify.