Una animación recorre una secuencia de estados que se escribe aparte, con el nombre que se le quiera dar. A diferencia de una transición, no espera a que algo la dispare: arranca sola en cuanto la página se pinta.
Las diferencias con una transición son tres:
El guion se escribe fuera de cualquier selector, con @keyframes y un nombre.
Dentro van las etapas, y en cada una las propiedades que tiene que tener el elemento en ese
momento.
Con dos etapas basta escribir from y to:
@keyframes con dos etapas
<div class="row"> <div class="caja">1</div> <div class="caja marcada">2</div> <div class="caja">3</div> </div>
.row { display: flex; gap: 10px; align-items: flex-start; } .caja { width: 90px; height: 70px; background: #dcdcea; border: 2px solid #8686a8; display: flex; align-items: center; justify-content: center; font-family: Verdana, sans-serif; font-size: .8rem; } .marcada { background: #9a9ac8; } @keyframes desplazar { from { transform: translateX(0); } to { transform: translateX(170px); } } .marcada { animation-name: desplazar; animation-duration: 2s; }
El guion por sí solo no hace nada: hay que asignárselo a un elemento. Eso es lo que
hacen las dos líneas de .marcada, y son las dos únicas obligatorias.
El código lleva los dos colores de la clase de transiciones:
@keyframes: el
guion. Describe qué cambia y en qué etapas, pero no se refiere a ningún
elemento concreto.animation-…:
las órdenes al elemento. Dicen qué guion sigue, cuánto dura, cuántas
veces se repite.Es el mismo reparto de la transición, con una diferencia: allí el estado de llegada estaba
en una regla propia, disparada por el :hover; aquí las etapas están todas dentro
del guion, y por eso no hace falta que nada lo dispare.
Cuando hay más, se escriben en porcentajes del recorrido total. 0% es el
principio y 100% el final.
@keyframes con cuatro etapas
<div class="row"> <div class="caja">1</div> <div class="caja marcada">2</div> <div class="caja">3</div> </div>
.row { display: flex; gap: 10px; align-items: flex-start; } .caja { width: 90px; height: 70px; background: #dcdcea; border: 2px solid #8686a8; display: flex; align-items: center; justify-content: center; font-family: Verdana, sans-serif; font-size: .8rem; } .marcada { background: #9a9ac8; } @keyframes recorrido { 0% { transform: translateX(0); background: #dcdcea; } 40% { transform: translateX(170px); background: #c0392b; } 70% { transform: translateX(170px) scale(1.3); } 100% { transform: translateX(0); background: #dcdcea; } } .marcada { animation-name: recorrido; animation-duration: 4s; animation-iteration-count: infinite; }
Los porcentajes no tienen que estar repartidos a partes iguales: aquí el primer tramo dura el 40 % del tiempo y el segundo el 30 %. Lo que hay entre dos etapas lo calcula el navegador, igual que en una transición.
Las dos obligatorias: qué guion se usa y cuánto tarda en recorrerlo entero. Sin duración, la animación dura cero y no se ve.
tres duraciones para el mismo guion
<div class="column"> <div class="caja d1">600 ms</div> <div class="caja d2">1,5 s</div> <div class="caja d3">4 s</div> </div>
.caja { width: 90px; height: 70px; background: #dcdcea; border: 2px solid #8686a8; display: flex; align-items: center; justify-content: center; font-family: Verdana, sans-serif; font-size: .8rem; } @keyframes desplazar { from { transform: translateX(0); } to { transform: translateX(170px); } } .column .caja { animation-name: desplazar; animation-iteration-count: infinite; animation-direction: alternate; } .column .d1 { animation-duration: 600ms; } .column .d2 { animation-duration: 1500ms; } .column .d3 { animation-duration: 4s; } .column { display: flex; flex-direction: column; gap: 10px; }
La curva, igual que en las transiciones: ease, linear,
ease-in, ease-out, ease-in-out,
cubic-bezier() y steps().
Hay una diferencia: aquí la curva se aplica entre cada par de etapas, no
al recorrido entero. Con cuatro etapas y ease-in-out, la caja frena y vuelve a
arrancar en cada una. Para un movimiento continuo que se repite, linear.
steps() encuentra aquí su uso principal: una animación a saltos sobre una
tira de dibujos, que es como se animan los personajes de los juegos.
linear frente a ease-in-out, con el mismo guion de cuatro etapas
<div class="column"> <div class="caja c1">linear</div> <div class="caja c2">ease-in-out</div> </div>
.caja { width: 90px; height: 70px; background: #dcdcea; border: 2px solid #8686a8; display: flex; align-items: center; justify-content: center; font-family: Verdana, sans-serif; font-size: .8rem; } @keyframes vaiven { 0% { transform: translateX(0); } 50% { transform: translateX(170px); } 100% { transform: translateX(0); } } .column .caja { animation-name: vaiven; animation-duration: 3s; animation-iteration-count: infinite; } .column .c1 { animation-timing-function: linear; } .column .c2 { animation-timing-function: ease-in-out; } .column { display: flex; flex-direction: column; gap: 10px; }
Lo que se espera antes de empezar. Su uso principal es escalonar varios elementos.
tres retardos sobre el mismo guion
<div class="column"> <div class="caja r1">0 ms</div> <div class="caja r2">200 ms</div> <div class="caja r3">400 ms</div> </div>
.caja { width: 90px; height: 70px; background: #dcdcea; border: 2px solid #8686a8; display: flex; align-items: center; justify-content: center; font-family: Verdana, sans-serif; font-size: .8rem; } @keyframes aparecer { from { opacity: 0; transform: translateX(-30px); } to { opacity: 1; transform: translateX(0); } } .column .caja { animation-name: aparecer; animation-duration: 800ms; animation-iteration-count: infinite; } .column .r1 { animation-delay: 0ms; } .column .r2 { animation-delay: 200ms; } .column .r3 { animation-delay: 400ms; } .column { display: flex; flex-direction: column; gap: 10px; }
Cuántas veces se repite. Un número, o infinite para que no pare. Admite
decimales: 2.5 hace dos recorridos y se queda a mitad del tercero.
normalalternatereverse y alternate-reversenormal frente a alternate
<div class="column"> <div class="caja n1">normal</div> <div class="caja n2">alternate</div> </div>
.caja { width: 90px; height: 70px; background: #dcdcea; border: 2px solid #8686a8; display: flex; align-items: center; justify-content: center; font-family: Verdana, sans-serif; font-size: .8rem; } @keyframes ida { from { transform: translateX(0); } to { transform: translateX(170px); } } .column .caja { animation-name: ida; animation-duration: 1500ms; animation-iteration-count: infinite; animation-timing-function: linear; } .column .n1 { animation-direction: normal; } .column .n2 { animation-direction: alternate; } .column { display: flex; flex-direction: column; gap: 10px; }
Transición cuando el movimiento responde a algo que hace el usuario y va de un estado a otro: un botón que cambia de color, un menú que se despliega, un campo que se marca al recibir el foco.
Animación cuando el movimiento tiene etapas, se repite, o tiene que ocurrir sin que nadie haga nada: un indicador de carga, un aviso que entra solo, un elemento que llama la atención.
En caso de duda, la transición es la opción más simple: se resuelve con dos líneas. La animación queda para lo que la transición no alcanza.
@keyframes y un nombre; el elemento lo
reclama con animation-name.alternate ahorra la mitad del guion en los movimientos de ida y vuelta.