Search
Close this search box.
Search
Close this search box.

Confetti Button Using JavaScript

Confetti Button

Table of Contents

Confetti Button Using JavaScript

Hello Coder, today we’re going to learn how to Create a Confetti Button. This button can be use as a playful design choice that adds a great user experience to the website.

What is a Confetti Button?

Generally speaking, a virtual button or feature seen on websites, applications, or digital platforms is referred to as a “confetti button”. After activation, an animated celebration occurs, featuring fictitious confetti falling or exploding over the screen.

HTML

We can break down the HTML code into three sections to better understand it.

Let’s Break –

Button Element

				
					<button id="button" class="ready" onclick="clickButton();">
				
			

This button element has the class “ready” and ID “button”. The JavaScript function “clickButton()” is called when the button is clicked, according to the setting for the “onclick” property.

Message Division within the Button

				
					<div class="message submitMessage">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.2">
        <polyline stroke="currentColor" points="2,7.1 6.5,11.1 11,7.1 "/>
        <line stroke="currentColor" x1="6.5" y1="1.2" x2="6.5" y2="10.3"/>
      </svg> <span class="button-text">Submit</span>
    </div>
    
    <div class="message loadingMessage">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 17">
        <circle class="loadingCircle" cx="2.2" cy="10" r="1.6"/>
        <circle class="loadingCircle" cx="9.5" cy="10" r="1.6"/>
        <circle class="loadingCircle" cx="16.8" cy="10" r="1.6"/>
      </svg>
    </div>
    
    <div class="message successMessage">
      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 11">
        <polyline stroke="currentColor" points="1.4,5.8 5.1,9.5 11.6,2.1 "/>
      </svg> <span class="button-text">Success</span>
    </div>

				
			

Three “” elements and three different states are shown by classes on the button:

“Submit Message”

“Message Loading”

“Success Message”

Every state has a text and SVG icon associated with it.

Canvas Element

				
					<canvas id="canvas"></canvas>
				
			

The ID “canvas” is attached to an HTML5 “” element. This canvas is probably used to display JavaScript-based confetti animation.

CSS

To create the confetti button and add animations, we can divide the CSS code into ten sections.
Let’s Start –

CSS Variable

				
					:root{
  --bgColor: #f4f7ff;
  --btnTxtColor: #f4f7ff;
  --btnBgPreColor: #1f2335;
  --btnSubmitColor: #5c86ff;
  --successColor: #5cffa1;
  --loadingColor: #5c86ff;
}
				
			

These are placeholder values for color that make it simple to change for aesthetically pleasing color schemes.

KeyFrames Animation

				
					@keyframes loading {
  0% {
    cy: 10;
  }
  25% {
    cy: 3;
  }
  50% {
    cy: 10;
  }
}

				
			

In this case, we build a keyframe animation called “loading” that controls the SVG circle’s vertical position to simulate the loading effect.

Body and Canvas Style

				
					body {
  -webkit-font-smoothing: antialiased;
  background-color: var(--bgColor);
}

canvas {
  height: 100vh;
  pointer-events: none;
  position: fixed;
  width: 100%;
  z-index: 2;
}

				
			

Here, we specify the body’s background color and offer confetti animation canvas styles, including position and size.

Button Styling

				
					button {
  background: none;
  border: none;
  color: var(--btnTxtColor);
  cursor: pointer;
  font-family: "Quicksand", sans-serif;
  font-size: 14px;
  font-weight: 500;
  height: 40px;
  left: 50%;
  outline: none;
  overflow: hidden;
  padding: 0 10px;
  position: fixed;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 190px;
  -webkit-tap-highlight-color: transparent;
  z-index: 1;
}
button::before {
  background: var(--btnBgPreColor);
  border-radius: 50px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4) inset;
  content: "";
  display: block;
  height: 100%;
  margin: 0 auto;
  position: relative;
  transition: width 0.2s cubic-bezier(0.39, 1.86, 0.64, 1) 0.3s;
  width: 100%;
}

				
			

Here, we specify the styles for the primary button and all of its auxiliary elements, such as transitions, sizes, and states.

Button States Styling

				
					button.ready .submitMessage svg {
  opacity: 1;
  top: 1px;
  transition: top 0.4s ease 600ms, opacity 0.3s linear 600ms;
}
button.ready .submitMessage .button-text span {
  top: 0;
  opacity: 1;
  transition: all 0.2s ease calc(var(--dr) + 600ms);
}

button.loading::before {
  transition: width 0.3s ease;
  width: 80%;
}
button.loading .loadingMessage {
  opacity: 1;
}
button.loading .loadingCircle {
  animation-duration: 1s;
  animation-iteration-count: infinite;
  animation-name: loading;
  cy: 10;
}

