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.
I've been trying to find an answer to my problem but I haven't been able to find one.
I want to be able to clear an interval once it's done, but then be able to restart it.
My current code doesn't let me do that: once the interval stops, you can't run it again.
Here's my code:
function about() {
var about = document.getElementById("about")
about.classList.add("about-mi")
var moreinfo = setInterval (function() {
about.classList.remove("about-mi")
}, 2000)
clearInterval(moreinfo)
}
.about-mi {
animation-name: moreinfo;
animation-duration: 2s;
}
#keyframes moreinfo {
0% {color: black;}
50% {color: red; transform: translateY(-20px);}
100% {color: black;}
}
<a onclick="about()">▼</a>
<h2 id=about>About</h2>
I would prefer solutions that only require HTML, CSS, or JavaScript, but I am also open to try solutions that require jQuery.
Actually setInterval doesn't do anything here. You don't need to use interval for that, just use setTimeout.
function about() {
var about = document.getElementById("about")
about.classList.add("about-mi")
var moreinfo = setTimeout(function() {
about.classList.remove("about-mi")
}, 2000)
}
.about-mi {
animation-name: moreinfo;
animation-duration: 2s;
}
#keyframes moreinfo {
0% {
color: black;
}
50% {
color: red;
transform: translateY(-20px);
}
100% {
color: black;
}
}
<a onclick="about()">▼</a>
<h2 id=about>About</h2>
Also you can do this with only CSS
.about-mi {
animation: moreinfo 2s, reset 2.1s;
}
a:active+.about-mi {
animation: anything, reset 2.1s;
}
#keyframes moreinfo {
0% {
color: black;
}
50% {
color: red;
transform: translateY(-20px);
}
100% {
color: black;
}
}
#keyframes reset {
from,
to {
color: black;
transform: unset;
}
}
<p>Wait for the animation to finish before first clicking (2.1s). because the animation starts when the page loads.</p>
▼
<h2 id="about" class="about-mi">About</h2>
As you use the interval timeout to remove the class about-mi to match with the 2 seconds you have defined in your css with animation-duration: 2s; it gets hard to mantain when you start changing one of those values you always have to keep in mind ooooh I also have to update the other one say javascript value and css value
That given, in this case another approach is remove the class based on HTMLElement: animationend event like so:
var aboutElement = document.getElementById("about")
function about() {
aboutElement.classList.add("about-mi")
}
aboutElement.addEventListener("animationend", function(){
aboutElement.classList.remove("about-mi");
});
.about-mi {
animation-name: moreinfo;
animation-duration: 2s;
}
#keyframes moreinfo {
0% {color: black;}
50% {color: red; transform: translateY(-20px);}
100% {color: black;}
}
<a onclick="about()">▼</a>
<h2 id=about>About</h2>
In your specific case, it looks like you only need to run the code once, and not multiple times at intervals, so as #dgknca mentioned, all you need is a setTimeout.
How to restart an interval in general
Answering this in case other users comes across this post. The best you can do (as far as I'm aware) is to define a non-anonymous function with the functionality you want, and then use that in the interval:
function doSomething() {
// your logic here
console.log('I am doing something.');
}
// execute doSomething every 1 second
var interval = setInterval(doSomething, 1000);
Like so, you can cancel the interval using:
clearInterval(interval);
To "restart" the interval, you would need to assign interval to a new interval:
interval = setInterval(doSomething, 1000);
Trying to set a random animation time on each iteration. I set a CSS custom property --animation-time changed randomly each animation iteration with JS.
let square = document.getElementById('square');
let time;
square.addEventListener('animationiteration', duration);
function duration() {
time = Math.random() + 1;
square.style.setProperty('--animation-time', time + 's');
console.log(time);
}
:root {
--animation-time: 5s;
--color-1: #ff0000;
--color-2: #ffee00;
}
#keyframes fire {
0% {
background-color: var(--color-1);
}
100% {
background-color: var(--color-2);
}
}
#square {
width: 100px;
height: 100px;
animation: fire var(--animation-time) ease alternate infinite;
}
<div id="square"></div>
The duration of animation is set to time = Math.random() + 1; so, the iterations should never have less than 1 second. However, it hapens sometimes very quickly. Why?
Here is a CodePen: https://codepen.io/aitormendez/pen/YbvvKw
When you change the animation-duration you will not simply change how each iteration will take but you will change the whole animation. If for example you have 3s as a duration so the first iteration will take 3s and if at 3s (iterationend) you change to 5s it's like you create another animation where the iteration will last 5s and you jump to a new state of that new animation which is 3s of 5s.
Here is an example to better illustrate the issue. I will keep increasing the duration and you will notice that once you reach the end of the iteration you will get back to a previous state because you increased the duration then you will reach again the last state and you will get back and so on.
It's like someone is running to the 100% state and you keep increasing that state.
let square = document.getElementById('square');
let time = 1;
square.addEventListener('animationiteration', duration);
function duration() {
time*=2;
square.style.setProperty('--animation-time', time + 's');
console.log(time);
}
:root {
--animation-time: 1s;
}
#keyframes fire {
0% {
background-color: red;
}
50% {
background:yellow;
}
100% {
background-color: black;
}
}
#square {
width: 100px;
height: 100px;
animation: fire var(--animation-time) ease infinite alternate;
}
<div id="square"></div>
In the above example I am multiplying the duration by 2 so each time I jump back to start from the 50% state and once I reach the 100% I will repeat the same again.
The same is happening with your code but in a more complex way since you are increasing/decreasing the duration. Decreasing the duration will make you jump to the next iteration and the alternate state.
In other words, You will end with a random flickering animation.
To visually illustrate what is happening here is an animation where I will animate the timeline of your animation
.box {
height:50px;
border:1px solid;
background:
/* This will illustrate the current state */
linear-gradient(green,green) left/2px 100% no-repeat,
/* This is our animation with a duration of 5s (50px) intially*/
linear-gradient(to right,yellow,red,red,yellow) left/100px 100%;
animation:
move 10s linear infinite, /* This is the current state*/
change 2s steps(3) infinite /* this is the change of the animation duration*/
; /**/
}
#keyframes move {
to {
background-position:right,left;
}
}
#keyframes change {
to {
background-size:2px 100%,40px 100%;
}
}
<div class="box">
</div>
You can see that we aren't changing the current state (illustrated by the green line) but we are changing the whole animation making the current state jumping in different color randomly.
One idea to obtain what you want is to consider transition and class toggling:
let square = document.getElementById('square');
let time;
/* We intially add the class to trigger the transition*/
setTimeout(function(){square.classList.toggle('change')},10);
square.addEventListener('transitionend', duration);
/* When the transition is done we toggle the class to start another one*/
function duration() {
time = Math.random() + 1;
square.style.setProperty('--animation-time', time + 's');
square.classList.toggle('change');
console.log(time);
}
:root {
--animation-time: 1s;
--color-1: #ff0000;
--color-2: #ffee00;
}
#square {
width: 100px;
height: 100px;
transition: var(--animation-time) ease;
background-color: var(--color-1);
}
#square.change {
background-color: var(--color-2);
}
<div id="square"></div>
i have a problem with javascript.I want to control the jumbotron color and set it every 3 seconds to a random one.The problem here is that i don't know how to manipulate CSS with JavaScript, as i am pretty new to javascript.
I saw some other solutions and some other threads but it did not work.(I don't know if i did anything wrong).
I have no js code written right now as i deleted everything that did not work.
.jumbotron {
background-color: #f14444 !important;
}
/*Without the !important rule it won't change color!*/
If you have any threads that you think i haven't checked i would be happy to check them but im confident enough to say that i've seen them already.
Thanks for your time anyways!
To change the background color, simply select the element and the set the property:
setTimeout(() => {
document.querySelector('.jumbotron').style.backgroundColor = '#f14444';
}, 1000);
.jumbotron {
height: 300px;
width: 300px;
background-color: black;
}
<div class="jumbotron"></div>
Note that above will select the first found element with the given class, so if you need to target a specific element, consider giving it an id and select it as #Phil shows above.
Html elements in the DOM have a style property.
document.getElementById('something').style.backgroundColor = '#ccc'
Note that hyphenated properties like background-color in CSS are typically camel-case (backgroundColor) in Javascript.
Also you can achieve it with keyframe animation
Here is a tutorial how to get random color
#-webkit-keyframes changeColors {
0% {
background-color: red;
}
50% {
background-color: blue;
}
100% {
background-color: green;
}
}
#-moz-keyframes changeColors {
0% {
background-color: red;
}
50% {
background-color: blue;
}
100% {
background-color: green;
}
}
#-ms-keyframes changeColors {
0% {
background-color: red;
}
50% {
background-color: blue;
}
100% {
background-color: green;
}
}
.jumbotron {
-webkit-animation: changeColors 3s infinite;
-moz-animation: changeColors 3s infinite;
-ms-animation: changeColors 3s infinite;
background-color: #f14444;
}
<div class="jumbotron">
Some text here
</div>
I change the background color of the table (from lightgrey to lightgreen and back) for X seconds with CSS3 animation:
#keyframes change {
0% {background-color: lightgreen;}
99% {background-color: lightgreen;}
100% {background-color: lightgrey;}
}
the HTML code:
<tr style='background-color: lightgrey; animation: change Xs linear 5s;'>
Now I need to override the CSS animation and change the background color of the table (at any moment) to red when I click on it (and come back to lightgrey when re-click stopping the animation).
I simply try to add this code but the CSS animation always overrides the Javascript onclick command:
onclick="this.style.animationPlayState='paused'; this.style.backgroundColor='red';"
Any suggestions? Do you think it's better to do all this in Javascript?
You might want to defer that to CSS classes and selectors:
/* Skipping the -webkit prefix needed by chrome for sake of brevity */
.animated:not(.clicked) {
animation: change linear 5s;
}
#keyframes change {
0% { background-color: lightgreen; }
99% { background-color: lightgreen; }
100% { background-color: lightgrey; }
}
.clicked {
background-color: red;
}
Then, just add the clicked class when the row is clicked:
// Using jQuery for simplicity
$("table").on("click", "tr", function() {
$(this).addClass("clicked");
});
Working example: http://jsbin.com/IQaRUZa/1/edit
The solution adopted (solved with "!important" CSS3 element):
HTML code:
<tr id="1" style='background-color: lightgrey; animation: change_visite 3s linear 3s;' onclick="change(document.getElementById('1'));">
CSS code:
#keyframes change_visite {
0% {background-color: lightgreen;}
99% {background-color: lightgreen;}
100% {background-color: lightgrey;}
}
.evidenziato {
background-color: coral !important;
}
JS code:
function change(classe){
if(classe.className === "evidenziato"){
classe.className='';
} else {
classe.className='evidenziato';
}
}
Working example here: http://jsbin.com/ENAqocUb/1/edit?html,css,js,output