Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a sparkling button animation?

I just found a GitHub Repo, on which there is a cool sparking animation for a button, but for Android.

This is the Animation:

animation

However, I want that for my website, so I started designing that myself.

Firstly, I broke the gif down by frames. And then, by these points I made a road map, which is what I'm following while writing code:

  • The star gradually scale()s down.
  • A small circle of orange color gradually covers the star by scale()ing up.
  • Another small circle but this time of background-color (white, in this case), gradually covers the previous circle also by scale()ing up.
  • Along with that the star also gradually scale()s up again by reappearing (thus, I've to increase z-index), this time of orange color (Indicating its selected status).
  • There are another small circles too scattered from all sides of the new star.

This is what I've made so far:

svg {
  position: absolute;
  top: 0;
  width: 100px;
  height: 100px;
  transition: 0.5s;
  fill:gray;
}

svg:hover {
  animation: up-svg 1s;
  fill: darkorange;
  z-index: 1;
}

svg:hover~.svg {
  animation: up-one 0.5s;
  display: block;
}

svg:hover~.svg1 {
  animation: up-two 1s;
  display: block;
}

.svg {
  position: absolute;
  top: 0;
  border-radius: 50%;
  width: 100px;
  height: 100px;
  background-color: orange;
  display: none;
  transform: scale(0.9);
  transition: 0.5s;
}

.svg1 {
  position: absolute;
  top: 0;
  border-radius: 50%;
  width: 100px;
  height: 100px;
  background-color: white;
  display: none;
  transition: 0.5s;
}

@keyframes up-one {
  0% {
    transform: scale(0);
  }
  40% {
    transform: scale(0);
  }
  100% {
    transform: scale(0.9);
  }
}

@keyframes up-two {
  0% {
    transform: scale(0);
  }
  37.5% {
    transform: scale(0);
  }
  50% {
    transform: scale(0.25);
  }
  62.5% {
    transform: scale(0.5);
  }
  75% {
    transform: scale(0.75);
  }
  87.5% {
    transform: scale(0.9);
  }
  100% {
    transform: scale(1);
  }
}

@keyframes up-svg {
  0% {
    transform: scale(1);
    fill: gray;
    z-index: 0;
  }
  70% {
    transform: scale(0);
    fill: darkorange;
    z-index: 1;
  }
  100% {
    transform: scale(1);
    fill: darkorange;
    z-index: 1;
  }
}
<svg id="s-tt" class="s-icon" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M23.954 4.569c-.885.389-1.83.654-2.825.775 1.014-.611 1.794-1.574 2.163-2.723-.951.555-2.005.959-3.127 1.184-.896-.959-2.173-1.559-3.591-1.559-2.717 0-4.92 2.203-4.92 4.917 0 .39.045.765.127 1.124C7.691 8.094 4.066 6.13 1.64 3.161c-.427.722-.666 1.561-.666 2.475 0 1.71.87 3.213 2.188 4.096-.807-.026-1.566-.248-2.228-.616v.061c0 2.385 1.693 4.374 3.946 4.827-.413.111-.849.171-1.296.171-.314 0-.615-.03-.916-.086.631 1.953 2.445 3.377 4.604 3.417-1.68 1.319-3.809 2.105-6.102 2.105-.39 0-.779-.023-1.17-.067 2.189 1.394 4.768 2.209 7.557 2.209 9.054 0 13.999-7.496 13.999-13.986 0-.209 0-.42-.015-.63.961-.689 1.8-1.56 2.46-2.548l-.047-.02z"/></svg>
<div class="svg"></div>
<div class="svg1"></div>

I've tried my best, but still it is not looking like the expected one, and I was stuck with that circles scattering too.

Colors are not the priority right now. And I am sticking with the hover property (So that the animation can be seen again and again without reloading) for now until the animation is complete.

Can anyone help me make that kind of animation?

like image 210
Letsintegreat Avatar asked Feb 08 '19 13:02

Letsintegreat


Video Answer


1 Answers

Here is my idea where I will only rely on CSS and few element. I will detail each part alone then I will combine this into one animation.

For the icon part (the star) I would do the same but I will probably consider a grayscale filter to have a generic effect that work with any element and any color.

.magic i{
  color:red;
  filter:grayscale(100%);
}
.magic:hover i{
  animation:change 1s forwards;
}


@keyframes change{
  50% {
    transform:scale(0);
  filter:grayscale(100%);
  }
  51% {
    filter:grayscale(0%);
  }
  100% {
    transform:scale(1);
    filter:grayscale(0%);
  }
}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
<span class="magic">
<i class="fas fa-star fa-5x"></i>
</span>

For the circle I will consider only one element and the trick here is to rely on border coloration vs background coloration. We initially make the height/width to be 0 and we only have border thus it will be a full circle. Then we simply decrease the thickness of border while keeping the overall width the same. So we will do:

  1. Start form width/height 0 and border-width 0
  2. We increase border-width to create the scale effect
  3. We decrease the border-width while increasing width/height to keep the overall width/height the same.

.circle {
  display:inline-block;
  width:0px;
  height:0px;
  border-radius:50%;
  border-color:orange;
  border-style:solid;
  border-width:0px;
  box-sizing:border-box;
}

body:hover .circle {
  animation:change 1s forwards;
}

@keyframes change {
   50% {
    border-width:25px;
   }
   100% {
    border-width:0;
    width:50px;
    height:50px;
   }

}

body {
 min-height:100px;
}
<span class="circle"></span>

This solution has a small drawback as it will make the element to increase from the top left and not the center. We can correct this by using scale instead of changing width/height:

.circle {
  display:inline-block;
  width:50px;
  height:50px;
  border-radius:50%;
  border-color:orange;
  border-style:solid;
  border-width:25px;
  transform:scale(0);
  box-sizing:border-box;
}

body:hover .circle {
  animation:change 1s linear forwards;
}

@keyframes change {
   50% {
    transform:scale(1);
    border-width:25px;
   }
   100% {
    transform:scale(1);
    border-width:0;
   }

}

body {
 min-height:100px;
}
<span class="circle"></span>

We can still simplify considering a simple transition:

.circle {
  display:inline-block;
  width:50px;
  height:50px;
  border-radius:50%;
  border-color:orange;
  border-style:solid;
  border-width:25px;
  transform:scale(0);
  box-sizing:border-box;
  transition:
    transform 0.5s,
    border-width 0.5s 0.5s;
}

body:hover .circle {
  border-width:0;
  transform:scale(1);
}

body {
 min-height:100px;
}
<span class="circle"></span>

Now the tricky part and the small circles. For this I will rely on radial-gradient and scale. the idea is to create the small circles with gradient inside one element and using scale we will create the expanding effect.

.small {
  display:inline-block;
  width:100px;
  height:100px;
  background:
    /*4 reds*/
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    /*4 oranges*/
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%); 
  
  background-size:16px 16px; 
  background-position:
    calc(50% - 30px) calc(50% - 30px),
    calc(50% + 30px) calc(50% - 30px),
    calc(50% - 30px) calc(50% + 30px),
    calc(50% + 30px) calc(50% + 30px),
    calc(50% +  0px) calc(50% + 40px),
    calc(50% + 40px) calc(50% +  0px),
    calc(50% - 40px) calc(50% +  0px),
    calc(50% +  0px) calc(50% - 40px);
  background-repeat:no-repeat;
  border-radius:50%;
}
<span class="small"></span>

