soy Kseso y esto EsCSS

Animación de títulos divididos para efectos de composición

Incursión con ejemplos de Furoya sobre cómo dividir titulares en partes más pequeñas que las letras que los conforman para aplicarles diferentes efectos a cada parte y al todo.

Animación de títulos divididos para efectos de composición

✎ 2
COLABORACIÓN AUTOR INVITADO

En el artículo Troceado de imagen con javascript y manejo de cada parte probamos algunas formas de descomponer una imagen para animar cada una de sus piezas hasta reconstruírla como un todo. El punto aquí es que el método sirve para cortar cualquier caja o bloque y modificar cada parte simulando que están enteros.

Si bien en textos hay algunas limitaciones y complicaciones. No es imposible animar por partes un título de letras grandes usando clip.

Animación de títulos divididos para efectos de composición

Por lo general estos efectos requieren "cortes" muy pequeños y exigen bastante al navegador si la animación es compleja. Vamos a aprovechar las próximas fiestas (cristianas y occidentales) para probar algunos títulos alusivos, sin llegar a los clips de 1 pixel que se usaban antiguamente para simular texto rotado o espejado.

Y ya que hablamos de antigüedades, recordemos que el clip:rect() aún es compatible, pero está condenado a desaparecer porque ya fue retirado de la plana. En su lugar se usará clip-path, cuando sea totalmente soportado por los navegadores, claro.

Texto con destellos

Éste es un efecto que se puede hacer más fácil con caracteres "transparentados" que permitan ver (por ejemplo) un gif animado como fondo que tenga los destellos. O con los filtros y modos de fusión CSS.

Pero nosotros no hacemos las cosas fáciles; y así nos quedan xD

See the Pen Title with animated flashes. by solipsistaCP (@solipsistacp) on CodePen.

Vamos a aclarar que a diferencia de las imágenes cortadas, aquí cada contenedor de texto no se corta, sino que ocupa su lugar (0:0) y solamente se muestra un cuadrito que será una parte del texto para animar (dejando el resto invisible). Aunque en realidad animamos toda la capa desde las coordenadas iniciales, y por supuesto tenemos que replicar —con su texto— tantas como cuadritos necesitemos.

En el ejemplo, insisto, cada cuadrado que vemos con un color diferente es un div que contiene todo el texto, y con un clip muestra solamente esa parte de 5 pixeles de lado. Así que lo que ven no es en realidad un texto, sino algunos cientos armados como un puzzle (si no me creen, prueben a seleccionarlo con el ratón).

/* EJECUTA LA FUNCIÓN AL CARGAR */ onload = destella; function destella(){ /* REFIERE AL TÍTULO */ var titulo = document.querySelector("p"); /* CAPTURA EL CONTENIDO DEL TÍTULO */ var texto = titulo.innerHTML; /* MIDE EL TAMAÑO DEL TÍTULO */ var ancho = titulo.offsetWidth; var alto = titulo.offsetHeight; /* FIJA EL TAMAÑO DEL TÍTULO */ titulo.style.width = ancho+"px"; titulo.style.height = alto+"px"; /* CALCULA LA CANTIDAD DE CUADROS */ var totalCuadrosAncho = ancho/5; var totalCuadrosAlto = alto/5; /* VACÍA EL TÍTULO CONTENEDOR DE TEXTO */ titulo.innerHTML = ""; /* CREA LOS CUADROS Y LES DA FORMATO */ for(f=0; f<totalCuadrosAlto; f++) { for(c=0; c<totalCuadrosAncho; c++) { /* UN DIV PARA CADA FAJA */ cuadrito = document.createElement("div"); /* PONE EL MISMO TEXTO ORIGINAL */ cuadrito.innerHTML = texto; /* DEJA VISIBLE UN CLIP DE 5px DE LADO, CADA UNO A CONTINUACIÓN DEL OTRO */ cuadrito.style.clip = "rect("+ f*5 +"px, "+ (c+1)*5 +"px, "+ (f+1)*5 +"px, "+ c*5 +"px)"; /* RETRASA LA ANIMACIÓN CSS DE CADA CUADRO AL AZAR */ cuadrito.style.animationDelay = Math.floor(Math.random()*4000)+"ms"; /* AGREGA LA CAPA AL CONTENEDOR */ titulo.appendChild(cuadrito); } } }

