I recently had to create an invitation card online that required falling petals and a few other animations. I looked at a few options and the easiest one I found was using https://animejs.com/. The complete runnable source code is below:

The Source Code

<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>

<style>
petal {
  display: inline-block;
  width: 0; height: 0;
  padding: 20px 9px;
  background: rgba(0,0,100,.4);
  transform: rotate(72deg);
  border-top-left-radius: 100px;
  border-bottom-right-radius: 100px;
  background: rgba(245, 30, 188, 0.4);
  position:absolute;
}
</style>

</head>
<body>
<div id="petals">
</div>
<script>

/*
Falling petals
*/

var global_petal_count = 0;
var petal_count_per_wave = 15;
function create (){
  for(var i=0;i<petal_count_per_wave;i++){
    var petal_element = document.createElement("petal");
    petal_element.setAttribute("id", "petal_"+global_petal_count);
    petal_element.classList.add("slowfall");
    petal_element.style.top = Math.floor(Math.random() * 1000* -1);
    petal_element.style.left = Math.floor(Math.random() * 1500);
    document.getElementById("petals").appendChild(petal_element);
    global_petal_count++;
  }
  console.log("creating...");
}
function resolveLater() {
  create();
  var petal_animation = anime.timeline({
  easing: 'easeInOutSine',
  duration: 15000,
  autoplay: true
  });

  var petal_targets = [];

  for(var i=global_petal_count - petal_count_per_wave + 1;i<=global_petal_count; i++){
    petal_targets.push("#petal_"+i);
  }

  petal_animation
  .add({
    targets: petal_targets,
    translateY: 2000,
    rotate: 1000
  })
  .add({
    targets: petal_targets,
    opacity: 0,
  },500)

  return new Promise(resolve => {
    setTimeout(() => {
      resolve(resolveLater());
    }, 5000);
  });

}
async function asyncCall() {
  await resolveLater();
}
asyncCall();
</script>
</body>

</html>

Importing anime.js

The first part in the header is about importing anime.js. At the time of writing I was using version 3.2.1 so I hope by the time you try this it still works but if not feel free to make adjustments or use version 3.2.1

Defining a petal in CSS

What this basically does is to define the color of the petal through the css background color. Define it’s shape through the transforms. Setting it’s position to absolute so that it can overlay other divs later on. In case you have issues making the petals appear on top of other divs you might also think about adjusting the z-index of the petals.

Creating <petal>s in Javascript

Then in the create() function we are dynamically creating <petal></petal> elements. We are creating 15 which is defined by petal_count_per_wave then randomly place them at the top (top:0) and a random value of left. You might extend the range of the random to the width of your window with window. innerWidth.

How to create an infinite asynchronous loop in Javascript with promise

Next is the complicated part and probably something you could use for other projects. We are calling an asynchronous function asyncCall() which will wait for resolveLater() to finish.

At the end of resolveLater() you will see that we call a promise after 5 seconds timeout:

  return new Promise(resolve => {
    setTimeout(() => {
      resolve(resolveLater());
    }, 5000);
  });

In this way we can wait for enough time for the previous batch of petals to start falling before we call the next one. Also, because resolveLater is calling itself, this goes on forever.

How is anime.js involved in making the petals fall?

 var petal_animation = anime.timeline({
  easing: 'easeInOutSine',
  duration: 15000,
  autoplay: true
 });

var petal_targets = [];

  for(var i=global_petal_count - petal_count_per_wave + 1;i<=global_petal_count; i++){
    petal_targets.push("#petal_"+i);
  }

  petal_animation
  .add({
    targets: petal_targets,
    translateY: 2000,
    rotate: 1000
  })
  .add({
    targets: petal_targets,
    opacity: 0,
  },500)

This piece of code is building a list of petal ids that have just been created. This list is passed to petal_animation which is a timeline object from anime.js. The timeline basically says that the petals need to fall by 2000 pixels (TranslateY) and that after 500 milliseconds they need to start fading away to Opacity: 0. What this also means is that the objects still exist but are just invisible.

An improvement would be to perform a cleanup from time to time using the list of petals ids to delete them.

If you are uncertain about any part of the feel free to comment and I will try to assist. Cheers!