I have created 8 circles and placed them by offseting from the center (check this answer to get more details about how background-position works: https://stackoverflow.com/a/51734530/8620333). You simply need to adjust the size, position and color of the circle like you want.

And here is with animation:

.small {
  display:inline-block;
  width:100px;
  height:100px;
  background:
    /*4 reds*/
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    /*4 oranges*/
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%); 
  
  background-size:16px 16px;
  background-position:
    calc(50% - 30px) calc(50% - 30px),
    calc(50% + 30px) calc(50% - 30px),
    calc(50% - 30px) calc(50% + 30px),
    calc(50% + 30px) calc(50% + 30px),
    calc(50% +  0px) calc(50% + 40px),
    calc(50% + 40px) calc(50% +  0px),
    calc(50% - 40px) calc(50% +  0px),
    calc(50% +  0px) calc(50% - 40px);
  background-repeat:no-repeat;
  border-radius:50%;
  transform:scale(0);
  transition:transform 0.5s,opacity 0.4s 0.4s;
}

body {
 min-height:200px;
}
body:hover .small {
  transform:scale(1);
  opacity:0;
}
<span class="small"></span>

If you want a more accurate animation you can also consider decreasing the circles by reducing the background-size.

.small {
  display:inline-block;
  width:100px;
  height:100px;
  background:
    /*4 reds*/
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    /*4 oranges*/
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%); 
  
  background-size:16px 16px; /*at least 2x7px */
  background-position:
    calc(50% - 30px) calc(50% - 30px),
    calc(50% + 30px) calc(50% - 30px),
    calc(50% - 30px) calc(50% + 30px),
    calc(50% + 30px) calc(50% + 30px),
    calc(50% +  0px) calc(50% + 40px),
    calc(50% + 40px) calc(50% +  0px),
    calc(50% - 40px) calc(50% +  0px),
    calc(50% +  0px) calc(50% - 40px);
  background-repeat:no-repeat;
  border-radius:50%;
  transform:scale(0);
  transition:transform 0.5s,opacity 0.4s 0.4s,background-size 0.5s 0.4s;
}

