545

I'm running an animation on some elements that are set to opacity: 0; in the CSS. The animation class is applied onClick, and, using keyframes, it changes the opacity from 0 to 1 (among other things).

Unfortunately, when the animation is over, the elements go back to opacity: 0 (in both Firefox and Chrome). My natural thinking would be that animated elements maintain the final state, overriding their original properties. Is this not true? And if not, how can I get the element to do so?

The code (prefixed versions not included):

@keyframes bubble { 0% { transform:scale(0.5); opacity:0.0; } 50% { transform:scale(1.2); opacity:0.5; } 100% { transform:scale(1.0); opacity:1.0; } } 
0

6 Answers 6

898

Try adding animation-fill-mode: forwards;. For example, the shorthand would be used like this:

animation: bubble 1.0s forwards; 

https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode

Sign up to request clarification or add additional context in comments.

5 Comments

You can read about the animation-fill-mode property here - dev.w3.org/csswg/css3-animations/#animation-fill-mode-property Hope that helps!
@Dan the forwards value of the animation-fill-mode property makes sure element would hold the last keyframe state of animation after animation ends. for example if your animation changes width from 0 to 100px, this property makes sure the element remains 100px wide after animation ends.
don't forget to specify 100% / to point in @keyframe otherwise it wont work.
I had an issue with this approach: at least in Chrome, even after the animation ended, the renderer was still sucking up graphics resources, making the application less responsive. Check my answer for a different approach.
Sometimes I think the people making the CSS API and I are from different planets
102

If you are using more animation attributes the shorthand is:

animation: bubble 2s linear 0.5s 1 normal forwards; 

This gives:

  • bubble animation name
  • 2s duration
  • linear timing-function
  • 0.5s delay
  • 1 iteration-count (can be 'infinite')
  • normal direction
  • forwards fill-mode (set 'backwards' if you want to have compatibility to use the end position as the final state[this is to support browsers that has animations turned off]{and to answer only the title, and not your specific case})

Available timing-functions:

ease | ease-in | ease-out | ease-in-out | linear | step-start | step-end 

Available directions

normal | reverse | alternate | alternate-reverse 

Comments

42

IF NOT USING THE SHORT HAND VERSION: Make sure the animation-fill-mode: forwards is AFTER the animation declaration or it will not work...

animation-fill-mode: forwards; animation-name: appear; animation-duration: 1s; animation-delay: 1s; 

vs

animation-name: appear; animation-duration: 1s; animation-fill-mode: forwards; animation-delay: 1s; 

1 Comment

As of 2023 in Chrome 108.0.5359.124 (Official Build) (64-bit) it seems not to matter if the animation-fill-mode line comes before or after or even in another curly braces block or another stylesheet than the animation-name or other properties in you snippets. And it would be strange if it did matter, since it would completely contradict other parts of the CSS spec (which define the computation of the final applied rules). Though, I'm not sure if I understood you correctly.
15

Use animation-fill-mode: forwards;

animation-fill-mode: forwards; 

The element will retain the style values that is set by the last keyframe (depends on animation-direction and animation-iteration-count).

Note: The @keyframes rule is not supported in Internet Explorer 9 and earlier versions.

Working example

div { width: 100px; height: 100px; background: red; position :relative; -webkit-animation: mymove 3ss forwards; /* Safari 4.0 - 8.0 */ animation: bubble 3s forwards; /* animation-name: bubble; animation-duration: 3s; animation-fill-mode: forwards; */ } /* Safari */ @-webkit-keyframes bubble { 0% { transform:scale(0.5); opacity:0.0; left:0} 50% { transform:scale(1.2); opacity:0.5; left:100px} 100% { transform:scale(1.0); opacity:1.0; left:200px} } /* Standard syntax */ @keyframes bubble { 0% { transform:scale(0.5); opacity:0.0; left:0} 50% { transform:scale(1.2); opacity:0.5; left:100px} 100% { transform:scale(1.0); opacity:1.0; left:200px} }
<h1>The keyframes </h1> <div></div>

Comments

4

I had an issue using forwards: at least in Chrome, even after the animation ended, the renderer was still sucking up graphics resources, making the application less responsive.

An approach that does not cause this trouble is by using an EventListener.

CSS animations emit events, so you can use the animationend event to intervene when the animation ends.

CSS

.fade_in { animation: fadeIn 2s; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } 

JavaScript

const element = document.getElementById("element-to-be-animated"); element.addEventListener("animationend", () => { // Set your final state here. For example: element.style["opacity"] = 1; }, { once: true }); 

The option once: true tells the engine to remove the event listener after its execution, leaving your application fresh and clean.

I have created a JSFiddle to show how it works.

2 Comments

wouldn't it be even simpler to just define the style such that the appearance without animation is identical to the final keyframe of the animation? saves the animationend listener alltogether.
(and yes, even with animation-delay would be doable this way (see modified fiddle), but at that point the readability of this approach begins to decrease)
0
 @keyframes stretch-first { from { width: 0%; } 50% { width: *; } to { width: *; } } @keyframes stretch-last { from { width: 0%; } 50% { width: 0%; } 60% { background-color: #0082d6; } to { width: *; background-color: *; } } @keyframes color-last { from { background-color: white; } 50% { background-color: black; } to { background-color: *; } } 

2 Comments

Good answers include some explanation, not just code
Is * used as a placeholder in this code? If so, how is it being dynamically replaced? If it's not replaced, it wouldn't render any meaningful output.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.