button.complete .submitMessage svg {
  top: -30px;
  transition: none;
}
button.complete .submitMessage .button-text span {
  top: -8px;
  transition: none;
}
button.complete .loadingMessage {
  top: 80px;
}
button.complete .successMessage .button-text span {
  left: 0;
  opacity: 1;
  transition: all 0.2s ease calc(var(--d) + 1000ms);
}
button.complete .successMessage svg {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.3s ease-in-out 1.4s;
}

				
			

Here, we offer styles for the “ready,” “loading,” and “success” stages of the button. In every state, specific styles are offered for SVG icons, loading animations, and other visual components.

Text and Messaging Styling

				
					.button-text span {
  opacity: 0;
  position: relative;
}

.message {
  left: 50%;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 100%;
}

.message svg {
  display: inline-block;
  fill: none;
  margin-right: 5px;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 2;
}

				
			

In this case, we specify the styles for the SVG icons and message containers, as well as the text included within the button.

Submit Message Style

				
					.submitMessage .button-text span {
  top: 8px;
  transition: all 0.2s ease var(--d);
}
.submitMessage svg {
  color: var(--btnSubmitColor);
  margin-left: -1px;
  opacity: 0;
  position: relative;
  top: 30px;
  transition: top 0.4s ease, opacity 0.3s linear;
  width: 14px;
}

				
			

Here we define –

  • A span element for submitting a message is located within the “Submit Message” section, eight pixels from the top. It transitions from a hidden to visible state using an ease-in-out timing function that is indicated by the CSS character.
  • The color of the SVG icon within the “Submit Message” is determined by the variable “btnSubmitColor”. It begins at 30 pixels from the top with 0 opacity and gradually changes in both opacity and top position.

Loading Message Style

				
					.loadingMessage {
  opacity: 0;
  transition: opacity 0.3s linear 0.3s, top 0.4s cubic-bezier(0.22, 0, 0.41, -0.57);
}
.loadingMessage svg {
  fill: var(--loadingColor);
  margin: 0;
  width: 22px;
}

				
			

Here we define –

  • It begins with 0 opacity inside the “Loading Message” container and changes linearly over the next 0.3 seconds, delaying the change by 0.3 seconds. Its top location also undergoes a precise time effect, transitioning from a cubic-bezier interpolation for 0.4 seconds.
  • The color given by the variable “loadingColor” fills the SVG icon within the “Loading Message”. It is set to a width of 22 pixels and has no margin.

Success Message Style

				
					.successMessage svg {
  color: var(--successColor);
  stroke-dasharray: 20;
  stroke-dashoffset: 20;
  transition: stroke-dashoffset 0.3s ease-in-out;
  width: 14px;
}

				
			

Here we define

  • The “Success Message” expands leftward, positioned five pixels from the left, and uses the CSS character “dr” to specify the timing function for the change from hidden to visible.
  • The color supplied by the variable “successColor” fills the SVG icon within the “Success Message”. When using an ease-in-out timing function, a stroke-dashoffset transition takes longer than 0.3 seconds. Initially, both stroke-dasharray and stroke-dashoffset are set to 20.

Animation Delay

				
					.loadingCircle:nth-child(2) {
  animation-delay: 0.1s;
}

.loadingCircle:nth-child(3) {
  animation-delay: 0.2s;
}
				
			

In order to produce a sequential effect, we apply animation delays for particular loading circles in this particular case. This makes the loading animation appear sequential.

JavaScript

To make an interactive confetti button, we can separate the JavaScript code into its various parts.

Let’s start – 

Initialization and Constants

Constant confettiCount = 20

Constant sequenceCount = 10

Constant gravityConfetti = 0.3

Constant gravitySequence = 0.55

Constant dragConfetti = 0.075

Constant dragSequence = 0.02

Constant terminalVelocity = 3

Here, we define constant variables that establish the confetti and sequence start setups. Here, a number of physical-related factors are defined, including gravity, drag, and terminal velocity for both sequence and confetti.

Global Elements and Canvas Setup

				
					const button = document.getElementById('button')
var disabled = false
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
canvas.width = window.innerWidth
canvas.height = window.innerHeight
let cx = ctx.canvas.width / 2
let cy = ctx.canvas.height / 2

				
			

Global components like the button and canvas are initialized here. After sizing the canvas to fill the window, a drawing reference (ctx) is acquired.

Confetti and Sequin Setup

				
					let confetti = []
let sequins = []

const colors = [
  { front : '#7b5cff', back: '#6245e0' }, // Purple
  { front : '#b3c7ff', back: '#8fa5e5' }, // Light Blue
  { front : '#5c86ff', back: '#345dd1' }  // Darker Blue
] 
				
			

