498

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; }
}
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
Dan
  • 6,022
  • 3
  • 20
  • 28

5 Answers5

825

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

Yukulélé
  • 15,644
  • 10
  • 70
  • 94
Christofer Vilander
  • 17,232
  • 6
  • 31
  • 26
  • 11
    You can read about the animation-fill-mode property here - http://dev.w3.org/csswg/css3-animations/#animation-fill-mode-property Hope that helps! – Christofer Vilander Oct 20 '12 at 18:51
  • 11
    @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. – Farzad Yousefzadeh Feb 17 '16 at 15:23
  • 6
    don't forget to specify `100% / to` point in `@keyframe` otherwise it wont work. – Tomasz Mularczyk Jun 20 '17 at 18:33
  • 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](https://stackoverflow.com/a/67032976/636561) for a different approach. – Luca Fagioli Apr 10 '21 at 09:55
90

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
agiopnl
  • 1,190
  • 9
  • 18
39

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;
Taylor A. Leach
  • 2,115
  • 4
  • 25
  • 43
  • 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. – d.k Jan 04 '23 at 08:49
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>
Deepu Reghunath
  • 8,132
  • 2
  • 38
  • 47
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.

Luca Fagioli
  • 12,722
  • 5
  • 59
  • 57