Creating an Animated Paper Plane Button Using HTML, CSS, and JavaScript

Creating an Animated Paper Plane Button Using HTML, CSS, and JavaScript

Introduction: As part of my journey through the #100DaysOfCode Challenge, I recently worked on an exciting project for Day 25. In this project, I created an animated paper plane button using HTML, CSS, JavaScript, and GSAP (GreenSock Animation Platform). This interactive button adds a fun and engaging element to web pages, making it perfect for various applications such as form submissions or call-to-action buttons.

Step 1: Setting Up the Project To begin, download the full source code from the provided link: Download Source Code. Once downloaded, extract the files to your project directory.

Step 2: HTML Structure Open the HTML file and set up the basic structure. This includes defining the button element and its various spans for text and SVG icons. Ensure to link the external CSS and JavaScript files.

<!DOCTYPE html>
<html lang="en">

<head>
    <!-- Define character encoding and viewport for responsive design -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- Link to external stylesheet -->
    <link rel="stylesheet" href="style.css" />
    <!-- Title of the webpage -->
    <title>Animated Send Button With Paper Plane Animation</title>
</head>

<body>
    <!-- Button element -->
    <button class="button">
        <!-- Default button text -->
        <span class="default">Send Now</span>
        <!-- Success button text with SVG icon -->
        <span class="success">
            <svg viewBox="0 0 16 16">
                <polyline points="3.75 9 7 12 13 5"></polyline>
            </svg>Sent
        </span>
        <!-- SVG for animation trails -->
        <svg class="trails" viewBox="0 0 33 64">
            <!-- Path for animation trails -->
            <path d="M26,4 C28,13.3333333 29,22.6666667 29,32 C29,41.3333333 28,50.6666667 26,60"></path>
            <path d="M6,4 C8,13.3333333 9,22.6666667 9,32 C9,41.3333333 8,50.6666667 6,60"></path>
        </svg>
        <!-- Paper plane animation div -->
        <div class="plane">
            <!-- Left wing of the paper plane -->
            <div class="left"></div>
            <!-- Right wing of the paper plane -->
            <div class="right"></div>
        </div>
    </button>
</body>

<!-- Link to external JavaScript files -->
<script src="script.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"
    integrity="sha512-16esztaSRplJROstbIIdwX3N97V1+pZvV33ABoG1H2OyTttBxEGkTsoIVsiP1iaTtM8b3+hu2kB6pQ4Clr5yug=="
    crossorigin="anonymous" referrerpolicy="no-referrer"></script>

</html>

Step 3: CSS Styling Open the style.css file and define the styles for the button and its animations. This includes setting custom properties for various button states and animation effects.

/* Define custom properties for button styling */
.button {
    --primary: #f6f8ff;
    --primary-dark: #d1d6ee;
    --primary-darkest: #8a91b4;
    --shadow: rgba(0, 0, 0, 0.3);
    --text: #362a89;
    --text-opacity: 1;
    --success: #eeecff;
    --success-x: -12;
    --success-stroke: 14;
    --success-opacity: 0;
    --border-radius: 7;
    --overflow: hidden;
    --x: 0;
    --y: 0;
    --rotate: 0;
    --plane-x: 0;
    --plane-y: 0;
    --plane-opacity: 1;
    --trails: rgba(255, 255, 255, 0.15);
    --trails-stroke: 57;
    --left-wing-background: var(--primary);
    --left-wing-first-x: 0;
    --left-wing-first-y: 0;
    --left-wing-second-x: 50;
    --left-wing-second-y: 0;
    --left-wing-third-x: 0;
    --left-wing-third-y: 100;
    --left-body-background: var(--primary);
    --left-body-first-x: 51;
    --left-body-first-y: 0;
    --left-body-second-x: 51;
    --left-body-second-y: 100;
    --left-body-third-x: 0;
    --left-body-third-y: 100;
    --right-wing-background: var(--primary);
    --right-wing-first-x: 49;
    --right-wing-first-y: 0;
    --right-wing-second-x: 100;
    --right-wing-second-y: 0;
    --right-wing-third-x: 100;
    --right-wing-third-y: 100;
    --right-body-background: var(--primary);
    --right-body-first-x: 49;
    --right-body-first-y: 0;
    --right-body-second-x: 49;
    --right-body-second-y: 100;
    --right-body-third-x: 100;
    --right-body-third-y: 100;
    /* Button base styles */
    display: block;
    cursor: pointer;
    position: relative;
    border: 0;
    padding: 8px 0;
    min-width: 100px;
    text-align: center;
    margin: 0;
    line-height: 24px;
    font-family: sans-serif;
    font-weight: 600;
    font-size: 14px;
    background: none;
    outline: none;
    color: var(--text);
    -webkit-appearance: none;
    -webkit-tap-highlight-color: transparent;
}