In this case, instances of the confetti and sequence objects are stored in arrays called “confetti” and “sequence,” respectively. Furthermore, a color spectrum is specified, outlining the forward and backward colors for confetti.

Helper Function

				
					randomRange = (min, max) => Math.random() * (max - min) + min

// helper function to get initial velocities for confetti
// this weighted spread helps the confetti look more realistic
initConfettoVelocity = (xRange, yRange) => {
  const x = randomRange(xRange[0], xRange[1])
  const range = yRange[1] - yRange[0] + 1
  let y = yRange[1] - Math.abs(randomRange(0, range) + randomRange(0, range) - range)
  if (y >= yRange[1] - 1) {
    // Occasional confetto goes higher than the max
    y += (Math.random() < .25) ? randomRange(1, 3) : 0
  }
  return {x: x, y: -y}
}

				
			

Helper functions are defined here. While “initConfettoVelocity” calculates initial velocities for confetti and offers charged spread for a more realistic presence, “randomRange” generates a random number within a defined range.

Confetto Class

				
					function Confetto() {
  this.randomModifier = randomRange(0, 99)
  this.color = colors[Math.floor(randomRange(0, colors.length))]
  this.dimensions = {
    x: randomRange(5, 9),
    y: randomRange(8, 15),
  }
  this.position = {
    x: randomRange(canvas.width/2 - button.offsetWidth/4, canvas.width/2 + button.offsetWidth/4),
    y: randomRange(canvas.height/2 + button.offsetHeight/2 + 8, canvas.height/2 + (1.5 * button.offsetHeight) - 8),
  }
  this.rotation = randomRange(0, 2 * Math.PI)
  this.scale = {
    x: 1,
    y: 1,
  }
  this.velocity = initConfettoVelocity([-9, 9], [6, 11])
}
Confetto.prototype.update = function() {
  // apply forces to velocity
  this.velocity.x -= this.velocity.x * dragConfetti
  this.velocity.y = Math.min(this.velocity.y + gravityConfetti, terminalVelocity)
  this.velocity.x += Math.random() > 0.5 ? Math.random() : -Math.random()
  
  // set position
  this.position.x += this.velocity.x
  this.position.y += this.velocity.y

  // spin confetto by scaling y and set the color, .09 just slows cosine frequency
  this.scale.y = Math.cos((this.position.y + this.randomModifier) * 0.09)    
}

				
			

For the Confetto class, we define a constructor function here. Individual confetti pieces are represented by instances of Confetto, each of which has unique features like state, color, size, rotation, and velocity. The update approach takes care of the rotation and velocity aspects of confetti physics.

Sequin Class

				
					function Sequin() {
  this.color = colors[Math.floor(randomRange(0, colors.length))].back,
  this.radius = randomRange(1, 2),
  this.position = {
    x: randomRange(canvas.width/2 - button.offsetWidth/3, canvas.width/2 + button.offsetWidth/3),
    y: randomRange(canvas.height/2 + button.offsetHeight/2 + 8, canvas.height/2 + (1.5 * button.offsetHeight) - 8),
  },
  this.velocity = {
    x: randomRange(-6, 6),
    y: randomRange(-8, -12)
  }
}
Sequin.prototype.update = function() {
  // apply forces to velocity
  this.velocity.x -= this.velocity.x * dragSequins
  this.velocity.y = this.velocity.y + gravitySequins
  
  // set position
  this.position.x += this.velocity.x
  this.position.y += this.velocity.y   
}

				
			

The Sequence class has a constructor function, just like Confetto. Sequence instances are individual sequins, each of which is a unique sequin with characteristics like color, state, velocity, and radius. The update mechanism takes care of the sequins’ mechanics, including their speed.

Initialization Function

				
					initBurst = () => {
  for (let i = 0; i < confettiCount; i++) {
    confetti.push(new Confetto())
  }
  for (let i = 0; i < sequinCount; i++) {
    sequins.push(new Sequin())
  }
}

				
			

