I have a div that has a background gradient which values are set dynamically using javascript. Sometime, I need to change the gradient values (can be colors or color stop position). I would like to animate those changes without using javascript (dont get me wrong, I set the gradient valeus using javascript but I want to animate thise using CSS3). I tried setting a transition property like I would do with CSS. Here is a MWE:
function getPercent() {
return Math.random() * 100;
}
setInterval(() => {
const per = getPercent();
document.getElementById('foo').style.background =
`linear-gradient(to right, #aebcbf 0%,#aebcbf ${per}%,#0a0e0a ${per}%,#0a0e0a 100%`
}, 1000)
.container {
transition: all 1s ease;
background: linear-gradient(to right, #aebcbf 0%,#6e7774 50%,#0a0e0a 51%,#0a0809 100%);
width: 150px;
height: 10px;
}
<div class="container" id="foo"></div>
Hope this is useful
background: linear-gradient(269deg, #ffffff, #ff0000);
background-size: 400% 400%;
-webkit-animation: AnimationName 23s ease infinite;
-moz-animation: AnimationName 23s ease infinite;
-o-animation: AnimationName 23s ease infinite;
animation: AnimationName 23s ease infinite;
#-webkit-keyframes AnimationName {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
#-moz-keyframes AnimationName {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
#-o-keyframes AnimationName {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
#keyframes AnimationName {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
Ref: https://www.gradient-animator.com/
and for more info about CSS transition check this
https://css-tricks.com/almanac/properties/t/transition/
Unfortunately the background-image is not animatable (more info about background-image). Therefore animating CSS gradients is an issue.
To solve this with pure CSS, you can create an :before that has contains the new gradient. Give it opacity: 0 and change this to opacity: 1 when you want to show the new gradient.
Eventually we can do the following:
.container{
width: 150px;
height: 10px;
background: linear-gradient(to right, #aebcbf 0%, #6e7774 50%, #0a0e0a 51%, #0a0809 100%);
position: relative;
z-index: 100;
}
.container:before{
background: linear-gradient(to right, #aebcbf 0%, #6e7774 10%, #0a0e0a 51%, #0a0809 100%);
content: '';
display: block;
height: 100%;
position: absolute;
top: 0;
left: 0;
opacity: 0;
width: 100%;
z-index: -100;
transition: opacity 0.45s;
}
.container:hover:before {
opacity: 1;
}
<div class="container" id="foo"></div>
EDIT:
If you want to make the gradients dynamic use a variable that keeps track of which element you changed last, like:
var swapElement = 0;
function getPercent() {
return Math.random() * 100;
}
setInterval(() => {
const per = getPercent();
if(swapElement == 0){
//change gradient of :before
//set opacity to 1
swapElement = 1;
} else {
document.getElementById('foo').style.background =
`linear-gradient(to right, #aebcbf 0%,#aebcbf ${per}%,#0a0e0a ${per}%,#0a0e0a 100%`
}, 1000);
//set opacity :before to 0
swapElement = 0;
}
Related
Here I have a Video that changes every few seconds with spinning animation + video getting small and big during the spinning.
But over time the timing gets out of order.
let degree = 720;
function rotateElement(){
let spin = `rotate(${degree}deg)`;
document.getElementById('myVideo').style.transform = spin;
document.getElementById('myVideo').style.transitionDuration = "1s";
degree += 720;
}
function smaller(){
document.getElementById('myVideo').style.maxWidth = "10px";
document.getElementById('myVideo').style.maxHeight = "10px";
document.getElementById('myVideo').style.transitionDuration = "1s";
}
function bigger(){
document.getElementById('myVideo').style.maxWidth = "30vw";
document.getElementById('myVideo').style.maxHeight = "19vw";
document.getElementById('myVideo').style.transitionDuration = "1s";
}
//setInterval(change, 5000);
window.onload = function () {
setInterval(change, 5000);
setInterval(rotateElement, 4900);
setInterval(smaller, 4850);
setInterval(bigger, 5050);
};
Not sure exactly what you want the animation to do with the rotate, but this is the basic idea of just doing it in CSS.
.test {
background-color: yellow;
/*animation: shrink 5s 3s ease infinite, rotate 50s steps(10, end) forwards infinite; */
animation: shrink 5s 3s ease infinite, rotate 50s forwards infinite;
transition: width 1s, height 1s;
overflow: hidden;
width: 30vw;
height: 19vh;
}
#keyframes shrink {
0%,
65%,
90%,
100% {
width: 30vw;
height: 19vh;
}
70% {
width: 14px;
height: 10px;
}
}
#keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<img class="test" src="http://placekitten.com/200/100">
What is the criteria?
In the following example, I am animating CSS transform, and when you click anywhere (while in Google Chrome) the animation is blocked by a 2-second-long while loop.
Why is the CSS transform animation blocked?
EDIT: Lately Chrome no longer blocks the transform while the main thread is blocked, indicating that they have moved the sort of animation in the following example off main thread.
Animating transform can happen on a separate thread, but it isn't clear exactly when. Sometimes it works.
In this first example, separate-thread transform animation does not happen (click on it to block the main thread and therefore pause the animation):
window.addEventListener('click', kill)
function kill() {
var start = +new Date;
while (+new Date - start < 2000){}
}
html, body, div {
width: 100%; height: 100%;
margin: 0; padding: 0;
/* background: #364659; */
/* background: #293442; */
background: #1E2630;
overflow: hidden;
}
#keyframes ShimmerEffect {
0% { transform: translate3d(-15%, -15%, 0) }
100% { transform: translate3d(-60%, -60%, 0) }
}
.shimmerSurface {
/* overflow: hidden; */
/* perspective: 100000px */
}
.shimmerSurfaceContent {
transform-style: preserve-3d;
background: linear-gradient(
-45deg,
rgba(0,0,0,0) 40%,
rgba(244,196,48,0.6) 50%,
rgba(0,0,0,0) 60%
);
background-repeat: repeat;
background-size: 100% 100%;
width: 400%; height: 400%;
animation: ShimmerEffect 1.8s cubic-bezier(0.75, 0.000, 0.25, 1.000) infinite;
}
<div class="shimmerSurface">
<div class="shimmerSurfaceContent"></div>
</div>
(codepen link)
EDIT: seems the example's animation is not blocked in Safari (though it chops the gradient), but is blocked only in Chrome and Firefox. How can we unblock the animation in Chrome and Firefox?
In next example, when you click anywhere to block the main thread (in Chrome), you will see that transform is animated on a separate thread because it continues to animate, while the stroke-offset animation is frozen because apparently stroke-offset animation is happening on the main thread:
window.addEventListener('click', kill)
function kill() {
var start = +new Date;
while (+new Date - start < 2000){}
}
.loader {
--path: #2F3545;
--dot: #5628EE;
--duration: 3s;
width: 44px;
height: 44px;
position: relative;
}
.loader:before {
content: "";
width: 6px;
height: 6px;
border-radius: 50%;
position: absolute;
display: block;
background: var(--dot);
top: 37px;
left: 19px;
transform: translate(-18px, -18px);
-webkit-animation: dotRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
animation: dotRect var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg {
display: block;
width: 100%;
height: 100%;
}
.loader svg rect,
.loader svg polygon,
.loader svg circle {
fill: none;
stroke: var(--path);
stroke-width: 10px;
stroke-linejoin: round;
stroke-linecap: round;
}
.loader svg polygon {
stroke-dasharray: 145 76 145 76;
stroke-dashoffset: 0;
-webkit-animation: pathTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
animation: pathTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg rect {
stroke-dasharray: 192 64 192 64;
stroke-dashoffset: 0;
-webkit-animation: pathRect 3s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
animation: pathRect 3s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader svg circle {
stroke-dasharray: 150 50 150 50;
stroke-dashoffset: 75;
-webkit-animation: pathCircle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
animation: pathCircle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
.loader.triangle {
width: 48px;
}
.loader.triangle:before {
left: 21px;
transform: translate(-10px, -18px);
-webkit-animation: dotTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
animation: dotTriangle var(--duration) cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite;
}
#-webkit-keyframes pathTriangle {
33% {
stroke-dashoffset: 74;
}
66% {
stroke-dashoffset: 147;
}
100% {
stroke-dashoffset: 221;
}
}
#keyframes pathTriangle {
33% {
stroke-dashoffset: 74;
}
66% {
stroke-dashoffset: 147;
}
100% {
stroke-dashoffset: 221;
}
}
#-webkit-keyframes dotTriangle {
33% {
transform: translate(0, 0);
}
66% {
transform: translate(10px, -18px);
}
100% {
transform: translate(-10px, -18px);
}
}
#keyframes dotTriangle {
33% {
transform: translate(0, 0);
}
66% {
transform: translate(10px, -18px);
}
100% {
transform: translate(-10px, -18px);
}
}
#-webkit-keyframes pathRect {
25% {
stroke-dashoffset: 64;
}
50% {
stroke-dashoffset: 128;
}
75% {
stroke-dashoffset: 192;
}
100% {
stroke-dashoffset: 256;
}
}
#keyframes pathRect {
25% {
stroke-dashoffset: 64;
}
50% {
stroke-dashoffset: 128;
}
75% {
stroke-dashoffset: 192;
}
100% {
stroke-dashoffset: 256;
}
}
#-webkit-keyframes dotRect {
25% {
transform: translate(0, 0);
}
50% {
transform: translate(18px, -18px);
}
75% {
transform: translate(0, -36px);
}
100% {
transform: translate(-18px, -18px);
}
}
#keyframes dotRect {
25% {
transform: translate(0, 0);
}
50% {
transform: translate(18px, -18px);
}
75% {
transform: translate(0, -36px);
}
100% {
transform: translate(-18px, -18px);
}
}
#-webkit-keyframes pathCircle {
25% {
stroke-dashoffset: 125;
}
50% {
stroke-dashoffset: 175;
}
75% {
stroke-dashoffset: 225;
}
100% {
stroke-dashoffset: 275;
}
}
#keyframes pathCircle {
25% {
stroke-dashoffset: 125;
}
50% {
stroke-dashoffset: 175;
}
75% {
stroke-dashoffset: 225;
}
100% {
stroke-dashoffset: 275;
}
}
.loader {
display: inline-block;
margin: 0 16px;
}
html {
-webkit-font-smoothing: antialiased;
}
* {
box-sizing: border-box;
}
*:before, *:after {
box-sizing: border-box;
}
body {
min-height: 100vh;
background: #F5F9FF;
display: flex;
justify-content: center;
align-items: center;
}
body .dribbble {
position: fixed;
display: block;
right: 20px;
bottom: 20px;
}
body .dribbble img {
display: block;
height: 28px;
}
<div class="loader">
<svg viewBox="0 0 80 80">
<circle id="test" cx="40" cy="40" r="32"></circle>
</svg>
</div>
<div class="loader triangle">
<svg viewBox="0 0 86 80">
<polygon points="43 8 79 72 7 72"></polygon>
</svg>
</div>
<div class="loader">
<svg viewBox="0 0 80 80">
<rect x="8" y="8" width="64" height="64"></rect>
</svg>
</div>
Why does the first example's transform animation run on the main thread, while the second example's transform animation runs on a separate thread?
What are the criteria under which a transform is guaranteed to run in a separate thread (at least, in Chrome)?
Browser Threads
Each browser has at least three threads; precisely what is run on each depends on the browser. Modern browsers all have more than three now, but they still have three categories of threads that will always be separate. Why?
One will always be entirely separate and only accessible by the browser to handle things like scrolling, opening a new tab etc... At least one will always be for things like calculating and parsing and so will be run on the CPU. And at least one thread will run on the GPU as it is required for something to be shown on your screen.
Layers
For the GPU to know what it's showing on the screen it needs the layout rasterised in a bitmap format. But as things move around the screen it's best if we send the GPU a few bitmaps that can move around. We call these layers.
as #irdkwmnsb has pointed out we can use the layers tab in the developer tools to see exactly which elements have been split into separate bitmaps.
Explicitly Creating A Layer
For any HTML or SVG element that we know will transform, we can add the following CSS rule to ensure the element is separated into a separate bitmap layer and the transition shouldn't be blocked by other activity on the main thread:
will-change: transform
so adding the CSS rule
.shimmerSurfaceContent {
will-change: transform;
}
should stop the transition from being blocked in your first example.
Why Only In Some Browsers?
The reason some browsers may not automatically split this element into a separate layer is that there is a performance issue with creating too many bitmap layers so they are careful not to create too many. Also, some things don't look good when created as separate bitmaps and moved around so the browser may avoid it.
But for this example specifically, we can see from the two bitmap layers in this image that the top one has a semi-transparent edge. Things like this have previously caused aliasing problems for the GPU as it calculates the various shaded of yellow.
This may have been a reason for chrome to previously avoid separating it into a new bitmap layer.
I have this .png that I wanted to make it bounce a little every time it detects a scroll movement but I'm not good in javascript and css. I hope you guys can help me
<div class="arrownav bounce">
<a href="" class="logo">
<img src="{{ asset('assets/img/arrow down.png') }}" height="45">
</a>
</div>
I am now using a css that made the image bounce
this is the code:
.bounce {
-webkit-animation:bounce 1s infinite;
-moz-animation:bounce 1s infinite;
-o-animation:bounce 1s infinite;
animation:bounce 1s infinite;
}
#-webkit-keyframes bounce {
0% { bottom:0px; }
50% { bottom:15px; }
100% {bottom:30;}
}
#-moz-keyframes bounce {
0% { bottom:0px; }
50% { bottom:15px; }
100% {bottom:30;}
}
#-o-keyframes bounce {
0% { bottom:0px; }
50% { bottom:15px; }
100% {bottom:30;}
}
#keyframes bounce {
0% { bottom:0px; }
50% { bottom:15px; }
100% {bottom:30;}
}
The first thing I noticed is the missing unit in all #keyframes, right here:
100% {bottom:30;}
This should be:
100% { bottom:30px; }
You've used the bottom style in you animation, which is perfectly fine, but in order for it to work the element's position needs to be either relative, absolute or fixed (more here).
.bounce {
position: relative;
-webkit-animation: bounce 1s infinite;
-moz-animation: bounce 1s infinite;
-o-animation: bounce 1s infinite;
animation: bounce 1s infinite;
}
Here's a working fiddle.
Bonus
Another way to change the element's position in the animation is the transform style, instead of using bottom. When you use transform, you don't need position: relative;.
#keyframes bounce {
0% {
transform: translateY(0px);
}
50% {
transform: translateY(-15px);
}
100% {
transform: translateY(-30px);
}
}
Fiddle
I've got this animation: http://codepen.io/tiamat/pen/Jrdvx
When the colored circle goes into the black one it restarts animation. When it does not go into it it takes me to circle.com website. Can I stop both of these actions? I would like if successful, not to restart the animation, but the colored circle to remain in the black one.
html:
<div id="intreg" style="border:1px solid #000000; width:750px; height: 550px;" >
<div id="jos">
<div id="square_drop"ondragenter="return dragEnter(event)" ondrop="return dragDrop(event)" ondragover="return dragOver(event)"></div>
<div id="circle_drop"ondragenter="return dragEnter(event)" ondrop="return dragDrop(event)" ondragover="return dragOver(event)"></div>
</div>
<div id="sus">
<div id="triangle_drop"ondragenter="return dragEnter(event)" ondrop="return dragDrop(event)" ondragover="return dragOver(event)"></div>
<div id="square" draggable="true"ondragstart="return dragStart(event)"></div>
<div id="circle" draggable="true"ondragstart="return dragStart(event)"></div>
<div id="triangle" draggable="true"ondragstart="return dragStart(event)"></div>
</div>
</div>
javascript:
function dragStart(ev) {
ev.dataTransfer.effectAllowed='move';
ev.dataTransfer.setData("Text", ev.target.getAttribute('id'));
ev.dataTransfer.setDragImage(ev.target,100,100);
return true;
}
function dragEnter(ev) {
event.preventDefault();
return true;
}
function dragOver(ev) {
event.preventDefault();
}
function dragDrop(ev) {
var data = ev.dataTransfer.getData("Text");
ev.target.appendChild(document.getElementById(data));
ev.stopPropagation();
return false;
}
css:
#square {
-webkit-animation: move4 3s forwards linear ;
-moz-animation: move4 3s forwards linear ;
-o-animation: move4 3s forwards linear ;
}
#-webkit-keyframes move4 {
0% {margin-bottom: -400px;margin-left:-100px;}
100% {margin-top: -130px;margin-left: 400px;}
}
#-moz-keyframes move4 {
0% {margin-top: 1400px;margin-left:-100px;}
100% {margin-top: 30px;margin-left: 400px;}
}
#-o-keyframes move4 {
0% {margin-bottom: -400px;margin-left:-100px;}
100% {margin-top: -130px;margin-left: 400px;}
}
#circle {
-webkit-animation: move5 3s forwards linear ;
-moz-animation: move5 3s forwards linear ;
-o-animation: move5 3s forwards linear ;}
#-webkit-keyframes move5 {
0% {margin-top: 1000px;margin-left:-100px;}
100% {margin-top: -40px;margin-left: 150px;}
}
#-moz-keyframes move5 {
0% {margin-top: 1000px;margin-left:-100px;}
100% {margin-top: -40px;margin-left: 150px;}
}
#-o-keyframes move5 {
0% {margin-top: 1000px;margin-left:-100px;}
100% {margin-top: -40px;margin-left: 150px;}
}
#triangle {
-webkit-animation: move6 3s forwards linear ;
-moz-animation: move6 3s forwards linear ;
-o-animation: move6 3s forwards linear ;
}
#-webkit-keyframes move6 {
0% {margin-top: 1000px;margin-left:-100px;}
100% {margin-top: -120px;margin-left: 20px;}
}
#-moz-keyframes move6 {
0% {margin-top: 1000px;margin-left:-100px;}
100% {margin-top: -120px;margin-left: 20px;}
}
#-o-keyframes move6 {
0% {margin-top: 1000px;margin-left:-100px;}
100% {margin-top: -120px;margin-left: 20px;}
}
For the case where the colored circle is dropped on the black one: When you append the dropped element, it is re-rendered in its new location, including the animation you specified in the style. You could try creating a class that removes the animations
#circle.stay {
-webkit-animation: none !important;
-moz-animation: none !important;
-o-animation: none !important;
}
and add this class to the element before appending it to its new home:
function dragDrop(ev) {
var data = ev.dataTransfer.getData("Text");
var el = document.getElementById(data);
el.className="stay";
ev.target.appendChild(el);
ev.stopPropagation();
return false;
}
For the case where it misses, add an ondrop handler to the body element that cancels the drag
<body ondrop="return false">
codepen
How can I get a CSS Animation to play with a JavaScript onClick? I currently have:
.classname {
-webkit-animation-name: cssAnimation;
-webkit-animation-duration:3s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes cssAnimation {
from {
-webkit-transform: rotate(0deg) scale(1) skew(0deg) translate(100px);
}
to {
-webkit-transform: rotate(0deg) scale(2) skew(0deg) translate(100px);
}
}
How can I apply an onClick?
Are you sure you only display your page on webkit? Here is the code, passed on safari.
The image (id='img') will rotate after button click.
function ani() {
document.getElementById('img').className = 'classname';
}
.classname {
-webkit-animation-name: cssAnimation;
-webkit-animation-duration: 3s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: ease;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes cssAnimation {
from {
-webkit-transform: rotate(0deg) scale(1) skew(0deg) translate(100px);
}
to {
-webkit-transform: rotate(0deg) scale(2) skew(0deg) translate(100px);
}
}
<input name="" type="button" onclick="ani()" value="Click">
<img id="img" src="https://i.stack.imgur.com/vghKS.png" width="328" height="328" />
You just use the :active pseudo-class. This is set when you click on any element.
.classname:active {
/* animation css */
}
Found solution on css-tricks
const element = document.getElementById('img')
element.classList.remove('classname'); // reset animation
void element.offsetWidth; // trigger reflow
element.classList.add('classname'); // start animation
You can achieve this by binding an onclick listener and then adding the animate class like this:
$('#button').onClick(function(){
$('#target_element').addClass('animate_class_name');
});
CSS ONLY solution that works on every click and plays the animation to the end:
All you have to do is to add the animation to the :focus pseudo class and set it to none in :active pseudo class.
If your element isn't focusable add tabindex="0" attribute to the html element:
#keyframes beat {
0% {
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
100% {
-webkit-transform: scale(0.8,0.8);
transform: scale(0.8, 0.8);
}
}
.className {
background-color:#07d;
color: #fff;
font-family: sans-serif;
font-size: 20px;
padding: 20px;
margin:5px;
-webkit-transform: scale(1, 1);
transform: scale(1, 1);
}
.className:focus {
-webkit-animation: beat 1s ease-in-out backwards;
animation: beat 1s ease-in-out backwards;
}
.className:active {
-webkit-animation: none;
animation: none;
}
body {
text-align: center;
}
<h3>Any element with tabindex="0", like a div:</h3>
<div tabindex="0" class="className"> Click me many times!</div>
<h3>Any focusable element like a button:</h3>
<button class="className"> Click me many times!</button>
var abox = document.getElementsByClassName("box")[0];
function allmove(){
abox.classList.remove("move-ltr");
abox.classList.remove("move-ttb");
abox.classList.toggle("move");
}
function ltr(){
abox.classList.remove("move");
abox.classList.remove("move-ttb");
abox.classList.toggle("move-ltr");
}
function ttb(){
abox.classList.remove("move-ltr");
abox.classList.remove("move");
abox.classList.toggle("move-ttb");
}
.box {
width: 100px;
height: 100px;
background: red;
position: relative;
}
.move{
-webkit-animation: moveall 5s;
animation: moveall 5s;
}
.move-ltr{
-webkit-animation: moveltr 5s;
animation: moveltr 5s;
}
.move-ttb{
-webkit-animation: movettb 5s;
animation: movettb 5s;
}
#keyframes moveall {
0% {left: 0px; top: 0px;}
25% {left: 200px; top: 0px;}
50% {left: 200px; top: 200px;}
75% {left: 0px; top: 200px;}
100% {left: 0px; top: 0px;}
}
#keyframes moveltr {
0% { left: 0px; top: 0px;}
50% {left: 200px; top: 0px;}
100% {left: 0px; top: 0px;}
}
#keyframes movettb {
0% {left: 0px; top: 0px;}
50% {top: 200px;left: 0px;}
100% {left: 0px; top: 0px;}
}
<div class="box"></div>
<button onclick="allmove()">click</button>
<button onclick="ltr()">click</button>
<button onclick="ttb()">click</button>
Add a
-webkit-animation-play-state: paused;
to your CSS file, then you can control whether the animation is running or not by using this JS line:
document.getElementById("myDIV").style.WebkitAnimationPlayState = "running";
if you want the animation to run once, every time you click. Remember to set
-webkit-animation-iteration-count: 1;
You can do that by using following code
$('#button_id').on('click', function(){
$('#element_want_to_target').addClass('.animation_class');});
Try this:
<div>
<p onclick="startAnimation()">Start</p><!--O botão para iniciar (start)-->
<div id="animation">Hello!</div> <!--O elemento que você quer animar-->
</div>
<style>
#keyframes animationName {
from {margin-left:-30%;}
}
</style>
<script>
function startAnimation() {
document.getElementById("animation").style.animation = "animationName 2s linear 1";
}
</script>
Add the animation and remove it after the animation-duration ends using setTimeout()
const elem = document.querySelector(".element");
elem.onclick = () => {
elem.style.animation="YOUR_ANIMATION";
setTimeout(()=>{
elem.style.animation="none";
},YOUR_ANIMATION_DURATION);
}