Emezeta.com

Tutorial CSS: Cómo crear un objeto que baile


El pasado noviembre participé en Codevember 2017, una iniciativa basada en Inktober (ink + october), un reto temático dentro del ámbito de la ilustración llevado a cabo durante el mes de Octubre, donde cada participante debe realizar una ilustración diaria con la temática propuesta para ese día, dentro de sus 24h correspondientes.

De la misma forma, Codevember (Code + november) también es un reto creativo donde el participante debe crear una demo sobre un concepto diario, sólo que esta vez lo tiene que hacer utilizando tecnologías web, como por ejemplo, HTML5, CSS3 y/o Javascript.

En mi repositorio de GitHub Codevember2017 se pueden encontrar y visualizar las demos que publiqué en cada día del mes, con información sobre ellas y enlaces a CodePen/GitHub.

Concepto del día: «Pen»

En este artículo me gustaría explicar un poco, a modo de tutorial, el «cómo se hizo» de la demo correspondiente al Día 28: «Pen», explicado paso a paso.

Como su propia palabra indica, la idea original con «pen» era precisamente hacer una demo relacionada con bolígrafos, plumas, estilográficas o algo similar, pero preferí aprovechar el doble sentido de la palabra «pen» en este contexto, que es el nombre que se le da a las demos de la Plataforma Codepen. De esta forma, me pareció más original un navegador que mostrase un pen en blanco de Codepen.

Tutorial: Cómo crear un objeto que baile con CSS

Si además, lo caricaturizamos un poco añadiéndole algunos extras, eso podría quedar bien. ¡Y todo ello sin una línea de Javascript! ¡Vamos a ponernos a ello!

1. Análisis y estructura

Lo primero es tener la estructura a crear bien clara y definida. Si entramos a un pen en blanco en Codepen veremos perfectamente como es esta pantalla y los elementos que la componen. Tenemos que simplificar esa apariencia y convertirla en algo muy básico pero que tenga la misma esencia.

Codepen en blanco: Un pen

Así pues, definimos la estructura general de Codepen: lo dividiremos en varias secciones, una parte superior .top que incluirá el titular y los botones, una parte central .mid que contendrá las ventanas de HTML, CSS y JS, una parte inferior .preview donde se previsualizan los contenidos y finalmente, una capa .bottom como barra inferior.

Modelo de zonas de Codepen

Todo ello estará en el interior de una capa .body, que a su vez estará en el interior de una capa .codepen que utilizaremos para separar los pies del cuerpo más adelante. De momento, simplemente colocamos un comentario que más adelante sustituiremos:

<div class="codepen">
  <div class="body">
    <div class="top"></div>
    <div class="mid"></div>
    <div class="preview"></div>
    <div class="bottom"></div>
  </div>
  <!-- feet -->
</div>

Pero todo esto es inútil si no le damos estilo. Para empezar, añadiremos algunas líneas para utilizar un color de fondo en toda la página (un gradiente, que siempre queda más atractivo) y precargamos la tipografía Lato que usaremos más adelante:

@import url('https://fonts.googleapis.com/css?family=Montserrat:600|Lato');

