Menús desplegables animados con CSS3
Explicamos cómo crear un menú desplegable animado sin necesidad de JavaScript
Los menús desplegables son una interfaz de navegación muy común. Hasta ahora con CSS podíamos hacer que los vínculos de las subsecciones aparecieran sin necesidad de JavaScript, pero si queríamos hacer que el efecto de dicha aparición fuera progresivo, no nos quedaba más opción que programar una animación. Con CSS3, podemos definir el efecto en la hoja de estilo.
Hace ya casi tres años publiqué una prueba de -webkit-transition
, una propiedad que permitía especificar tiempos y modos de transición entre dos valores. Los experimentos de Webkit fueron la base del modulo de transiciones de CSS nivel 3, con el que podemos crear pequeñas animaciones.
¿Animaciones? ¿Cómo?
Lo que hacen los navegadores que soportan el módulo de transición es interpolar los pasos intermedios entre el valor de una o varias propiedades de CSS aplicadas a un elemento —o los pseudoelementos :before
y :after
— y el valor asignado al mismo cuando se cumplen las condiciones que hayamos elegido, por ejemplo, cuando un usuario interactúa con el elemento y se cambia la propiedad en :hover
.
La propiedades de transición —que, por cierto, no se heredan— son las siguientes:
transition-property
: indica la propiedad de CSS que queremos animar. No todas las propiedades pueden someterse a transiciones, pero ésta es la lista de las que sí. Su valor por defecto es all
, es decir, que si no se especifica ninguna propiedad, la transición se aplicará a todas las del elemento.
transition-duration
: indica el tiempo que debe durar la animación, y se puede indicar en segundos (s
) o milisegundos (ms
). Si no se indica una duración, la transición es instantánea.
-
transition-timing-function
: indica la manera en la que deben calcularse los valores intermedios de la transición, y se expresa por medio de una curva cúbica de Bézier, con el valor cubic-bezier(a,b,c,d)
, donde a, b, c y d son las coordenadas de P1 y P2, con valores entre 0 y 1; P0 y P3 son siempre (0,0) y (1,1) respectivamente.
No obstante, con buen criterio, la especificación ya define las más comunes por medio de palabras clave:
ease
: el valor por defecto, equivalente a cubic-bezier(0.25,0.1,0.25,1.0)
. Traducido quiere decir que la animación es un poco más lenta al comienzo y al final.
linear
: equivalente a cubic-bezier(0.0,0.0,1.0,1.0)
, la progresión del valor de la propiedad animada es aritmética, por lo que la transición es uniforme.
ease-in
: equivale a cubic-bezier(0.42,0,1.0,1.0)
; la animación acelera su velocidad a medida que progresa.
ease-out
: equivale a cubic-bezier(0,0,0.58,1.0)
; la animación decelera su velocidad a medida que progresa.
ease-in-out
: equivale a cubic-bezier(0.42,0,0.58,1.0)
; la animación primero acelera hasta la mitad de su duración y luego decelera. Se diferencia de ease
en que esta última acelera al principio, se mantiene un intervalo constante y luego decelera.
transition-delay
: si no queremos que la animación comience inmediatamente, podemos asignarle un retraso inicial, medido también en segundos (s
) o milisegundos (ms
).
Además, contamos con una propiedad abreviada, que es transition
, donde podemos indicar todos estos valores separados por espacios. Por ejemplo, en esta declaración:
transition: background-color 0.5s linear 0.25s;
los valores corresponden a la propiedad a animar, la duración de la animación, su tipo y el retraso inicial.
Genial. Pero antes de pasar a explicar cómo podemos emplear todo esto para hacer un menú desplegable, vamos a indicar unos cuantos puntos a tener en cuenta al trabajar con animaciones.
Algunas notas
¿Dónde indicar la transición?
Primero y más importante, las propiedades de transición se deben especificar en el elemento que va a sufrirlas. Parece obvio, pero al principio puede ser un tanto contraintuitivo.
Imaginemos que tenemos un vínculo en una barra de navegación y que queremos que su color de fondo cambie de manera progresiva cuando el usuario pase el ratón sobre él. Estas reglas…
a{
background-color: #D0D0D0;
}
a:hover{
background-color: #FF4040;
transition-duration: .5s;
}
…no funcionan. Las correctas son estas:
a{
background-color: #D0D0D0;
transition-duration: .5s;
}
a:hover{
background-color: #FF4040;
}
¿Se pueden indicar parámetros independientes para varias propiedades?
Sí, de varias formas, dependiendo de lo que resulte más cómodo para el autor.
Primero, podemos especificar cada propiedad con los distintos valores separados por comas:
a{
color: #FFF;
background: #333;
transition-property: color, background;
transition-duration: .5s, .75s;
transition-timing-function: ease-in-out;
transition-delay: .25s, 0;
}
Los valores se asignan en el orden en que se indican. Si faltan valores —como aquí en transition-timing-function
— estos se asignan por orden a las propiedades hasta agotarse y los restantes reciben los valores por defecto.
La segunda opción es especificar las dos transiciones abreviadas:
a{
color: #FFF;
background: #333;
transition: color .5s ease-in-out .25s;
transition: background .75s;
}
En este caso no especificamos para el fondo ni el tipo de animación ni el retardo, dado que sus valores son los aplicados por defecto.
Y, por último, podemos especificar todos los valores en una sola transición, separando cada animación por comas:
a{
color: #FFF;
background: #333;
transition: color .5s ease-in-out .25s, background .75s;
}
¿Y el soporte?
En el momento en que escribo, ninguno de los navegadores actuales ha implementado las propiedades estándar del módulo de transiciones, pero algunos sí soportan las versiones experimentales con los prefijos propietarios:
|
Prefijo |
Desde la versión |
---|
Firefox |
-moz- |
4 |
---|
Safari |
-webkit- |
3.1 |
---|
Safari (iOS) |
-webkit- |
3.2 |
---|
Chrome |
-webkit- |
4.0 |
---|
Opera |
-o- |
10.5 |
---|
Explorer |
De momento no hay soporte alguno |
---|
Nuestro ejemplo
El menú de navegación consta de una serie de listas anidadas con los vínculos. En el ejemplo hemos empleado muchas otras propiedades de CSS3, pero vamos a ceñirnos estrictamente a la animación del menú:
#navegacion li ul{
height:0;
overflow:hidden;
-moz-transition-duration:.3s;
-webkit-transition-duration:.3s;
-o-transition-duration:.3s;
transition-duration:.3s;
position:absolute;
}
#navegacion li:hover ul{
height:17em;
}
Simplemente variamos la altura, de 0
a 17em
. ¿Y por qué no simplemente cambiar el valor a auto
? Buena pregunta. En un principio es lo que probamos, pero parece que la animación no funciona si no se especifica un valor en alguna de las unidades de medida.
En la especificación hay una sección que indica cuando no debe iniciarse una animación, pero no parece que deba ser aplicable en este caso. Tal vez se trate de un bug —ya está reportado como tal en Bugzilla—, pero lo cierto es que ninguno de los navegadores que soportan transiciones animan el elemento con height:auto
.
¿CSS o JavaScript?
Hace años ya que hay cierta controversia sobre si una animación pertenece a la capa de presentación o a la de comportamiento —este artículo de Jonathan Snook es de 2007—. Personalmente entiendo que la capa de comportamiento, léase JavaScript, tiene que ver con la funcionalidad que añade a una página —validación de formularios, manipulación del DOM, etc.—, y no tanto con la interpolación entre dos valores relativos al aspecto de la presentación de un elemento. Incluso el W3C las define así: «Transitions are a presentational effect».
Pero no sólo se trata de una cuestión más o menos académica: las transiciones de CSS son más eficientes. Al soportarlas el navegador de forma nativa las animaciones pueden beneficiarse de la aceleración por hardware del disposivo en el que el navegador esté instalado, reducen el consumo de recursos por parte del motor de JavaScript y, lo más importante, como este lenguaje es «mono-hilo» (single-threaded) el efecto no interfiere con otras funciones.
Así pues, viendo que además degrada muy elegantemente, no hay motivo para no empezar a utilizar las transiciones ya mismo.
Esta entrada se publicó el 31 de julio de 2011, se archivó en , y fue etiquetada como animaciones, css3. Autor: Saúl González Fernández. Hay 9 comentarios ›.
Comentarios
Muy bien explicado, por fin encontré una página con los detalles que necesitaba. Gracias.
Excelente blog, muy bien explicado. Si hay alguna manera de ayudar para difundir tu blog no dudes en hacérmelo saber.
Bien explicado, la unica duda que tengo con respecto al menú, es cómo hacer que se superponga al texto, o sea, que el despliegue se haga por encima del contenido. Gracias.
@Mauricio: Prueba asignar una posicion relativa al elemento en el que incluyes la navegación y al del contenido; después al primero especifícale un
z-index
superior al del segundo.Hola.
Estoy intentando que cuando el menu se despliege, lo haga mas lento, es decir, que el submenu me aparezca a una velocidad más lenta, y si puede ser, cada linea a un tiempo.
No sé muy bien dónde aplicar la transición, ya que sí que consigo aplicarla para que al pasar el raton por encima, la pestaña cambie de color más lentamente, pero no para ese efecto que deseo.
Os copio el texto a ver si me decís dónde ponerlo.
Gracias.
@el Síndrome de Willy Fog: Nos resultaría más fácil verlo si nos envías una dirección donde podamos echar un vistazo a la página, pero creemos que lo que necesitas es asignar estas propiedades:
Sustituye la altura por
0
en lugar deauto
y eliminadisplay:none
. En el código la duración que hemos puesto es.3s
, sustitúyela por la que creas apropiada.Para esta regla, elimina
display:block
, y dale la altura que necesites para que se vean todas las opciones del menú.Prueba y nos comentas.
Hola Saúl,
No soy el tío que preguntó sobre como hacer que las listas desplegables de su menú salgan más lento pero, déjame decirte que me has salvado de muchas horas más de dolor de cabeza XD. Yo he estado intentando hacer que las listas de mi menú se desplieguen lento y no había podido desde hace más de una semana, no miento. Me dí cuenta desde hace 7 días que el efecto TRANSITION no afecta a los DISPLAY, a pesar de eso intentaba e intentaba todo terco pero no conseguía algo; no se me había ocurrido cambiar el tamaño de las listas desplegables para que funcione el efecto, como tú lo has explicado… muchas gracias por el código, me sirvió mucho…
Muchas gracias, por fin entendí que la propiedad
display
no acepta transiciones.:O ¡Muchas gracias, me ha servido mucho! Mil gracias ?