Hay mucha cuenta, pero el método es sencillo y lo que da más trabajo es reescribir el contenido del título.

  • Esperamos a que se cargue el documento y ejecutamos la única función.
  • Allí tenemos variables para ubicar el título, su texto, su ancho y su alto. Estos últimos no son exactos, porque toman también el borde, pero para el caso nos sirven bien en las líneas siguientes donde fijamos el tamaño del mismo contenedor a una medida donde quepa el texto sin inconvenientes.
  • Después calculamos cuantos cuadros de 5px de lado caben a lo ancho y a lo alto del contenedor, para saber cuantas vueltas darán los bucles que arman los clips tabulados. Y borramos el contenido del título antes de rellenarlo de nuevo.
  • El método es un viejo conocido, y no lo desarrollo de nuevo. Aunque nos podemos detener un poco en las coordenadas del clip(rect). Como sabemos, esta propiedad muestra solamente un rectángulo de algún elemento, y los valores para recortar son la horizontal de arriba, la vertical de la derecha, la horizontal de abajo y la vertical de la izquierda. Para escribirlas como estilo inline en cada nuevo div aprovechamos las variables f y c que son para filas (Y) y columnas (X). En la primera vuelta ambos valores son "0", así que el clip nos queda:

    "rect("+ f*5 +"px, "+ (c+1)*5 +"px, "+ (f+1)*5 +"px, "+ c*5 +"px)" "rect(" 0 px, 5 px, 5 px, 0 px)"

    por lo que el primer cuadrito va a mostrar desde la coordenada 0:0 hasta la 5:5, y si seguimos aumentando a c resulta que la siguiente capa va a mostrar el clip 5:0 10:5, es decir, el cuadrito siguiente a la derecha. Al aumentar f se empieza a crear la siguiente fila en la capa que le corresponda.
  • Un animation-delay aleatorio nos asegura que la secuencia de colores (idéntica para cada capa) se vea desfasada una con otra, creando la ilusión de destellos. Terminamos, como siempre, metiendo el código generado con javascript en su contenedor, a la espera de que el CSS haga su trabajo.

Texto reconstituído.

Ahora que entendemos cómo se hace para mostrar sólo un cuadradito del texto, ya podemos hacer más fantasías con los títulos.

Éste sería un efecto "confeti", que trae montones de papelitos desde fuera del contenedor y los pega en su sitio para formar las letras. Una vez terminado, le cambia el color.

See the Pen Confetti title with animation. by solipsistaCP (@solipsistacp) on CodePen.

Si no vieron la animación, pueden recargar el pen.

