Para este cuarto y último artículo de nuestra pequeña serie sobre cargadores de un solo elemento, vamos a explorar patrones 3D. Al crear un elemento 3D, es difícil imaginar que un solo elemento HTML sea suficiente para simular algo así como las seis caras de un cubo. Pero tal vez podamos salirnos con la nuestra con algo más cubo-me gusta en lugar de mostrar solo los tres lados frontales de la forma, es totalmente posible y eso es lo que vamos a hacer juntos.

El cargador de cubos divididos

Aquí hay un cargador 3D donde un cubo se divide en dos partes, pero solo se hace con un solo elemento:

Cada mitad del cubo se hace usando un pseudo-elemento:

¡¿Guay, verdad?! Podemos usar un degradado cónico con CSS clip-path en el elemento ::before y ::after pseudos para simular las tres caras visibles de un cubo 3D. El margen negativo es lo que une los dos pseudos para superponerse y simular un cubo completo. ¡El resto de nuestro trabajo consiste principalmente en animar esas dos mitades para obtener cargadores que se vean bien!

Veamos una imagen que explica las matemáticas detrás de los puntos de ruta de recorte utilizados para crear este elemento con forma de cubo:

Tenemos nuestras variables y una ecuación, así que pongámoslas a trabajar. Primero, estableceremos nuestras variables y estableceremos el tamaño para el principal .loader elemento:

.loader {
  --s: 150px; /* control the size */
  --_d: calc(0.353 * var(--s)); /* 0.353 = sin(45deg)/2 */

  width: calc(var(--s) + var(--_d)); 
  aspect-ratio: 1;
  display: flex;
}

Nada demasiado loco hasta ahora. Tenemos una 150px cuadrado que se configura como un contenedor flexible. Ahora establecemos nuestros pseudos:

.loader::before,
.loader::after {
  content: "";
  flex: 1;
}

Esas son dos mitades en el .loader envase. Necesitamos pintarlos, así que ahí es donde entra en juego nuestro gradiente cónico:

.loader::before,
.loader::after {
  content: "";
  flex: 1;
  background:
    conic-gradient(from -90deg at calc(100% - var(--_d)) var(--_d),
    #fff 135deg, #666 0 270deg, #aaa 0);
}

El gradiente está ahí, pero se ve raro. Necesitamos recortarlo al elemento:

.loader::before,
.loader::after {
  content: "";
  flex: 1;
  background:
    conic-gradient(from -90deg at calc(100% - var(--_d)) var(--_d),
    #fff 135deg, #666 0 270deg, #aaa 0);
  clip-path:
    polygon(var(--_d) 0, 100% 0, 100% calc(100% - var(--_d)), calc(100% - var(--_d)) 100%, 0 100%, 0 var(--_d));
}

Asegurémonos de que las dos mitades se superpongan con un margen negativo:

.loader::before {
  margin-right: calc(var(--_d) / -2);
}

.loader::after {
  margin-left: calc(var(--_d) / -2);
}

¡Ahora hagamos que se muevan!

.loader::before,
.loader::after {
  /* same as before */
  animation: load 1.5s infinite cubic-bezier(0, .5, .5, 1.8) alternate;
}

.loader::after {
  /* same as before */
  animation-delay: -.75s
}

@keyframes load{
  0%, 40%   { transform: translateY(calc(var(--s) / -4)) }
  60%, 100% { transform: translateY(calc(var(--s) / 4)) }
}

Aquí está la demostración final una vez más:

El cargador de cubos de progreso

Usemos la misma técnica para crear un cargador de progreso 3D. Sí, todavía sólo un elemento!

No vamos a cambiar nada en lo que respecta a simular el cubo de la misma manera que lo hicimos antes, aparte de cambiar la altura y la relación de aspecto del cargador. La animación que estamos haciendo se basa en una técnica sorprendentemente fácil en la que actualizamos el ancho del lado izquierdo mientras que el lado derecho llena el espacio restante, gracias a flex-grow: 1.

El primer paso es agregar algo de transparencia al lado derecho usando opacity:

Esto simula el efecto de que un lado del cubo está lleno mientras que el otro está vacío. Luego actualizamos el color del lado izquierdo. Para hacer eso, actualizamos los tres colores dentro del degradado cónico o lo hacemos agregando un color de fondo con un background-blend-mode:

.loader::before {
  background-color: #CC333F; /* control the color here */
  background-blend-mode: multiply;
}

Este truco solo nos permite actualizar el color una sola vez. El lado derecho del cargador se mezcla con los tres tonos de blanco del degradado cónico para crear tres nuevos tonos de nuestro color, aunque solo estamos usando un valor de color. ¡Trampas de colores!

Vamos a animar el ancho del lado izquierdo del cargador:

Vaya, ¡la animación es un poco extraña al principio! ¿Observe cómo comienza fuera del cubo? Esto se debe a que estamos comenzando la animación en el 0% ancho. Pero debido a la clip-path y el margen negativo que estamos usando, lo que debemos hacer en su lugar es comenzar desde nuestro --_d variable, que usamos para definir la clip-path puntos y el margen negativo:

@keyframes load {
  0%,
  5% {width: var(--_d); }
  95%,
  100% {width: 100%; }
}

Eso es un poco mejor:

Pero podemos hacer que esta animación sea aún más fluida. ¿Notaste que nos falta algo? Déjame mostrarte una captura de pantalla para comparar cómo debería verse la demostración final con la última demostración:

¡Es la cara inferior del cubo! Dado que el segundo elemento es transparente, necesitamos ver la cara inferior de ese rectángulo como puede ver en el ejemplo de la izquierda. Es sutil, ¡pero debería estar ahí!

Podemos agregar un degradado al elemento principal y recortarlo como hicimos con los pseudos:

background: linear-gradient(#fff1 0 0) bottom / 100% var(--_d) no-repeat;

Aquí está el código completo una vez que todo está unido:

.loader {
  --s: 100px; /* control the size */
  --_d: calc(0.353*var(--s)); /* 0.353 = sin(45deg) / 2 */

  height: var(--s); 
  aspect-ratio: 3;
  display: flex;
  background: linear-gradient(#fff1 0 0) bottom / 100% var(--_d) no-repeat;
  clip-path: polygon(var(--_d) 0, 100% 0, 100% calc(100% - var(--_d)), calc(100% - var(--_d)) 100%, 0 100%, 0 var(--_d));
}
.loader::before,
.loader::after {
  content: "";
  clip-path: inherit;
  background:
    conic-gradient(from -90deg at calc(100% - var(--_d)) var(--_d),
     #fff 135deg, #666 0 270deg, #aaa 0);
}
.loader::before {
  background-color: #CC333F; /* control the color here */
  background-blend-mode: multiply;
  margin-right: calc(var(--_d) / -2);
  animation: load 2.5s infinite linear;
}
.loader:after {
  flex: 1;
  margin-left: calc(var(--_d) / -2);
  opacity: 0.4;
}

@keyframes load {
  0%,
  5% { width: var(--_d); }
  95%,
  100% { width: 100%; }
}

¡Eso es todo! Acabamos de utilizar una técnica inteligente que utiliza pseudoelementos, degradados cónicos, recorte, combinación de fondo y márgenes negativos para obtener, no uno, sino dos cargadores 3D atractivos con nada más que un solo elemento en el marcado.

Más 3D

Todavía podemos ir más allá y simular una cantidad infinita de cubos 3D usando un elemento, ¡sí, es posible! Aquí hay una cuadrícula de cubos:

Esta demostración y las siguientes demostraciones no son compatibles con Safari en el momento de escribir este artículo.

loco, ¿verdad? Ahora estamos creando un patrón repetido de cubos hechos con un solo elemento… ¡y sin pseudos tampoco! No entraré en detalles sobre las matemáticas que estamos usando (hay números muy específicos allí), pero aquí hay una figura para visualizar cómo llegamos aquí:

Primero usamos un conic-gradient para crear el patrón de cubo repetitivo. La repetición del patrón está controlada por tres variables:

  • --size: Fiel a su nombre, esto controla el tamaño de cada cubo.
  • --m: Esto representa el número de columnas.
  • --n: Este es el número de filas.
  • --gap: este es el espacio o distancia entre los cubos
.cube {
  --size: 40px; 
  --m: 4; 
  --n: 5;
  --gap :10px;

  aspect-ratio: var(--m) / var(--n);
  width: calc(var(--m) * (1.353 * var(--size) + var(--gap)));
  background:
    conic-gradient(from -90deg at var(--size) calc(0.353 * var(--size)),
      #249FAB 135deg, #81C5A3 0 270deg, #26609D 0) /* update the colors here */
    0 0 / calc(100% / var(--m)) calc(100% / var(--n));
}

Luego aplicamos una capa de máscara usando otro patrón que tenga el mismo tamaño. Esta es la parte más complicada de esta idea. Usando una combinación de un linear-gradient y un conic-gradient Cortaremos algunas partes de nuestro elemento para mantener visibles solo las formas del cubo.

.cube {
  /* etc. */
  mask: 
    linear-gradient(to bottom right,
       #0000 calc(0.25 * var(--size)),
       #000 0 calc(100% - calc(0.25 * var(--size)) - 1.414 * var(--gap)),
       #0000 0),
    conic-gradient(from -90deg at right var(--gap) bottom var(--gap), #000 90deg, #0000 0);  
  mask-size: calc(100% / var(--m)) calc(100% / var(--n));
  mask-composite: intersect;
}

El código puede parecer un poco complejo, pero gracias a las variables CSS todo lo que tenemos que hacer es actualizar algunos valores para controlar nuestra matriz de cubos. ¿Necesita una cuadrícula de 10⨉10? Actualizar el --m y --n variables a 10. ¿Necesita un espacio más amplio entre los cubos? Actualizar el --gap valor. Los valores de color solo se usan una vez, ¡así que actualícelos para una nueva paleta de colores!

Ahora que tenemos otra técnica 3D, usémosla para crear variaciones del cargador jugando con diferentes animaciones. Por ejemplo, ¿qué tal un patrón repetitivo de cubos que se deslizan infinitamente de izquierda a derecha?

Este cargador define cuatro cubos en una sola fila. Eso significa que nuestro --n el valor es 4 y --m es igual a 1 . En otras palabras, ¡ya no los necesitamos!

En su lugar, podemos trabajar con el --size y --gap variables en un contenedor de cuadrícula:

.loader {
  --size: 70px;
  --gap: 15px;  

  width: calc(3 * (1.353 * var(--size) + var(--gap)));
  display: grid;
  aspect-ratio: 3;
}

Este es nuestro contenedor. Tenemos cuatro cubos, pero solo queremos mostrar tres en el contenedor a la vez para que siempre tengamos uno deslizándose hacia adentro mientras el otro se desliza hacia afuera. Es por eso que estamos factorizando el ancho por 3 y tener la relación de aspecto establecida en 3 también.

Asegurémonos de que nuestro patrón de cubo esté configurado para el ancho de cuatro cubos. Vamos a hacer esto en el contenedor ::before pseudo-elemento:

.loader::before { 
  content: "";
  width: calc(4 * 100% / 3);
  /*
     Code to create four cubes
  */
}

Ahora que tenemos cuatro cubos en un contenedor de tres cubos, podemos justificar el patrón del cubo hasta el final del contenedor de cuadrícula para desbordarlo, mostrando los últimos tres cubos:

.loader {
  /* same as before */
  justify-content: end;
}

Esto es lo que tenemos hasta ahora, con un contorno rojo para mostrar los límites del contenedor de cuadrícula:

Ahora todo lo que tenemos que hacer es mover el pseudo-elemento a la derecha agregando nuestra animación:

@keyframes load {
  to { transform: translate(calc(100% / 4)); }
}

¿Conseguiste el truco de la animación? Terminemos esto ocultando el patrón de cubo desbordado y agregando un toque de enmascaramiento para crear ese efecto de desvanecimiento que es el principio y el final:

.loader {
  --size: 70px;
  --gap: 15px;  
  
  width: calc(3*(1.353*var(--s) + var(--g)));
  display: grid;
  justify-items: end;
  aspect-ratio: 3;
  overflow: hidden;
  mask: linear-gradient(90deg, #0000, #000 30px calc(100% - 30px), #0000);
}

Podemos hacer esto mucho más flexible introduciendo una variable, --n, para establecer cuántos cubos se muestran en el contenedor a la vez. Y dado que el número total de cubos en el patrón debe ser uno más que --npodemos expresar que como calc(var(--n) + 1).

Aquí está la cosa completa:

Bien, otro cargador 3D que es similar pero tiene los cubos cambiando de color en sucesión en lugar de deslizarse:

Vamos a basarnos en un fondo animado con background-blend-mode Para este:

.loader {
  /* ... */
  background:
    linear-gradient(#ff1818 0 0) 0% / calc(100% / 3) 100% no-repeat,
    /* ... */;
  background-blend-mode: multiply;
  /* ... */
  animation: load steps(3) 1.5s infinite;
}
@keyframes load {
  to { background-position: 150%; }
}

Eliminé el código superfluo utilizado para crear el mismo diseño que el último ejemplo, pero con tres cubos en lugar de cuatro. Lo que estoy agregando aquí es un degradado definido con un color específico que se mezcla con el degradado cónico, tal como lo hicimos anteriormente para el cargador 3D de la barra de progreso.

A partir de ahí, está animando el degradado de fondo. background-position como una animación de tres pasos para que los cubos parpadeen de uno en uno.

Si no está familiarizado con los valores que estoy usando para background-position y la sintaxis de fondo, recomiendo encarecidamente uno de mis artículos anteriores y una de mis respuestas de Stack Overflow. Encontrarás una explicación muy detallada allí.

¿Podemos actualizar el número de cubos para que sea variable?

Sí, tengo una solución para eso, pero me gustaría que la probaras en lugar de incrustarla aquí. Tome lo que hemos aprendido del ejemplo anterior e intente hacer lo mismo con este, ¡luego comparta su trabajo en los comentarios!

¡Variaciones en abundancia!

Al igual que los otros tres artículos de esta serie, me gustaría dejarles algo de inspiración para seguir adelante y crear sus propios cargadores. Aquí hay una colección que incluye los cargadores 3D que creamos juntos, además de algunos otros para que dejes volar tu imaginación:

Eso es un envoltorio

Espero que hayan disfrutado de pasar tiempo haciendo cargadores de un solo elemento conmigo estas últimas semanas. Es una locura que comenzamos con un aparentemente simple spinner y luego agregamos gradualmente nuevas piezas para trabajar nosotros mismos hasta llegar a las técnicas 3D que aún usan un solo elemento en el marcado. Así es exactamente como se ve CSS cuando aprovechamos sus poderes: escalable, flexible y reutilizable.

¡Gracias de nuevo por leer esta pequeña serie! Terminaré recordándote que tengo una colección de más de 500 cargadores si estás buscando más ideas e inspiración.

Deja una respuesta