Problem
I just came through a GitHub repo, which has an amazing animation for Button
in Android.
So, I thought why not make that for web buttons too.
And I started designed that.
Here is my repo for the same.
Here is my code:
let icons = $('.magic')
for (let i = 0; i < icons.length; ++i) {
icons[i].onclick = function (element) {
icons[i].classList.toggle('enabled');
}
}
.magic {
display: inline-block;
margin: 50px;
position: relative;
}
.magic i {
color: orange;
filter: grayscale(100%);
position: relative;
animation: disable 0.5s forwards;
}
.enabled i {
animation: enable 1s forwards;
}
.magic::before {
content: "";
top:calc(50% - 45px);
left:calc(50% - 45px);
width: 90px;
height: 90px;
position: absolute;
border-color: orange;
border-style: solid;
border-width: 45px;
border-radius: 50%;
transform: scale(0);
box-sizing: border-box;
}
.enabled::before {
transition:
transform 0.5s,
border-width 0.5s 0.2s;
transform: scale(1);
border-width: 0px;
}
.magic::after,
.magic i::after {
content: "";
position: absolute;
top: calc(50% - 80px);
left: calc(50% - 80px);
height: 160px;
width: 160px;
background:
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%),
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-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% - 0px) calc(50% + 70px),
calc(50% + 70px) calc(50% - 0px);
background-size: 16px 16px;
background-repeat: no-repeat;
border-radius:50%;
transform: scale(0);
}
.magic i:after {
background-size: 10px 10px;
transform: rotate(10deg) scale(0);
}
.enabled::after {
transition:
transform 0.5s 0.5s,
opacity 0.4s 1s,
background-size 0.4s 1s;
transform: scale(1);
opacity: 0;
background-size: 0 0;
}
.enabled i:after {
transition:
transform 0.5s 0.5s,
opacity 0.4s 0.8s,
background-size 0.4s 0.8s;
transform: rotate(10deg) scale(1);
opacity: 0;
background-size: 0 0;
}
@keyframes enable {
50% {
filter: grayscale(100%);
transform: scale(0);
}
51% {
filter: grayscale(0%)
}
90% {
transform: scale(1.3)
}
100% {
filter: grayscale(0%);
transform: scale(1);
}
}
@keyframes disable {
50% {
filter: grayscale(0%);
transform: scale(1.3);
}
100% {
transform: scale(1);
}
}
<span class="magic">
<i class="fas fa-star fa-5x"></i>
</span>
<span class="magic">
<i class="fas fa-bell fa-5x"></i>
</span>
<span class="magic">
<i class="fas fa-bolt fa-5x"></i>
</span>
<span class="magic">
<i class="fas fa-check fa-5x"></i>
</span>
<span class="magic">
<i class="fas fa-thumbs-up fa-5x"></i>
</span>
<!-- Other stuff -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css">
What do you reckon? Is there anything I can improve either in the animation or in the code?
Solution
I’d like to suggest the opposite of Sᴀᴍ Onᴇᴌᴀ: Since you are using jQuery for selecting only, you can drop it altogether.
const icons = $(".magic");
becomes either
const icons = document.getElementsByClassName("magic");
or
const icons = document.querySelectorAll(".magic");
However you shouldn’t use on...
properties to assign event handlers. on...
properties can only hold a single handler so if you or another script would attempt to assign another click handler then they’d overwrite each other. Instead use addEventListener
.
Alternatively to avoid assigning seperate event handlers to each icon you could use event delegation. This means assign an single event handler to a surrounding element (or simply document
) and check the target element:
document.addEventListener("click", function (event) {
if (event.target.classList.contains("magic")) {
event.target.classlist.toggle("enabled");
}
};
Finally you could avoid using JavaScript altogether by using an HTML element that has the toggle functionality built in: a checkbox:
<label class="magic-wrapper">
<input type="checkbox"><span class="magic"><i class="fas fa-star fa-5x"></i></span>
</label>
Hide the actual checkbox with:
.magic-wrapper > input {
opacity: 0;
position: absolute;
}
(This is more accessable than just using display: none;)
And replace the selector .enabled
with input:checked + .magic
in the CSS.
Complete example: https://jsfiddle.net/rhy6gfn4/
A small points about the CSS:
You should select not just .enabled
but use .magic.enabled
, because then it’s more obvious that these rules belong to the animated icons. Also “enabled” is a common class name and you don’t want those styles apply to unrelated elements.
It would be a tiniest bit more performant to select the i
elements using .magic > i
and not just .magic i
.
Wow- that is some sparkly animations effects!
Because icons
is only assigned one time, const
can be used instead of let
. This helps avoid accidental re-assignment.
If jQuery is going to be included on the page, then it can be used to simplify the JavaScript code – with the click
handler:
$('.magic').click(function() {
$(this).toggleClass('enabled');
});
That way there is no need to iterate over the collection of elements and add an onclick
event handler to each one.