/* EJECUTA LA FUNCIÓN AL CARGAR (PUEDE TENER UN RETRASO PARA QUE MUESTRE LA FUENTE) */ onload = setTimeout(confeti,1000); function confeti(){ /* REFIERE AL TÍTULO */ var titulo = document.querySelector("p"); /* CAPTURA EL CONTENIDO DEL TÍTULO */ var texto = titulo.innerHTML; /* MIDE EL TAMAÑO DEL TÍTULO */ var ancho = titulo.offsetWidth; var alto = titulo.offsetHeight; /* CALCULA LA MITAD DEL TAMAÑO DEL TÍTULO */ var medioAncho = ancho/2; var medioAlto = alto/2; /* FIJA EL TAMAÑO DEL TÍTULO */ titulo.style.width = ancho+"px"; titulo.style.height = alto+"px"; /* CALCULA LA CANTIDAD DE CUADROS 10px*/ var totalCuadrosAncho = ancho/10; var totalCuadrosAlto = alto/10; /* VACÍA EL TÍTULO CONTENEDOR DE TEXTO */ titulo.innerHTML = ""; /* CREA LOS CUADROS Y LES DA FORMATO */ for(f=0; f<totalCuadrosAlto; f++) { for(c=0; c<totalCuadrosAncho; c++) { /* UN DIV PARA CADA CUADRO */ cuadrito = document.createElement("div"); /* PONE EL MISMO TEXTO ORIGINAL */ cuadrito.innerHTML = texto; /* DEJA VISIBLE UN CLIP DE 10px DE LADO, CADA UNO A CONTINUACIÓN DEL OTRO */ cuadrito.style.clip = "rect("+ f*10 +"px, "+((c+1)*10)+"px, "+ (f+1)*10 +"px, "+ c*10 +"px)"; /* RETRASA LA ANIMACIÓN CSS DE CADA CUADRO AL AZAR */ cuadrito.style.transitionDelay = Math.floor(Math.random()*3000)+"ms"; /* ALTERNA POSICIONES FUERA DEL CONTENEDOR USANDO LÓGICA "PAR" O "IMPAR"*/ if(c%2==0){ /* CREA VALOR AL AZAR MENOR QUE EL ANCHO */ azar = Math.floor(Math.random()*medioAncho); /* PONE EL CUADRO UN alto ARRIBA O ABAJO, SI ES DE LA MITAD SUPERIOR O INFERIOR */ cuadrito.style.top = (f*10<medioAlto)? -alto+"px" : alto+"px"; /* PONE EL CUADRO AL AZAR A IZQUIERDA O DERECHA, DEPENDIENDO SI ES DE LA MITAD IZQUIERDA O DERECHA */ cuadrito.style.left = (c*10%lt;medioAncho)? -azar+"px" : azar+"px"; } else { /* CREA VALOR AL AZAR MENOR QUE EL ALTO */ azar = Math.floor(Math.random()*alto); /* PONE EL CUADRO AL AZAR ARRIBA O ABAJO, DEPENDIENDO SI ES DE LA MITAD SUPERIOR O INFERIOR */ cuadrito.style.top = (f*10<medioAlto)? -azar+"px" : azar+"px"; /* PONE EL CUADRO UN medioAncho A IZQUIERDA O DERECHA, SI ES DE LA MITAD IZQUIERDA O DERECHA */ cuadrito.style.left = (c*10<medioAncho)? -ancho+"px" : ancho+"px"; } /* AGREGA LA CAPA AL CONTENEDOR */ titulo.appendChild(cuadrito); } } /* ESPERA 1 SEGUNDO PARA INICIAR ANIMACIÓN */ setTimeout(function(){ /* HACE VISIBLE EL CONTENEDOR */ titulo.style.visibility = "visible"; /* CALCULA EL TOTAL DE CUADROS */ totalCuadros = totalCuadrosAncho*totalCuadrosAlto; /* REFIERE A LA COLECCIÓN */ coleccionCuadros = document.querySelectorAll("p div"); /* LE ASIGNA A CADA DIV LAS COORDENADAS 0:0 */ for(d=0; d<totalCuadros; d++){ coleccionCuadros[d].style.top = 0; coleccionCuadros[d].style.left = 0; }; /* CAMBIA EL COLOR DE TEXTO */ titulo.style.color = "lime"; }, 1000); } En éste la complicación está en el algoritmo que desparrama los div's. La idea es mover los de la mitad superior y la mitad derecha hacia arriba y hacia la derecha, alternando las posiciones aprovechando una variable incremental, porque lo más práctico para alternar es mandar las pares a un lado y las impares al otro. El mismo trabajo se hace para los otros cuadrantes, enviando las capas para su lado. Para ver realmente cómo se acomodan, se puede quitar el overflow:hidden; visibility:hidden del párrafo. Éste último está para evitar que se vea el texto mientras el navegador se demora en cargar la nueva fuente y toma sus medidas, y luego se revierte en el escript. Por supuesto, se puede aplicar el mecanismo a cualquiera de las demos, pero yo tuve problemas en ésta con un Edge y aproveché para agregar el parche.
  • El ejemplo es basicamente igual al anterior, así que vamos ya a las novedades. Las variables que contienen la mitad del ancho y del alto se usan para saber en qué cuadrante está el cuadrito que muestra esa capa.
  • Ya en el loop, vemos que los cuadros son de 10 pixeles de lado, cuanto más grandes, menos recursos consume el efecto.
  • La variable que usamos para separar las capas pares de las impares es la incremental c, que crece de a 1. Entonces para cada vuelta le asignamos un formato, que también dependerá del cuadrante, como mencionamos. Primero creamos para los pares un valor al azar menor al ancho (o al medio ancho, si no queremos que se mueva muy lejos) y luego confirmamos si la coordenada vertical está en la mitad de arriba (y la movemos 100% para arriba) o si está en la de abajo (y la movemos para abajo); luego hacemos lo mismo con la coordenada horizontal, pero ahora la movemos el valor aleatorio hacia el lado que corresponda. Esto es para que de alguna forma la animación parezca caótica y que no se muevan "en fila" hasta su posición final. Luego hacemos lo propio con las capas impares, pero invirtiendo la mecánica: las coordenadas se ponen al 100% en "x" (para el lado que le corresponda) y con el valor aleatorio en "y" (también para su lado). Una vez que los tenemos hechos, los vamos agregando al contenedor.
  • Al final tenemos una función demorada 1 segundo que luego de hacer visible el contenedor recorre todos los div y les reescribe la posición para devolverla a 0:0, que es lo que va a rearmar el texto desde dondequiera que estén las capas.
  • Por último, cambia el color del texto, con una demora en el CSS de 6 segundos, para darle tiempo a hacer todos los movimientos antes de ponerse verde.