body {
 min-height:200px;
}
body:hover .small {
  transform:scale(1);
  opacity:0;
  background-size:0 0;
}
<span class="small"></span>

Now you simply do the same with the other small circles by changing some of the values.

Let's put all this together!

.magic {
  display:inline-block;
  margin:50px;
  position:relative;
}

.magic i{
  color:orange;
  filter:grayscale(100%);
  position:relative;
}
.magic:hover i{
  animation:change 1s forwards;
}


@keyframes change{
  50% {
    transform:scale(0);
  filter:grayscale(100%);
  }
  51% {
    filter:grayscale(0%);
  }
  100% {
    transform:scale(1);
    filter:grayscale(0%);
  }
}
/**/

.magic:before {
  content:"";
  position:absolute;
  top:calc(50% - 45px);
  left:calc(50% - 45px);
  width:90px;
  height:90px;
  border-radius:50%;
  border-color:orange;
  border-style:solid;
  border-width:45px;
  transform:scale(0);
  box-sizing:border-box;
}

.magic:hover::before {
  transition:
    transform 0.5s,
    border-width 0.5s 0.5s;
  border-width:0;
  transform:scale(1);
}

/**/

.magic::after {
  content:"";
  position:absolute;
  width:160px;
  height:160px;
  left:calc(50% - 80px);
  top:calc(50% - 80px);
  background:
    /*4 reds*/
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    /*4 oranges*/
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%); 
  
  background-size:16px 16px; 
  background-position:
    calc(50% - 50px) calc(50% - 50px),
    calc(50% + 50px) calc(50% - 50px),
    calc(50% - 50px) calc(50% + 50px),
    calc(50% + 50px) calc(50% + 50px),
    calc(50% +  0px) calc(50% + 70px),
    calc(50% + 70px) calc(50% +  0px),
    calc(50% - 70px) calc(50% +  0px),
    calc(50% +  0px) calc(50% - 70px);
  background-repeat:no-repeat;
  border-radius:50%;
  transform:scale(0);
}

.magic:hover:after {
  transform:scale(1);
  opacity:0;
  background-size:0 0;
  transition:
    transform 0.5s 0.5s,
    opacity 0.4s 0.9s,
    background-size 0.5s 0.9s;
}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
<span class="magic">
<i class="fas fa-star fa-5x"></i>
</span>

<span class="magic">
<i class="fas fa-user fa-5x"></i>
</span>

As I said, it's not perfect but very close to what you want with less of element and with the needed details so you can easily adjust the different values. It's also easy to use with any icon since you only have to add a wrapper to your icon.

I didn't add the tiny circles for simplicity but we can consider another pseudo element and easily add them:

.magic {
  display:inline-block;
  margin:50px;
  position:relative;
}

.magic i{
  color:orange;
  filter:grayscale(100%);
}
.magic:hover i{
  animation:change 1s forwards;
}


@keyframes change{
  50% {
    transform:scale(0);
  filter:grayscale(100%);
  }
  51% {
    filter:grayscale(0%);
  }
  100% {
    transform:scale(1);
    filter:grayscale(0%);
  }
}
/**/

.magic:before {
  content:"";
  position:absolute;
  top:calc(50% - 45px);
  left:calc(50% - 45px);
  width:90px;
  height:90px;
  border-radius:50%;
  border-color:orange;
  border-style:solid;
  border-width:45px;
  transform:scale(0);
  box-sizing:border-box;
}

.magic:hover::before {
  border-width:0;
  transform:scale(1);
  transition:
    transform 0.5s,
    border-width 0.5s 0.5s;
}

/**/

.magic::after,
.magic i::after{
  content:"";
  position:absolute;
  width:160px;
  height:160px;
  left:calc(50% - 80px);
  top:calc(50% - 80px);
  background:
    /*4 reds*/
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    radial-gradient(circle,red 50%,transparent 60%),
    /*4 oranges*/
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%),
    radial-gradient(circle,orange 50%,transparent 60%); 
  
  background-size:16px 16px; 
  background-position:
    calc(50% - 50px) calc(50% - 50px),
    calc(50% + 50px) calc(50% - 50px),
    calc(50% - 50px) calc(50% + 50px),
    calc(50% + 50px) calc(50% + 50px),
    calc(50% +  0px) calc(50% + 70px),
    calc(50% + 70px) calc(50% +  0px),
    calc(50% - 70px) calc(50% +  0px),
    calc(50% +  0px) calc(50% - 70px);
  background-repeat:no-repeat;
  border-radius:50%;
  transform:scale(0);
}
.magic i::after {
  background-size:10px 10px;
  transform:rotate(10deg) scale(0);
}

