'Seamlessly reverse CSS animation
I have a styled list of colors that can be selected to change a page's theme.
See an example codepen here: https://codepen.io/jdillon/pen/abEzRRK?editors=0100
Currently when a color listitem is clicked, a pulsing animation is kicked off, which is working as expected.
When a different item is clicked, the animation is removed from whichever item it is applied to and the newly clicked item then has the animation applied to it.
The problem I've encountered is that when the animation is removed from a list item, it snaps back to it's original dimensions, so my question is how can I make the item the animation is removed from seamlessly animate back to it's original dimensions?
HTML:
<ul class="theme-picker">
<li class="blue"></li>
<li class="orange"></li>
<li class="purple"></li>
<li class="crystal"></li>
<li class="peach"></li>
</ul>
CSS:
$blue-start: #6080ff;
$blue-stop: #2141bd;
$orange-start: #feb692;
$orange-stop: #ea5455;
$purple-start: #97abff;
$purple-stop: #123597;
$crystal-start: #81ffef;
$crystal-stop: #f067b4;
$peach-start: #f6d242;
$peach-stop: #ff52e5;
* {
box-sizing: border-box;
}
body {
background-color: #f2f5ff;
color: #31507d;
display: flex;
flex-direction: column;
font-family: "Poppins", sans-serif;
height: 100vh;
align-items: center;
justify-content: center;
}
ul.theme-picker {
list-style-type: none;
border-radius: 50px;
display: flex;
padding: 0;
margin-bottom: 50px;
margin-top: 50px;
li {
margin-right: 20px;
width: 65px;
height: 65px;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
box-shadow: rgba(74, 79, 97, 0.25) 0px 13px 27px -5px,
rgba(74, 79, 97, 0.3) 0px 8px 16px -8px;
border-radius: 50px;
cursor: pointer;
transition: all 150ms ease-in-out;
position: relative;
border: 5px solid #fff;
animation-name: none;
&:last-child {
margin-right: 0;
}
&.blue {
background: linear-gradient(225deg, $blue-start 16%, $blue-stop 100%);
}
&.orange {
background: linear-gradient(225deg, $orange-start 16%, $orange-stop 100%);
}
&.purple {
background: linear-gradient(225deg, $purple-start 16%, $purple-stop 100%);
}
&.crystal {
background: linear-gradient(
225deg,
$crystal-start 16%,
$crystal-stop 100%
);
}
&.peach {
background: linear-gradient(225deg, $peach-start 16%, $peach-stop 100%);
}
&.pulse {
animation-name: pulse;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
animation-duration: 1500ms;
animation-direction: forwards;
}
&:hover {
transform: scale(1.3);
}
}
}
@keyframes pulse {
0% {
transform: scale(1.3);
}
50% {
transform: scale(0.9);
}
100% {
transform: scale(1.3);
}
}
JS:
let themeListItems = document.querySelectorAll(".theme-picker li");
themeListItems.forEach(function (element) {
element.addEventListener("click", changeTheme);
});
function changeTheme(e) {
let currentlyActive = document.querySelector("li.pulse");
currentlyActive?.classList.remove("pulse");
e.target.classList.add("pulse");
e.preventDefault();
}
Solution 1:[1]
function changeTheme(e) {
if (document.querySelector("li.pulse") !== null) {
let currentlyActive = document.querySelector("li.pulse");
let tracker = setInterval(function() {
let matrix = window.getComputedStyle(currentlyActive).getPropertyValue("transform");
matrix = matrix.substring(7, matrix.length - 1).split(",");
let scale = Math.sqrt(matrix[0] * matrix[0] + matrix[1] * matrix[1]);
if (scale > 0.98 && scale < 1.02) {
currentlyActive.classList.remove("pulse");
clearInterval(tracker);
}
});
}
e.target.classList.add("pulse");
e.preventDefault();
}
It seems to be a pretty smooth animation out, but feel free to toy with the 0.98 and 1.02 if it isn't smooth enough.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