Por supuesto, esta vez la animación ya no será animation sino transition

Texto craquelé

Último ejemplo, por hoy y por este año.

Vamos a ver algo similar al anterior, pero con un movimiento más corto y continuo, que da una ilusión de craquelado en las letras, y entre los cortes se puede ver el fondo del contenedor. Es un poco más llamativo que si simplemente transparentamos los caracteres y mostramos un fondo con esa animación; porque si vemos con cuidado, las letras se deforman.

See the Pen Cracked title with animation. by solipsistaCP (@solipsistacp) on CodePen.

/* EJECUTA LA FUNCIÓN AL CARGAR */ onload = cuarteado; function cuarteado(){ /* REFIERE AL TÍTULO */ var titulo = document.querySelector("p"); /* CAPTURA EL CONTENIDO DEL TÍTULO */ var texto = titulo.innerHTML; /* MIDE EL TAMAÑO DEL TÍTULO */ var ancho = titulo.offsetWidth; var alto = titulo.offsetHeight; /* FIJA EL TAMAÑO DEL TÍTULO */ titulo.style.width = ancho+"px"; titulo.style.height = alto+"px"; /* CALCULA LA CANTIDAD DE CUADROS */ var totalCuadrosAncho = ancho/5; var totalCuadrosAlto = alto/5; /* VACÍA EL TÍTULO CONTENEDOR DE TEXTO */ titulo.innerHTML = ""; /* CREA LOS CUADROS Y LES DA FORMATO */ for(f=0; f<totalCuadrosAlto; f++) { for(c=0; c<totalCuadrosAncho; c++) { /* UN DIV PARA CADA CUADRO */ cuadrito = document.createElement("div"); /* PONE EL MISMO TEXTO ORIGINAL */ cuadrito.innerHTML = texto; /* DEJA VISIBLE UN CLIP DE 5px DE LADO, CADA UNO A CONTINUACIÓN DEL OTRO */ cuadrito.style.clip = "rect("+ f*5 +"px, "+((c+1)*5)+"px, "+ (f+1)*5 +"px, "+ c*5 +"px)"; /* RETRASA LA ANIMACIÓN CSS DE CADA CUADRO AL AZAR */ cuadrito.style.animationDelay = (Math.random()*3000)+"ms"; /* AGREGA LA CAPA AL CONTENEDOR */ titulo.appendChild(cuadrito); } } }

Y es demasiado simple. No necesita explicación.

Además de crear las capas dentro del contenedor, lo único que hace el script es asignar una demora al azar para que el CSS no muestre una animación tan lineal.

Por el momento terminamos con las animaciones cortando imágenes o texto. Ya tienen bastante para entretenerse inventando sus propios efectos por lo menos hasta el año que viene.

Muchas felicidades a los creyentes, y también a los que no.

Furoya: Autor del artículo

Artículo original de Furoya.
La intención del autor con sus colaboraciones no es que los artículos sirvan para hacer un copy&paste de códigos sino que comprendas y aprendas la lógica y el cómo trabaja javaScript.
Y a partir de lo expuesto experimentes tú.
El autor del post y el editor del blog te animamos a que plantees tus dudas o reflexiones y que compartas tus realizaciones en base a lo expuesto en los comentarios. Recuerda que puedes incluir pens (ejemplos en Codepen.io) en ellos.