Here is a simple pulsating animation using CSS keyframes.
The problem is if I want to discard or remove the animation in the middle of it, the animation stops with a jerking movement (a sudden jump to its initial state as you can see in the snippet).
I want the animation to go back to its initial state smoothly even if you stop it in the middle of the animation.
JQuery and TweenMax are accepted.
Is there any way to do that?
let test = document.getElementById("test");
setTimeout(function(){
test.classList.add("addAniamte");
}, 2000)
setTimeout(function(){
test.classList.remove("addAniamte");
test.classList.add("removeAniamte");
console.log('stoping aniamtion!');
}, 4500)
#-webkit-keyframes pulse {
0% {
-webkit-transform: scale(1, 1);
}
50% {
-webkit-transform: scale(3, 3);
}
100% {
-webkit-transform: scale(1, 1);
};
}
#test {
background: red;
width: 20px;
height: 20px;
margin: 60px;
}
.addAniamte{
-webkit-animation: pulse 1s linear infinite;
animation: pulse 1s linear infinite;
}
.removeAniamte{
-webkit-animation: none;
animation:none;
}
<div id="test"></div>
This is quite easy to do with GSAP! Here's how to do something like this in GSAP 3.
var tween = gsap.from("#test", {duration: 1, scale: 0.33, repeat: -1, yoyo: true, paused: true});
function startAnimation() {
tween.play();
}
function stopAnimation() {
tween.pause();
gsap.to(tween, {duration: 0.5, progress: 0});
}
#test {
background: red;
width: 60px;
height: 60px;
margin: 60px;
}
<div id="test"></div>
<button onclick="startAnimation()">Start</button>
<button onclick="stopAnimation()">Stop</button>
<script src="https://cdn.jsdelivr.net/npm/gsap#3.0.4/dist/gsap.min.js"></script>
Without GSAP (or at least a JS-only approach), it's incredibly messy and error prone as the other answer shows. In fact, I had my own question which lead to a CSS-Tricks article on the subject.
I have tried to make the javascript completely dynamic by using getComputedStyle. To stop animation smoothly, we need to have a watcher variable, which will count the duration of each animation cycle. (tempAnimationWatcher).
Add animation start event to element.
Calculate single animation duration (testAnimationTime) and initiate tempAnimationWatcherInterval to watch animation cycle time tempAnimationWatcher
if stopAnimation, stop animation after remaining css time. (testAnimationTime - tempAnimationWatcher)
NOTE: the testAnimationTime calculations are based on consideration that the css animation time is written in seconds. see line testAnimationTime = parseInt(animationDurationInCss.substring(0, animationDurationInCss.length - 1)) * 1000;, this is removing last char and converting it in milliseconds.
const test = document.getElementById("test");
let tempAnimationWatcher = 0;
let testAnimationTime = 0;
let tempAnimationWatcherInterval;
test.addEventListener('animationstart', function (e) {
console.log('animation starts')
const animationDurationInCss = getComputedStyle(test).animationDuration;
testAnimationTime = parseInt(animationDurationInCss.substring(0, animationDurationInCss.length - 1)) * 1000;
tempAnimationWatcher = 0;
tempAnimationWatcherInterval = setInterval(() => {
tempAnimationWatcher += 10;
if (tempAnimationWatcher >= testAnimationTime) tempAnimationWatcher = 0;
}, 10);
});
function startAnimation() {
test.classList.add("addAniamte");
}
function stopAnimation() {
clearInterval(tempAnimationWatcherInterval);
setTimeout(() => {
test.classList.remove("addAniamte");
console.log("stoping aniamtion!");
}, (testAnimationTime - tempAnimationWatcher));
}
startAnimation();
#keyframes pulse {
0% {
-webkit-transform: scale(1, 1);
}
50% {
-webkit-transform: scale(3, 3);
}
100% {
-webkit-transform: scale(1, 1);
}
;
}
#test {
background: red;
width: 20px;
height: 20px;
margin: 60px;
}
.addAniamte {
animation: pulse 2s linear infinite;
}
<div id="test"></div>
<hr>
<button onclick="startAnimation()">Start</button>
<button onclick="stopAnimation()">Stop</button>
Related
I want to display the word 'Hello' on the home page of a website. I used CSS to make the 'Hello' transition up as the page loads in the beginning. I would like to implement a shuffling animation that randomly shuffles between the word Hello in different languages. I would like to do so with an animation where as the 'Hello' slides up at the beginning, the 'Hello' slides up more, fades out and disappears. As this occurs, a 'Bonjour' for example slides up from beneath and takes place. I picture this repeating forever.
Is there any way to implement such animation using CSS, JavaScript, Jquery, or any other web tools? Below is the HTML, CSS, and JS structure I have that only achieves the initial transition as the page loads:
<body>
<section>
<h1 id="switch">Hello</h1>
</section>
</body>
section {
text-align: left;
}
section h1 {
font-size: 100px;
font-weight: 420;
position: absolute;
top: 130px;
left: 200px;
opacity: 0;
transform: translateY( 43px );
animation-name: fade-in;
animation-duration: 0.6s;
animation-timing-function: ease-out;
animation-fill-mode: forwards;
}
var currentIndex = 0;
var hello = new Array( 'Hello', 'Bonjour', 'Hola' );
function randomIndex( ) {
return Math.floor( Math.random( ) * hello.length);
};
window.setInterval( function( ) {
var newIndex = randomIndex( );
while( newIndex === currentIndex ) newIndex = randomIndex();
currentIndex = newIndex;
document.getElementById("switch").textContent = hello[ currentIndex ];
}, 2300 );
In CSS you need to set up #keyframes for your fade-in animation,. Then you can add a percentage of the duration that you wish to animate the animate-able properties opacity and top position. Make sure your duration matches the setInterval time => 2300 => 2.3s.
#keyframes:
In my example I set up a tween that will start at 0% with opacity 0 and top position in vh lengths, then as the tween reaches 70%, it is shown moving upwards to 5vh, where it will stay at an opacity of 1 until 90%, when its opacity will start to fade out. At 100% it will be opacity of 0, then the loop starts over as it is set to infinte in the css animation, the element will reset to 20vh and the animation repeats itself over again.
*You can play around with the percentages in the #keyframes rule to get the effect you're look for in terms of fading in and out movement, etc...
let currentIndex = 0;
const hello = ['Hello', 'Bonjour', 'Hola'];
function randomIndex() {
return ~~(Math.random() * hello.length);
};
window.setInterval(function() {
let newIndex = randomIndex();
while (newIndex === currentIndex) newIndex = randomIndex();
currentIndex = newIndex;
document.getElementById("switch").textContent = hello[currentIndex];
}, 2300);
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
section {
text-align: center;
}
section h1 {
font-size: 100px;
font-weight: 420;
position: absolute;
top: 5vh;
left: 50vh;
opacity: 0;
animation: fade-in 2.3s ease-in-out forwards infinite;
}
#keyframes fade-in {
0% {
opacity: 0;
top: 20vh;
}
70%,
90% {
opacity: 1;
top: 5vh;
}
100% {
opacity: 0;
top: 5vh;
}
}
<body>
<section>
<h1 id="switch">Hello</h1>
</section>
</body>
As the greeting is not really semantic (you don't for example want it read out every few seconds to a screen reader) you could put it instead on the body (or another suitable element, depending on exactly the structure you want) in a pseudo before element. That way it is basically decoration, not meaning.
Also, to avoid timing issues, where a setInterval may get out of step with the timing of the keyframes animation, you can sense the animationend event and then set a timout for the next 300ms and then reset the animation to run again.
let currentIndex = 0;
const hello = ['Hello', 'Bonjour', 'Hola'];
function randomIndex() {
return ~~(Math.random() * hello.length);
};
function getNext() {
let newIndex = randomIndex();
while (newIndex === currentIndex) newIndex = randomIndex();
currentIndex = newIndex;
document.body.style.setProperty('--greeting', "'" + hello[currentIndex] + "'");
document.body.style.setProperty('--animationname', 'none');
setTimeout(function () {
document.body.style.setProperty('--animationname', 'move');
}, 300);
}
document.body.addEventListener('animationend',getNext);
body {
--greeting: 'Hello';
--animationname: move;
}
body::before {
content: var(--greeting);
animation-duration: 2s;
animation-iteration-count: 1;
animation-name: var(--animationname);
position: absolute;
top: 100%;
font-size: 100px;
font-weight: 420;
position: absolute;
left: 200px;
animation-timing-function: linear;
animation-fill-mode: forwards;
}
#keyframes move {
0% {
top: 100%;
opacity: 0;
}
50% {
top: 50%;
opacity: 1;
}
100% {
top: 0%;
opacity: 0;
}
}
Obviously you'll want to change the timings, positionings etc to be exactly what you want.
I'm trying to fade in a toast message when i fire a function in JavaScript.
I want the function to create the element, add it to the dom, then fade it in using a css transition, then fade it out using that same transition, then remove it from the dom.
The fade out won't work unless i wrap it in a timeout.
EDIT :: Rickard
I added a button to show the toast.
function flashToast(msg, duration) {
duration = duration || 3000;
// create the toast element
const toastElement = document.createElement("p");
toastElement.innerHTML = msg;
toastElement.classList.add("toast");
// add it to the dom
document.body.appendChild(toastElement);
// fade in won't work unless I wrap it in a timeout
setTimeout(function() {
toastElement.style.opacity = "1";
}, 1);
// remove it after the duration is over
setTimeout(function() {
toastElement.style.opacity = "0";
}, duration - 500);
// start fading it out 500ms before removing it
setTimeout(function() {
document.body.removeChild(toastElement);
}, duration);
}
.toast {
display: inline-block;
font-size: 1.2rem;
padding: 0.8em 1em;
border-radius: 5rem;
color: #eaeaea;
background: #606060;
background: rgba(96, 96, 96, 0.7);
position: absolute;
bottom: 2%;
left: 50%;
transform: translate(-50%, -50%);
/* opacity is 0 when first enjected */
opacity: 0;
transition: opacity 500ms ease;
}
<button onclick="flashToast('Toast!')">Show toast</button>
After watching the video recommended by Rickard Elimää. I found that requestAnimationFrame solves the issue. Here's the final JavaScript:
function flashToast(msg, duration) {
duration = duration || 3000;
// create the toast element
const toastElement = document.createElement("p");
toastElement.innerHTML = msg;
toastElement.classList.add("toast");
// add it to the dom
document.body.appendChild(toastElement);
requestAnimationFrame(function() {
toastElement.style.opacity = "1";
});
// remove it after the duration is over
setTimeout(function() {
toastElement.style.opacity = "0";
}, duration - 500);
// start fading it out 500ms before removing it
setTimeout(function() {
document.body.removeChild(toastElement);
}, duration);
}
You having to use a setTimeout is because of how javascript works with the event loop. Here is a really good video explaining a basic fundamental thing that you need to know about javascript. (Hey, I've been working with web development for five years, but been using javascript for 20, and just got to know about this this summer.)
Jake Archibald: In The Loop
If you don't want to use a timeout, you can instead use an animation. The downside is that it's hard to control specific times. If you start with opacity 0 and then have opacity 1 at 15%, that will create slower fade ins for longer toast durations.
function flashToast(msg, duration) {
duration = duration || 3000;
const toastElement = document.createElement("p");
toastElement.innerHTML = msg;
toastElement.classList.add("toast");
// Added
toastElement.style.setProperty("--duration", duration + "ms");
document.body.appendChild(toastElement);
setTimeout(function() {
document.body.removeChild(toastElement);
}, duration);
}
.toast {
display: inline-block;
font-size: 1.2rem;
padding: 0.8em 1em;
border-radius: 5rem;
color: #eaeaea;
background: #606060;
background: rgba(96, 96, 96, 0.7);
position: absolute;
bottom: 2%;
left: 50%;
transform: translate(-50%, -50%);
/* NEW */
animation: fade var(--duration) linear;
}
#keyframes fade {
0% {opacity: 0;}
15% {opacity: 1;}
85% {opacity: 1;}
100% {opacity: 0;}
}
<button onclick="flashToast('Toast!')">Show toast</button>
Naturally, we can create a CSS animation using keyframes, and control it from there.
However, ideally, I would like to trigger this animation from a button click - so the button click would be an event...
#keyframes fade-in {
0% {opacity: 0;}
100% {opacity: 1;}
}
Now, on click, I want to trigger this animation; as opposed to from within the CSS animation property.
see here jsfiddle
if you want your animation to work every time you press the button use this code :
$('button').click(function() {
$(".fademe").addClass('animated');
setTimeout(function() {
$(".fademe").removeClass('animated');
}, 1500);
});
where 1500 is the animation-duration in this case, 1.5s
$('button').click(function() {
$(".fademe").addClass('animated');
setTimeout(function() {
$(".fademe").removeClass('animated');
}, 1500);
});
.fademe {
width: 100px;
height: 100px;
background: red;
}
.fademe.animated {
animation: fade-in 1.5s ease;
}
#keyframes fade-in {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="fademe">
</div>
<button>CLICK ME</button>
EXPLANATION :
on click on the button add class animated ( or any other class ) to the element you want to apply the animation to , .fademe
make a setTimeout(function() to delay the removeClass for the duration of the animation 1.5s or 1500ms
write in CSS the declaration of the animation , #keyframes, and add it to the element with the class added by the JQ .fademe.animated
$("#move-button").on("click", function(){
$("#ship").removeClass("moving");
$("#ship")[0].offsetWidth = $("#ship")[0].offsetWidth;
$("#ship").addClass("moving");
});//
#ship
{
background: green;
color: #fff;
height: 60px;
line-height: 60px;
text-align: center;
width: 100px;
}
#move-button
{
margin-top: 20px;
}
#ship.moving
{
animation: moving 2s ease;
}
#keyframes moving
{
0%{ transform: translate(0px);}
50%{ transform: translate(20px);}
100%{ transform: translate(0px);}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="ship">Ship</div>
<button id="move-button">Push</button>
If you want to make the animation happen and always end before allowing the event listener to trigger it again, I would suggest to control the behaviour like this:
// Add this to your event listener
if (!element.classList.contains("myClass")) {
element.className = "myClass";
setTimeout(function() {
element.classList.remove("myClass");
}, 1000); //At least the time the animation lasts
}
There is a toggle method that works just fine for this, hope it helps:
function Fade() {
document.getElementById("box").classList.toggle("animate");
}
#box {
background-color: black;
height: 50px;
width: 50px;
}
.animate {
animation: fademe 0.5s;
}
#keyframes fademe {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
<html>
<head>
<title>
Animation Trigger
</title>
</head>
<body>
<div id="box"></div>
<button onclick="Fade()"> Fade above Box</button>
</body>
I have two CSS #keyframe animations applied to the same element. One that fires on :hover and the other that fires on mouse out, both applied with CSS.
I was curious to know if there was a way to detect the end of a selected keyframe animation rather than it being attached to the element and firing twice?
if there was a way to detect the end of a selected keyframe animation
If your intention is to detect the ending of a keyframe animation itself instead of detect end of every keyframe then, yes, it can be done using the animationend event. This event is fired every time any animation that is attached to the element is completed and the context info has one parameter named animationName using which we can find which animation had ended.
The animationName parameter is important because when multiple animations would be applied to the same element like in your case then you'd need to know which animation had actually ended because this event would get fired at the end of every animation.
Using vanilla JS:
window.onload = function() {
var elm = document.querySelector('.animate');
var op = document.querySelector('.output');
elm.addEventListener('animationend', function(e) { /* this is fired at end of animation */
op.textcontent = 'Animation ' + e.animationName + ' has ended';
});
elm.addEventListener('animationstart', function(e) { /* this is fired at start of animation */
op.textcontent = 'Animation ' + e.animationName + ' has started';
});
}
.animate {
height: 100px;
width: 100px;
margin: 10px;
border: 1px solid red;
animation: shake-up-down 2s ease;
}
.animate:hover {
animation: shake-left-right 2s ease forwards;
}
#keyframes shake-up-down {
0% {
transform: translateY(0px);
}
25% {
transform: translateY(10px);
}
75% {
transform: translateY(-10px);
}
100% {
transform: translateY(0px);
}
}
#keyframes shake-left-right {
0% {
transform: translateX(0px);
}
25% {
transform: translateX(10px);
}
75% {
transform: translateX(-10px);
}
100% {
transform: translateX(0px);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='animate'></div>
<div class='output'></div>
Using jQuery:
$(document).ready(function() {
var elm = $('.animate');
var op = $('.output');
elm.on('animationend', function(e) { /* fired at the end of animation */
op.html('Animation ' + e.originalEvent.animationName + ' has ended');
});
elm.on('animationstart', function(e) { /* fired at the start of animation */
op.html('Animation ' + e.originalEvent.animationName + ' has started');
});
});
.animate {
height: 100px;
width: 100px;
margin: 10px;
border: 1px solid red;
animation: shake-up-down 2s ease;
}
.animate:hover {
animation: shake-left-right 2s ease forwards;
}
#keyframes shake-up-down {
0% {
transform: translateY(0px);
}
25% {
transform: translateY(10px);
}
75% {
transform: translateY(-10px);
}
100% {
transform: translateY(0px);
}
}
#keyframes shake-left-right {
0% {
transform: translateX(0px);
}
25% {
transform: translateX(10px);
}
75% {
transform: translateX(-10px);
}
100% {
transform: translateX(0px);
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='animate'></div>
<div class='output'></div>
In the above snippet, you can see how the .output div's content indicates the name of the animation that is ended after each animation completes.
Note: CSS animations still need vendor prefixes in some browsers/versions. To be on the safer side, you need to listen for the prefixed versions of the animationend event also.
Try this example:
function whichAnimationEvent(){
var t,
el = document.createElement("fakeelement");
var animations = {
"animation" : "animationend",
"OAnimation" : "oAnimationEnd",
"MozAnimation" : "animationend",
"WebkitAnimation": "webkitAnimationEnd"
}
for (t in animations){
if (el.style[t] !== undefined){
return animations[t];
}
}
}
var animationEvent = whichAnimationEvent();
$(".button").click(function(){
$(this).addClass("animate");
$(this).one(animationEvent,
function(event) {
// Do something when the animation ends
});
});
I have a CSS3 animation that needs to be restarted on a click. It's a bar showing how much time is left. I'm using the scaleY(0) transform to create the effect.
Now I need to restart the animation by restoring the bar to scaleY(1) and let it go to scaleY(0) again.
My first attempt to set scaleY(1) failed because it takes the same 15 seconds to bring it back to full length. Even if I change the duration to 0.1 second, I would need to delay or chain the assignment of scaleY(0) to let the bar replenishment complete.
It feels too complicated for such a simple task.
I also found an interesting tip to restart the animation by removing the element from the document, and then re-inserting a clone of it:
http://css-tricks.com/restart-css-animation/
It works, but is there a better way to restart a CSS animation?
I'm using Prototype and Move.js, but I'm not restricted to them.
No need in timeout, use reflow to apply the change:
function reset_animation() {
var el = document.getElementById('animated');
el.style.animation = 'none';
el.offsetHeight; /* trigger reflow */
el.style.animation = null;
}
#animated {
position: absolute;
width: 50px; height: 50px;
background-color: black;
animation: bounce 3s ease-in-out infinite;
}
#keyframes bounce {
0% { left: 0; }
50% { left: calc( 100% - 50px ); }
100% { left: 0; }
}
<div id="animated"></div>
<button onclick="reset_animation()">Reset</button>
Just set the animation property via JavaScript to "none" and then set a timeout that changes the property to "", so it inherits from the CSS again.
Demo for Webkit here: http://jsfiddle.net/leaverou/xK6sa/
However, keep in mind that in real world usage, you should also include -moz- (at least).
#ZachB's answer about the Web Animation API seems like "right"™ way to do this, but unfortunately seems to require that you define your animations through JavaScript. However it caught my eye and I found something related that's useful:
Element.getAnimations() and Document.getAnimations()
The support for them is pretty good as of 2021.
In my case, I wanted to restart all the animations on the page at the same time, so all I had to do was this:
const replayAnimations = () => {
document.getAnimations().forEach((anim) => {
anim.cancel();
anim.play();
});
};
But in most cases people will probably want to select which animation they restart...
getAnimations returns a bunch of CSSAnimation and CSSTransition objects that look like this:
animationName: "fade"
currentTime: 1500
effect: KeyframeEffect
composite: "replace"
pseudoElement: null
target: path.line.yellow
finished: Promise {<fulfilled>: CSSAnimation}
playState: "finished"
ready: Promise {<fulfilled>: CSSAnimation}
replaceState: "active"
timeline: DocumentTimeline {currentTime: 135640.502}
# ...etc
So you could use the animationName and target properties to select just the animations you want (albeit a little circuitously).
EDIT
Here's a handy function that might be more compatible using just Document.getAnimations, with TypeScript thrown in for demonstration/fun:
// restart animations on a given dom element
const restartAnimations = (element: Element): void => {
for (const animation of document.getAnimations()) {
if (element.contains((animation.effect as KeyframeEffect).target)) {
animation.cancel();
animation.play();
}
}
};
Implement the animation as a CSS descriptor
Add the descriptor to an element to start the animation
Use a animationend event handler function to remove the descriptor when the animation completes so that it will be ready to be added again next time you want to restart the animation.
HTML
<div id="animatedText">
Animation happens here
</div>
<script>
function startanimation(element) {
element.classList.add("animateDescriptor");
element.addEventListener( "animationend", function() {
element.classList.remove("animateDescriptor");
} );
}
</script>
<button onclick="startanimation(
document.getElementById('animatedText') )">
Click to animate above text
</button>
CSS
#keyframes fadeinout {
from { color: #000000; }
25% {color: #0000FF; }
50% {color: #00FF00; }
75% {color: #FF0000; }
to { color : #000000; }
}
.animateDescriptor {
animation: fadeinout 1.0s;
}
Try it here: jsfiddle
If you have a class for CSS3 animation, for example .blink, then you can removeClass for some element and addClass for this element thought setTimeout with 1 millisecond by click.
$("#element").click(function(){
$(this).removeClass("blink");
setTimeout(function(){
$(this).addClass("blink);
},1 // it may be only 1 millisecond, but it's enough
});
You can also use display property, just set the display to none.
display:none;
and the change backs it to block (or any other property you want).
display:block;
using JavaScript.
and it will work amazingly.
The Animation API gives you full control over when and what to play, and is supported by all modern browsers (Safari 12.1+, Chrome 44+, Firefox 48+, Edge 79+) .
const effect = new KeyframeEffect(
el, // Element to animate
[ // Keyframes
{transform: "translateY(0%)"},
{transform: "translateY(100%)"}
],
{duration: 3000, direction: "alternate", easing: "linear"} // Keyframe settings
);
const animation = new Animation(effect, document.timeline);
animation.play();
Demo: https://jsfiddle.net/cstz9L8v/
References:
https://developer.mozilla.org/en-US/docs/Web/API/KeyframeEffect/KeyframeEffect
https://developer.mozilla.org/en-US/docs/Web/API/Animation
There is an answer on MDN, which is similar to the reflow approach:
<div class="box">
</div>
<div class="runButton">Click me to run the animation</div>
#keyframes colorchange {
0% { background: yellow }
100% { background: blue }
}
.box {
width: 100px;
height: 100px;
border: 1px solid black;
}
.changing {
animation: colorchange 2s;
}
function play() {
document.querySelector(".box").className = "box";
window.requestAnimationFrame(function(time) {
window.requestAnimationFrame(function(time) {
document.querySelector(".box").className = "box changing";
});
});
}
If you create two identical sets of keyframes, you can "restart" the animation by swapping between them:
function restart_animation(element) {
element.classList.toggle('alt')
}
#keyframes spin1 {
to { transform: rotateY(360deg); }
}
#keyframes spin2 {
to { transform: rotateY(360deg); }
}
.spin {
animation-name: spin1;
animation-duration: 2s;
}
.alt {
animation-name: spin2;
}
div {
width: 100px;
background: #8CF;
padding: 5px;
}
<div id=_square class=spin>
<button onclick="restart_animation(_square)">
Click to restart animation
</button>
</div>
On this page you can read about restarting the element animation: Restart CSS Animation (CSS Tricks)
Here is my example:
<head>
<style>
#keyframes selectss
{
0%{opacity: 0.7;transform:scale(1);}
100%{transform:scale(2);opacity: 0;}
}
</style>
<script>
function animation()
{
var elm = document.getElementById('circle');
elm.style.animation='selectss 2s ease-out';
var newone = elm.cloneNode(true);
elm.parentNode.replaceChild(newone, elm);
}
</script>
</head>
<body>
<div id="circle" style="height: 280px;width: 280px;opacity: 0;background-color: aqua;border-radius: 500px;"></div>
<button onclick="animation()"></button>
</body>
But if you want to you can just remove the element animation and then return it:
function animation()
{
var elm = document.getElementById('circle');
elm.style.animation='';
setTimeout(function () {elm.style.animation='selectss 2s ease-out';},10)
}
setInterval(() => {
$('#XMLID_640_').css('animation', 'none')
setTimeout(() => {
$('#XMLID_640_').css('animation', '')
}, 3000)
}, 13000)
Create a second "keyframe#" which restarts you animation, only problem with this you cannot set any animation properties for the restarting animation (it just kinda pops back)
HTML
<div class="slide">
Some text..............
<div id="slide-anim"></div>
</div><br>
<button onclick="slider()"> Animation </button>
<button id="anim-restart"> Restart Animation </button>
<script>
var animElement = document.getElementById('slide-anim');
document.getElementById('anim-restart').addEventListener("mouseup", restart_slider);
function slider() {
animElement.style.animationName = "slider"; // other animation properties are specified in CSS
}
function restart_slider() {
animElement.style.animation = "slider-restart";
}
</script>
CSS
.slide {
position: relative;
border: 3px black inset;
padding: 3px;
width: auto;
overflow: hidden;
}
.slide div:first-child {
position: absolute;
width: 100%;
height: 100%;
background: url(wood.jpg) repeat-x;
left: 0%;
top: 0%;
animation-duration: 2s;
animation-delay: 250ms;
animation-fill-mode: forwards;
animation-timing-function: cubic-bezier(.33,.99,1,1);
}
#keyframes slider {
to {left: 100%;}
}
#keyframes slider-restart {
to {left: 0%;}
}
Note that with React, clearing the animation like this, a codesandbox I found helps.
Example I used in my code:
function MyComponent() {
const [shouldTransition, setShouldTransition] = useState(true);
useEffect(() => {
setTimeout(() => {
// in my code, I change a background image here, and call this hook restart then animation,
// which first clears the animationName
setShouldTransition(false);
}, timeout * 1000);
}, [curr]);
useEffect(() => {
// then restore the animation name after it was cleared
if (shouldTransition === false) {
setShouldTransition(true);
}
}, [shouldTransition]);
return (
<div
ref={ref2}
style={{
animationName: shouldTransition ? "zoomin" : "",
}}
/>
);
}
I found out a simple solution today. Using the example provided in this answer, you can just append the element again to the body:
function resetAnimation() {
let element = document.getElementById('animated');
document.body.append(element);
}
#animated {
position: absolute;
width: 50px;
height: 50px;
background-color: LightSalmon;
animation: bounce 3s ease-in-out infinite;
}
#keyframes bounce {
0% {left: 0;}
50% {left: calc(100% - 50px);}
100% {left: 0;}
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="animated"></div>
<button onclick="resetAnimation()">Reset</button>
</body>
</html>
Using Chrome's developer tools, the append does not actually append the element to the body and just replace it, probably because the same reference to the element is used.