html {
  background:linear-gradient(100deg, #444, #777);
}

También le daremos estilo a la estructura del modelo que hemos planificado antes. Básicamente, tamaños de ancho y/o alto, márgenes y colores de fondo para que sea bien visible la parte principal:

.body {
  width:600px;
  margin:2em auto;    /* Para centrarlo todo */
  box-shadow:0 0 15px #333;
}

.top {
  height:40px;
  background:#000;
}

.mid {
  width:100%;
  background:#343436;
  min-height:165px;
}

.preview {
  background:#FFF;
  min-height:150px;
}

.bottom {
  height:15px;
  background:#343436;
}

Una vez hecho, tendremos el cuerpo de la ventana del navegador. No olvidar el box-shadow del .body para darle un buen aspecto de suavidad y profundidad con una sombra (o varias si es necesario).

Paso 1: Estructura principal de Codepen

2. Ventanas HTML/CSS/JS

Para que empiece a tomar forma, es necesario colocar las tres ventanas A, B y C donde se escribe el código HTML, CSS y JS en Codepen. Les aplicaremos una clase .tab común a las tres, para darle los estilos que deben tener todas. Posteriormente, si lo necesitaramos podríamos usar las otras clases para hacer referencia a una ventana específica. Todo ello, por supuesto, en el interior de la clase .mid:

<div class="mid">
  <div class="tab html"></div>
  <div class="tab css"></div>
  <div class="tab js"></div>
</div>

Con un border-top simulamos el título de cada ventana como si fuera una sombra, un efecto bastante bonito en nuestro ejemplo. Sin embargo, al ser elementos block cada ventana se colocará una debaja de otra. Para solucionar esto, establecemos la clase .mid como un contenedor de elementos flexbox con display:flex:

/* Sería más eficiente usar .tab, pero así se ve mejor */
.mid .tab {
  width:100%;
  height:125px;
  margin:10px;
  background:#1e1f21;
  border-top:20px solid #111;
}

.mid {
  /* ... */
  display:flex;
}

Ya se va pareciendo más a Codepen. ¡Sigamos!

3. Franja superior

Otro de los detalles importantes de la página de Codepen que le da el aspecto reconocible son los botones de la parte superior-derecha, así como el título y el logo. Vamos a ponernos con ello, en el interior del elemento con clase .top.

Si nos fijamos bien, establecemos dos capas iniciales: una contendrá el logotipo y el titular de la página, y la otra los botones y la foto de perfil del usuario, en este caso, la de mi perfil de CodePen :-P

Algo similar al boceto de la siguiente imagen:

Boceto superior de Codepen

Varios detalles:

  • De momento, dentro de .logo no colocamos ningún logotipo. Más adelante veremos como colocar el logo con SVG, ahorrándonos imágenes externas y utilizando un SVG, que es más versátil.
  • En el interior de .title irá el título del pen de Codepen. Aprovecharemos y colocamos el atributo contentEditable, para que el usuario pueda cambiar el texto si lo desea.
  • Finalmente, en .buttons colocamos tres botones y un enlace que será la foto de perfil (avatar) del usuario. En esto profundizaremos más adelante.
<div class="top">
  <div>
    <div class="logo"></div>
    <div class="title" contentEditable>Untitled</div>
  </div>
  <div class="buttons">
    <div class="button"></div>
    <div class="button"></div>
    <div class="button"></div>
    <a class="avatar" href="https://codepen.io/manz/"></a>
  </div>
</div>

Ahora lo necesario es darle forma y colocarlo todo, por lo que toca centrarse en el CSS. Por defecto, sólo haciendo los cambios de HTML ocurren varias cosas que hay que tener claras. Al establecer dos capas dentro de .top, estas se colocan una debajo de otra, ya que son elementos de tipo block. Esto se puede ver fácilmente, si ponemos temporalmente un .top>div { background:fuchsia } que cambia el color de fondo del elemento. Para evitar este comportamiento de colocación, haremos uso de nuevo de Flexbox:

.top {
  /* ... */
  display:flex;
  justify-content:space-between;
  align-items:center;
  padding:0 15px;
}

.top > div {
  display:flex;
}

Con flexbox, los hijos se comportan como elementos flexibles que se colocan en horizontal «automágicamente» y haciendo uso de justify-content:space-between ya conseguimos lo que buscamos en el boceto previo.

Además, también habría que usar flexbox en las capas hijas de .top, para colocar sus elementos hijos de la misma forma. De resto, sólo quedaría ir retocando el diseño en los demás elementos y puliendo detalles menores de diseño que veremos a continuación.

4. Titulo de la página

CodePen muestra el título de la página en la parte superior izquierda, seguido del icono de un lápiz. Para simular esta apariencia, podemos establecer una tipografía, color y otros como separarlo del borde (espaciado) u ocultar ese borde azul de los elementos interactivos (outline):

.top .title {
  color:#FFF;
  padding-left:15px;
  font-family:Lato;
  outline:0;
}

Podríamos dejar esta parte aquí, pero por hacerlo un poco más fiel a CodePen, podemos utilizar el emoji Right Upper Pencil para colocarlo a la derecha del texto de título. Para ello realizamos varios pasos:

  • Al elemento .title le asignamos una posición relativa. De este modo, todo lo que esté en su interior con posicionamiento absoluto, tomará este elemento como marco de referencia.

  • Creamos un pseudoelemento ::after que es el que contendrá el emoji del lápiz. Asignándole posicionamiento absoluto, podemos colocarlo justo después del texto. Si usamos el scale de transform, también podremos darle la vuelta (en el eje X y en el eje Y) para que aparezca igual que en CodePen:

.top .title {
  /* ... */
  position:relative;
}

.top .title::after {
  position:absolute;
  right:-20px;
  top:1px;
  content:"?";
  color:#FFF;
  transform:scale(-1);
}

5. Botones de Codepen

En la zona superior derecha, tenemos una parte donde hay que colocar varios botones. Para ello lo principal será darle estilo a estos botones, con la clase .button:

  • Le damos el mismo color que usan en Codepen.
  • Redondeamos ligeramente las esquinas.
  • Cambiamos tamaños y márgenes.
  • Damos estilos diferentes al primer y segundo botón con :nth-child.

Nos quedaríamos con algo parecido a esto:

.buttons .button {
  background:#343436;
  width:70px;
  height:23px;
  margin:0 5px;
  border-radius:4px;
  border-top:2px solid #343436;
}

/* Primer botón: SAVE */
.button:nth-child(1) {
  width:50px;
  border-top-color:#fedd31;
}
/* Segundo botón: Settings */
.button:nth-child(2) {
  width:60px;
}

Por último, hay que establecer el avatar del perfil del usuario que CodePen muestra en la parte superior derecha. Esto es muy sencillo. Simplemente le damos estilo a la etiqueta <a> con clase .avatar, asignándole un tamaño de 30x25 píxels y le establecemos nuestra imagen de fondo con background:

.buttons .avatar {
  width:30px;
  height:25px;
  border-radius:3px;
  margin:0 5px;
  background:#FFF url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/154065/profile/profile-80.jpg);
  background-size:cover;
}

¡Esto ya tiene mejor pinta! Pero aún falta darle algo de personalidad… Vamos a antropomorfizar nuestra ventana del navegador de Codepen y a darle un aspecto más humano.

Paso 2: Codepen con barra superior y botones

6. ¡Antropomorfosis!

En primer lugar, vamos a dibujarle unos ojitos y una boca. Insertar etiquetas HTML sobre lo que ya tenemos no sería una solución muy limpia, ya que estaríamos «ensuciando» lo que hemos hecho hasta ahora, así que vamos a utilizar pseudoelementos y posicionamiento para este fin.

En primer lugar, con ayuda de la posición absoluta, calc() y las unidades de viewport, colocamos a nuestro personaje en la parte baja de nuestra página. Hacemos esto porque, posteriormente, necesitaremos que aparezca justo «encima» del pie de ventana del navegador.

.codepen {
  position:absolute;
  left:calc(50vw - 300px);
  bottom:32px;
  height:472px;
}

Una vez hecho esto, vamos a crear los ojos. Para ello, utilizaremos el pseudoelemento ::before para el ojo izquierdo y el pseudoelemento ::after para el ojo derecho. Ambos sobre la zona central de clase .mid. Lo interesante de este sistema es que utilizaremos la generación de contenido previamente no existente con CSS.

Con la propiedad content generamos un espacio en blanco al que le damos cierto estilo: unas dimensiones de ancho, alto, redondeamos las esquinas y coloreamos el fondo. Utilizamos un gradiente circular de negro (exterior) a blanco (interior) con cambio brusco (de golpe) sin difuminar, con lo que conseguimos mostrar un aparente circulo blanco dentro de un circulo negro, que simula ser un ojo negro con brillo blanco.

Paso 3: Codepen antropomórfico

Por último, con posicionamiento absoluto, colocamos el ojo entre las ventanas B y C. En este momento, ambos ojos (::after y ::before) están situados en el mismo sitio con las mismas características, ya que comparten las mismas propiedades CSS. Por eso, establecemos un posicionamiento diferente en el .mid::before, colocando el ojo entre la ventana A y B.

Además, con un transform:scale(-1,1) hacemos el efecto espejo con el ojo, eliminando la sensación de estar clonado y haciendo que no mire hacia el mismo lado, sino en dirección opuesta.

/* Fijamos como marco de referencia: Los ojos se colocarán en base a este */
.mid {
  position:relative;
}

/* Características comunes del ojo */
.mid::before,
.mid::after {
  content:" ";
  width:30px;
  height:30px;
  border-radius:50%;
  background:radial-gradient(20px at 13px 10px, #FFF 20%, #000 30%);
  /* Colocación */
  position:absolute;
  right:200px;
  top:100px;
}

.mid::before {
  left:200px;
  right:none;
  transform:scale(-1, 1);
}

Nos quedaría realizar una acción muy parecida para crear la boca del personaje. En este caso, creamos un rectángulo o cuadrado negro donde redondearemos las esquinas inferior izquierda e inferior derecha, obteniendo medio círculo que hará de boca del personaje.

.preview::after {
  content:" ";
  width:50px;
  height:30px;
  border-bottom-left-radius:50px;
  border-bottom-right-radius:50px;
  background:#000;
  /* Colocación */
  position:absolute;
  left:calc(50% - 25px);
  bottom:105px;
}

Por último, remarcar que la estamos colocando en base al elemento .mid, ya que hemos establecido un posicionamiento absoluto.

7. Logo Codepen

Habíamos pospuesto la colocación del logo de Codepen en la página, teniendo una capa .logo vacía, sin ningún contenido. En lugar de una imagen, vamos a colocar la siguiente etiqueta SVG en línea, donde tenemos el logotipo vectorial de Codepen:

<div class="logo">
  <svg id="codepen-box" viewBox="0 0 100 100" width="100%" height="100%">
    <path d="M100 34.2c-.4-2.6-3.3-4-5.3-5.3-3.6-2.4-7.1-4.7-10.7-7.1-8.5-5.7-17.1-11.4-25.6-17.1-2-1.3-4-2.7-6-4-1.4-1-3.3-1-4.8 0-5.7 3.8-11.5 7.7-17.2 11.5L5.2 29C3 30.4.1 31.8 0 34.8c-.1 3.3 0 6.7 0 10v16c0 2.9-.6 6.3 2.1 8.1 6.4 4.4 12.9 8.6 19.4 12.9 8 5.3 16 10.7 24 16 2.2 1.5 4.4 3.1 7.1 1.3 2.3-1.5 4.5-3 6.8-4.5 8.9-5.9 17.8-11.9 26.7-17.8l9.9-6.6c.6-.4 1.3-.8 1.9-1.3 1.4-1 2-2.4 2-4.1V37.3c.1-1.1.2-2.1.1-3.1 0-.1 0 .2 0 0zM54.3 12.3L88 34.8 73 44.9 54.3 32.4V12.3zm-8.6 0v20L27.1 44.8 12 34.8l33.7-22.5zM8.6 42.8L19.3 50 8.6 57.2V42.8zm37.1 44.9L12 65.2l15-10.1 18.6 12.5v20.1zM50 60.2L34.8 50 50 39.8 65.2 50 50 60.2zm4.3 27.5v-20l18.6-12.5 15 10.1-33.6 22.4zm37.1-30.5L80.7 50l10.8-7.2-.1 14.4z"/>
  </svg>
</div>

Inicialmente, el logotipo aparecerá en negro y con unas dimensiones muy grandes, pero aprovecharemos las características de SVG para solucionarlo con las 3 líneas de CSS siguientes.

Le aplicamos estilo al contenedor del elemento SVG, el elemento con clase .logo, donde se le indica que tenga unas dimensiones de 20x20 píxels y que utilizaremos la propiedad fill para rellenar de blanco los elementos SVG que se encuentren dentro de la etiqueta HTML con clase .logo:

.logo {
  width:20px;
  height:20px;
  fill:#FFF;
}

De esta forma, el logotipo de Codepen ahora si tiene un tamaño aceptable y se coloca perfectamente en nuestra demo. ¡Además al ser vectorial es mucho más versátil si en algún momento tenemos que cambiar tamaños!

8. Let’s go dancing!

Si algo aprendí en codevember es que todo lo que baile, siempre gusta más. Así que, nos falta convertir a nuestro navegador en un excelente bailarín. Y si tiene piernas, pues mucho mejor.

Mediante Inkscape diseño unas patitas, que guardamos en SVG, y mediante SVGO o su versión gráfica online, SVGOMG, optimizamos la imagen al máximo para que ocupe lo menos posible.

Una vez la tengamos lista, reemplazamos la etiqueta <svg> en cuestión por el comentario <!-- feet --> que colocamos en el primer punto de esta guía, justo tras cerrar la capa .body:

<svg class="feet" viewBox="0 0 50 40">
  <path d="m 33.865999,2.4621617 c 5.91069,24.0121503 -1.10825,35.4641003 -1.10825,35.4641003 h 11.08253"/>
  <path d="M 12.778709,2.4621617 C 6.8680187,26.474312 13.886959,37.926262 13.886959,37.926262 H 2.8044287"/>
</svg>

Aunque se podría indicar el CSS en la propia etiqueta HTML, mediante un atributo style, he preferido separarlo y tenerlo de forma independiente, para que el código sea más modular y limpio, aplicando las propiedades CSS para SVG necesarias desde el estilo CSS:

.feet {
  width:200px;
  position:relative;
  left:205px;
  top:-90px;
  z-index:-1;
}

.feet path {
  fill:none;
  stroke:#111;
  stroke-width:4px;
  stroke-linecap:round;
}

Propiedades como fill, stroke, stroke-width o stroke-linecap son estilos CSS para SVG que se aplican a la imagen SVG contenida en el elemento de clase .feet.

Paso 4: Codepen antropomórfico

¡Listo! ¡Ya tenemos las patitas! Sólo nos quedan dos cosas más para terminar.

9. Animación de baile

¡Nuestro personaje ya tiene piernas pero aún no baila! ¡Devuélveme mi dinero! Vamos a crear una animación CSS que simplemente se mueva de un lado a otro simulando que baile. Al tener cara de alegría y todo el cuerpo (salvo las patas) fuera del .body, no tenemos más que aplicar la animación a dicha capa.

La animación en cuestión será un simple movimiento de izquierda a derecha y viceversa, utilizando la función de transformación skew (muchas veces traducida como sesgar) y moviendo ligeramente hacia arriba y abajo. Bien coordinada, el efecto visual es bastante llamativo:

html {
  /* ... */
  overflow:hidden; /* Ocultamos barra de desplazamiento */
}

.body {
  /* ... */
  animation:dancing .75s ease 2s infinite;
}

@keyframes dancing {
  25% {
    transform:translateY(20px) skew(-2deg);
  }
  0%, 50%, 100% {
    transform:translateY(0) skew(0);
  }
  75% {
    transform:translateY(20px) skew(2deg);
  }
}

Le aplicamos dicha animación al .body, estableciendo una duración de 0,75 segundos de forma infinita (no termina nunca). El retardo inicial de 2 segundos es porque la demo original no muestra las patitas y ojos inicialmente, sino que deja pasar unos segundos antes de mostrarlos mientras muestra el cartel superior de Codevember. En este tutorial actual no haría falta ya que no estamos realizando esa acción.

¡Ya lo tenemos casi listo, como se puede ver en la siguiente imagen!

Dancing Codepen, un codepen bailongo

Si analizamos los keyframes, la animación alterna siempre en el estado normal del personaje (al principio, a la mitad y al final de la animación), moviéndose hacia arriba y hacia uno de los dos lados al 25% y al 75% de la animación. Esto hará que la animación sea fluida y no experimente cortes o movimientos bruscos.

10. ¿Y la música?

Bueno, pues sólo nos queda ponerle una canción en bucle que se escuche mientras el personaje baila. ¿Qué mejor que utilizar Bonetrousle, de Toby Fox, el tema del personaje Papyrus en el gran videojuego Undertale?

Podemos hacerlo utilizando la etiqueta <audio> de HTML5, utilizando los formatos MP3 y OGG para hacer compatible el audio en cualquier tipo de navegador y sistema operativo:

<audio autoplay loop>
  <source src="https://cdn.rawgit.com/ManzDev/codevember2017/master/assets/bonetrousle-tobyfox-by-bulby.mp3"/>
  <source src="https://cdn.rawgit.com/ManzDev/codevember2017/master/assets/bonetrousle-tobyfox-by-bulby.ogg"/>
</audio>

¡Listo! ¡Ya tenemos nuestro ejemplo terminado y funcionando! Puedes verlo en el siguiente enlace de Codepen: Codevember2017 - Day 28: Pen o en el siguiente widget:

En el widget, el sonido está deshabilitado. Si quieres escucharlo con sonido, accede al pen de ejemplo: Codepen: Codevember2017 - Day 28: Pen.

¡Espero que les haya gustado y se animen a hacer algún personaje parecido y compartirlo en los comentarios! En mi GitHub de Codevember2017 tienen una lista de todas las demos que presenté y publiqué durante el mes de noviembre con algunas anotaciones. Animo a los lectores a preguntar cualquier duda en los comentarios, así como de otra demo de la lista.