.magic:hover:after {
  transform:scale(1);
  opacity:0;
  background-size:0 0;
  transition:transform 0.5s 0.5s,opacity 0.4s 0.9s,background-size 0.5s 0.9s;
}
.magic:hover i:after {
  transform:rotate(10deg) scale(1);
  opacity:0;
  background-size:0 0;
  transition:transform 0.5s 0.5s,opacity 0.4s 0.9s,background-size 0.5s 0.9s;
}

/**/
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
<span class="magic">
<i class="fas fa-star fa-5x"></i>
</span>

<span class="magic">
<i class="fas fa-user fa-5x"></i>
</span>

UPDATE

Here is an improvement of the code considering some CSS variable and using more flexbile unit to easily control the dimension:

.magic {
  display:inline-block;
  margin:50px;
  position:relative;
  --r:45px;
}

.magic i{
  color:orange;
  filter:grayscale(100%);
}
.magic:hover i{
  animation:change 1s forwards;
}


@keyframes change{
  50% {
   transform:scale(0);
   filter:grayscale(100%);
  }
  51% {
    filter:grayscale(0%);
  }
  100% {
    transform:scale(1);
    filter:grayscale(0%);
  }
}
/**/

.magic:before {
  content:"";
  position:absolute;
  top:calc(50% - var(--r));
  left:calc(50% - var(--r));
  width:calc(2*var(--r));
  height:calc(2*var(--r));
  border-radius:50%;
  border:solid orange var(--r);
  transform:scale(0);
  box-sizing:border-box;
}

.magic:hover::before {
  border-width:0;
  transform:scale(1);
  transition:
    transform 0.5s,
    border-width 0.5s 0.5s;
}

/**/

.magic::after,
.magic i::after{
  content:"";
  position:absolute;
  width: calc(4*var(--r));
  height:calc(4*var(--r));
  left:calc(50% - 2*var(--r));
  top: calc(50% - 2*var(--r));
  --c1:radial-gradient(circle,red 50%   ,transparent 60%);
  --c2:radial-gradient(circle,orange 50%,transparent 60%);
  background:
    /*4 reds*/
    var(--c1),var(--c1),var(--c1),var(--c1),
    /*4 oranges*/
    var(--c2),var(--c2),var(--c2),var(--c2); 
  
  background-size:calc(var(--r)/3) calc(var(--r)/3); 
  background-position:
    calc(50% - var(--r)) calc(50% - var(--r)),
    calc(50% + var(--r)) calc(50% - var(--r)),
    calc(50% - var(--r)) calc(50% + var(--r)),
    calc(50% + var(--r)) calc(50% + var(--r)),
    calc(50% +  0px) calc(50% + var(--r)*1.414),
    calc(50% + var(--r)*1.414) calc(50% +  0px),
    calc(50% - var(--r)*1.414) calc(50% +  0px),
    calc(50% +  0px) calc(50% - var(--r)*1.414);
  background-repeat:no-repeat;
  transform:scale(0);
}
.magic i::after {
  background-size:calc(var(--r)/5) calc(var(--r)/5);
  transform:rotate(55deg) scale(0);
}

.magic:hover:after {
  transform:scale(1);
  opacity:0;
  background-size:0 0;
  transition:
    transform 0.5s 0.5s,
    opacity 0.4s 0.9s,
    background-size 0.5s 0.9s;
}
.magic:hover i:after {
  transform:rotate(55deg) scale(1);
  opacity:0;
  background-size:0 0;
  transition:
    transform 0.5s 0.5s,
    opacity 0.4s 0.9s,
    background-size 0.5s 0.9s;
}

/**/
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
<span class="magic" style="--r:80px;">
<i class="fas fa-star fa-10x"></i>
</span>
<span class="magic">
<i class="fas fa-user fa-5x"></i>
</span>
<span class="magic" style="--r:20px;">
<i class="far fa-bell fa-3x"></i>
</span>

Basically the variable r will define the radius of the whole shape and you can easily changing based on the size of your icon.


Here is a Codepen demo if you want to play with the code

like image 67
Temani Afif Avatar answered Oct 08 '22 05:10

Temani Afif