Render Function

				
					render = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height)
  
  confetti.forEach((confetto, index) => {
    let width = (confetto.dimensions.x * confetto.scale.x)
    let height = (confetto.dimensions.y * confetto.scale.y)
    
    // move canvas to position and rotate
    ctx.translate(confetto.position.x, confetto.position.y)
    ctx.rotate(confetto.rotation)

    // update confetto "physics" values
    confetto.update()
    
    // get front or back fill color
    ctx.fillStyle = confetto.scale.y > 0 ? confetto.color.front : confetto.color.back
    
    // draw confetto
    ctx.fillRect(-width / 2, -height / 2, width, height)
    
    // reset transform matrix
    ctx.setTransform(1, 0, 0, 1, 0, 0)

    // clear rectangle where button cuts off
    if (confetto.velocity.y < 0) {
      ctx.clearRect(canvas.width/2 - button.offsetWidth/2, canvas.height/2 + button.offsetHeight/2, button.offsetWidth, button.offsetHeight)
    }
  })

  sequins.forEach((sequin, index) => {  
    // move canvas to position
    ctx.translate(sequin.position.x, sequin.position.y)
    
    // update sequin "physics" values
    sequin.update()
    
    // set the color
    ctx.fillStyle = sequin.color
    
    // draw sequin
    ctx.beginPath()
    ctx.arc(0, 0, sequin.radius, 0, 2 * Math.PI)
    ctx.fill()

    // reset transform matrix
    ctx.setTransform(1, 0, 0, 1, 0, 0)

    // clear rectangle where button cuts off
    if (sequin.velocity.y < 0) {
      ctx.clearRect(canvas.width/2 - button.offsetWidth/2, canvas.height/2 + button.offsetHeight/2, button.offsetWidth, button.offsetHeight)
    }
  })

  // remove confetti and sequins that fall off the screen
  // must be done in seperate loops to avoid noticeable flickering
  confetti.forEach((confetto, index) => {
    if (confetto.position.y >= canvas.height) confetti.splice(index, 1)
  })
  sequins.forEach((sequin, index) => {
    if (sequin.position.y >= canvas.height) sequins.splice(index, 1)
  })

  window.requestAnimationFrame(render)
}

				
			

Here, we define the “render” function that is in responsible for rendering sequins and confetti onto the canvas. It creates dynamic animations by utilizing the HTML5 canvas API. The function displays the sequins and confetti after clearing the canvas and updating their positions. It uses rotations and modifications to create a visually pleasing effect.

Button Click Function

				
					clickButton = () => {
  if (!disabled) {
    disabled = true
    // Loading stage
    button.classList.add('loading')
    button.classList.remove('ready')
    setTimeout(() => {
      // Completed stage
      button.classList.add('complete')
      button.classList.remove('loading')
      setTimeout(() => {
        window.initBurst()
        setTimeout(() => {
          // Reset button so user can select it again
          disabled = false
          button.classList.add('ready')
          button.classList.remove('complete')
        }, 4000)
      }, 320)
    }, 1800)
  }
}

				
			

Let’s define the function called “clickButton” now. This function mimics the event of a button click. The button is initially set to the loading state, then it moves through to the complete state, starts the confetti explosion, and then it resets itself to await user input.

Canvas Resize Function

				
					resizeCanvas = () => {
  canvas.width = window.innerWidth
  canvas.height = window.innerHeight
  cx = ctx.canvas.width / 2
  cy = ctx.canvas.height / 2
}

				
			

In order to modify the canvas size as the window resizes, we define the “resizeCanvas” function here.

Event Listeners

				
					window.addEventListener('resize', () => {
  resizeCanvas()
})

document.body.onkeyup = (e) => {
  if (e.keyCode == 13 || e.keyCode == 32) {
    clickButton()
  }
}

				
			

 Key presses and window resizing are handled by these event listeners. While the “clickButton” function is triggered by clicking the space bar or Enter, the “resizeCanvas” function activates when the window size is changed.

Text Transition Setup

				
					textElements = button.querySelectorAll('.button-text')
textElements.forEach((element) => {
  characters = element.innerText.split('')
  let characterHTML = ''
  characters.forEach((letter, index) => {
    characterHTML += `<span class="char${index}" style="--d:${index * 30}ms; --dr:${(characters.length - index - 1) * 30}ms;">${letter}</span>`
  })
  element.innerHTML = characterHTML
})


				
			

The button text’s transition times between characters are set in this area to produce a visually appealing text effect.

Text Transition Setup

				
					window.initBurst()
render()
				
			

Here, we define the script. “initBurst” is used to start the confetti and sequins, and render is used to start the animation loop. Confetti and sequins are constantly updated and shown on the canvas by the animation, creating a visually pleasing scene.

Output

See the Pen Confetti Button by Cooper Goeke (@coopergoeke) on CodePen.

You can visit our Codepen profile for source codes.

Conclusion

We’re going to finish this project with a fantastic confetti button that will encourage users to click and interact even more.

Download JavaScript Notes From HERE –

Facebook
Twitter
LinkedIn
Telegram
Email
WhatsApp

1 thought on “Confetti Button Using JavaScript”

  1. Pingback: Create Glow Flicker Using CSS - cssiseasy

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top