/* Styles for paper plane animation and trails */
.button .plane,
.button .trails {
    pointer-events: none;
    position: absolute;
}

/* Styles for paper plane animation */
.button .plane {
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    filter: drop-shadow(0 3px 6px var(--shadow));
    transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)) rotate(calc(var(--rotate) * 1deg)) translateZ(0);
}

/* Styles for paper plane wings */
.button .plane .left,
.button .plane .right {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    opacity: var(--plane-opacity);
    transform: translate(calc(var(--plane-x) * 1px), calc(var(--plane-y) * 1px)) translateZ(0);
}

/* Styles for paper plane wing elements */
.button .plane .left:before,
.button .plane .left:after,
.button .plane .right:before,
.button .plane .right:after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    border-radius: calc(var(--border-radius) * 1px);
    transform: translate(var(--part-x, 0.4%), var(--part-y, 0)) translateZ(0);
    z-index: var(--z-index, 2);
    background: var(--background, var(--left-wing-background));
    -webkit-clip-path: polygon(calc(var(--first-x, var(--left-wing-first-x)) * 1%) calc(var(--first-y, var(--left-wing-first-y)) * 1%), calc(var(--second-x, var(--left-wing-second-x)) * 1%) calc(var(--second-y, var(--left-wing-second-y)) * 1%), calc(var(--third-x, var(--left-wing-third-x)) * 1%) calc(var(--third-y, var(--left-wing-third-y)) * 1%));
    clip-path: polygon(calc(var(--first-x, var(--left-wing-first-x)) * 1%) calc(var(--first-y, var(--left-wing-first-y)) * 1%), calc(var(--second-x, var(--left-wing-second-x)) * 1%) calc(var(--second-y, var(--left-wing-second-y)) * 1%), calc(var(--third-x, var(--left-wing-third-x)) * 1%) calc(var(--third-y, var(--left-wing-third-y)) * 1%));
}

/* Styles for left wing */
.button .plane .left:after {
    --part-x: -1%;
    --z-index: 1;
    --background: var(--left-body-background);
    --first-x: var(--left-body-first-x);
    --first-y: var(--left-body-first-y);
    --second-x: var(--left-body-second-x);
    --second-y: var(--left-body-second-y);
    --third-x: var(--left-body-third-x);
    --third-y: var(--left-body-third-y);
}

/* Styles for left wing */
.button .plane .right:before {
    --part-x: -1%;
    --z-index: 2;
    --background: var(--right-wing-background);
    --first-x: var(--right-wing-first-x);
    --first-y: var(--right-wing-first-y);
    --second-x: var(--right-wing-second-x);
    --second-y: var(--right-wing-second-y);
    --third-x: var(--right-wing-third-x);
    --third-y: var(--right-wing-third-y);
}

/* Styles for right wing element */
.button .plane .right:after {
    --part-x: 0;
    --z-index: 1;
    --background: var(--right-body-background);
    --first-x: var(--right-body-first-x);
    --first-y: var(--right-body-first-y);
    --second-x: var(--right-body-second-x);
    --second-y: var(--right-body-second-y);
    --third-x: var(--right-body-third-x);
    --third-y: var(--right-body-third-y);
}

/* Styles for animation trails */
.button .trails {
    display: block;
    width: 33px;
    height: 64px;
    top: -4px;
    left: 16px;
    fill: none;
    stroke: var(--trails);
    stroke-linecap: round;
    stroke-width: 2;
    stroke-dasharray: 57px;
    stroke-dashoffset: calc(var(--trails-stroke) * 1px);
    transform: rotate(68deg) translateZ(0);
}

/* Styles for button text */
.button span {
    display: block;
    position: relative;
    z-index: 4;
    opacity: var(--text-opacity);
}

/* Styles for success message */
.button span.success {
    z-index: 0;
    position: absolute;
    left: 0;
    right: 0;
    top: 8px;
    transform: translateX(calc(var(--success-x) * 1px)) translateZ(0);
    opacity: var(--success-opacity);
    color: var(--success);
}

/* Styles for success icon */
.button span.success svg {
    display: inline-block;
    vertical-align: top;
    width: 16px;
    height: 16px;
    margin: 4px 8px 0 0;
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 14px;
    stroke: var(--success);
    stroke-dashoffset: calc(var(--success-stroke) * 1px);
}

/* Global styles */
html {
    box-sizing: border-box;
    -webkit-font-smoothing: antialiased;
}

* {
    box-sizing: inherit;
}

*:before,
*:after {
    box-sizing: inherit;
}

body {
    overflow: hidden;
    min-height: 100vh;
    font-family: sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

Step 4: JavaScript Logic Open the script.js file and write the JavaScript logic to handle button clicks and initiate animations using GSAP. Adding Animation Effects Implement animation effects using GSAP to create the paper plane animation, button transformation, and other interactive elements.

// Select all elements with the class "button" and apply event listeners and animations
document.querySelectorAll(".button").forEach((button) => {
  // Function to get computed CSS variable values
  let getVar = (variable) =>
    getComputedStyle(button).getPropertyValue(variable);

  // Add click event listener to each button
  button.addEventListener("click", (e) => {
    // Check if the button is not already active
    if (!button.classList.contains("active")) {
      // Add the "active" class to the button
      button.classList.add("active");

      // Animation using GSAP library
      gsap.to(button, {
        // Keyframes for animation
        keyframes: [
          {
            "--left-wing-first-x": 50,
            "--left-wing-first-y": 100,
            "--right-wing-second-x": 50,
            "--right-wing-second-y": 100,
            duration: 0.2,
            // Function executed after completing this keyframe
            onComplete() {
              // Set new CSS variables for paper plane transformation
              gsap.set(button, {
                "--left-wing-first-y": 0,
                "--left-wing-second-x": 40,
                "--left-wing-second-y": 100,
                "--left-wing-third-x": 0,
                "--left-wing-third-y": 100,
                "--left-body-third-x": 40,
                "--right-wing-first-x": 50,
                "--right-wing-first-y": 0,
                "--right-wing-second-x": 60,
                "--right-wing-second-y": 100,
                "--right-wing-third-x": 100,
                "--right-wing-third-y": 100,
                "--right-body-third-x": 60,
              });
            },
          },
          {
            "--left-wing-third-x": 20,
            "--left-wing-third-y": 90,
            "--left-wing-second-y": 90,
            "--left-body-third-y": 90,
            "--right-wing-third-x": 80,
            "--right-wing-third-y": 90,
            "--right-body-third-y": 90,
            "--right-wing-second-y": 90,
            duration: 0.2,
          },
          {
            "--rotate": 50,
            "--left-wing-third-y": 95,
            "--left-wing-third-x": 27,
            "--right-body-third-x": 45,
            "--right-wing-second-x": 45,
            "--right-wing-third-x": 60,
            "--right-wing-third-y": 83,
            duration: 0.25,
          },
          {
            "--rotate": 60,
            "--plane-x": -8,
            "--plane-y": 40,
            duration: 0.2,
          },
          {
            "--rotate": 40,
            "--plane-x": 45,
            "--plane-y": -300,
            "--plane-opacity": 0,
            duration: 0.375,
            // Function executed after completing this keyframe
            onComplete() {
              // Remove inline styles added during animation
              setTimeout(() => {
                button.removeAttribute("style");
                // Fade in button after animation completes
                gsap.fromTo(
                  button,
                  {
                    opacity: 0,
                    y: -8,
                  },
                  {
                    opacity: 1,
                    y: 0,
                    clearProps: true,
                    duration: 0.3,
                    // Function executed after completing this animation
                    onComplete() {
                      // Remove the "active" class from the button
                      button.classList.remove("active");
                    },
                  }
                );
              }, 1800);
            },
          },
        ],
      });

      // Additional animation for button appearance change
      gsap.to(button, {
        // Keyframes for animation
        keyframes: [
          {
            "--text-opacity": 0,
            "--border-radius": 0,
            "--left-wing-background": getVar("--primary-dark"),
            "--right-wing-background": getVar("--primary-dark"),
            duration: 0.11,
          },
          {
            "--left-wing-background": getVar("--primary"),
            "--right-wing-background": getVar("--primary"),
            duration: 0.14,
          },
          {
            "--left-body-background": getVar("--primary-dark"),
            "--right-body-background": getVar("--primary-darkest"),
            duration: 0.25,
            delay: 0.1,
          },
          {
            "--trails-stroke": 171,
            duration: 0.22,
            delay: 0.22,
          },
          {
            "--success-opacity": 1,
            "--success-x": 0,
            duration: 0.2,
            delay: 0.15,
          },
          {
            "--success-stroke": 0,
            duration: 0.15,
          },
        ],
      });
    }
  });
});

Conclusion: In this project, we successfully created an animated paper plane button using HTML, CSS, JavaScript, and GSAP. This project adds an interactive and visually appealing element to web pages, enhancing user experience and engagement. By following these step-by-step instructions, you can easily implement similar animations in your web projects. Stay tuned for more exciting projects as I continue my #100DaysOfCode Challenge!

Connect with Me: If you have any questions or want to discuss this project further, feel free to connect with me on Bento. Keep coding and stay inspired!

Did you find this article valuable?

Support Aarzoo by becoming a sponsor. Any amount is appreciated!