Saltar enlaces

Cree una cuadrícula piramidal responsiva usando CSS moderno

existir Artículo anteriorconstruimos una cuadrícula hexagonal clásica. Esta es una implementación reactiva sin utilizar consultas de medios. El desafío es mejorar método hace cinco años Utilice CSS moderno.

Solo es compatible con Chrome, ya que esta tecnología utiliza funciones lanzadas recientemente, incluidas corner-shape, sibling-index()y División de unidades.

En este artículo, veremos otro tipo de cuadrícula: la cuadrícula piramidal. Seguimos usando la forma hexagonal, pero los elementos están organizados de manera diferente.

Una demostración que vale más que mil palabras:

Para una mejor visualización, abra vista de página completa Demostración para ver la estructura piramidal. Obtienes un comportamiento receptivo al cambiar el tamaño de la pantalla, donde la parte inferior comienza a comportarse como la cuadrícula que creamos en el artículo anterior.

Se muestra cómo un conjunto de formas hexagonales dispuestas en una cuadrícula piramidal deben responder a los cambios en el tamaño de la pantalla, resaltando el hexágono en el borde izquierdo y cómo debe ajustarse al nuevo diseño.

Genial, ¿verdad? Todo esto se hace sin consultas de medios, JavaScript ni mucho CSS pirateado. Puedes fragmentar tantos elementos como quieras y todo se ajustará perfectamente.

Antes de comenzar, hazte un favor y lee Artículo anterior Si aún no lo has hecho. Voy a omitir algunas de las cosas que ya expliqué allí, como cómo crear formas y algunas de las fórmulas que reutilizaré aquí. Al igual que en el artículo anterior, la implementación de Pyramid Grid es una mejora de Pyramid Grid. método hace cinco añosasí que si quieres comparar 2021 y 2026, consulta también el artículo anterior.

Configuración inicial

Esta vez, confiaremos en CSS Grid en lugar de Flexbox. Esta estructura facilita el control de la ubicación de elementos dentro de columnas y filas sin tener que ajustar los márgenes.

<div class="container">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <!-- etc. -->
</div>
.container {
  --s: 40px;  /* size  */
  --g: 5px;   /* gap */

  display: grid;
  grid-template-columns: repeat(auto-fit, var(--s) var(--s));
  justify-content: center;
  gap: var(--g);
}

.container > * {
  grid-column-end: span 2;
  aspect-ratio: cos(30deg);
  border-radius: 50% / 25%;
  corner-shape: bevel;
  margin-bottom: calc((2*var(--s) + var(--g))/(-4*cos(30deg)));
}

estoy usando repetición clásica auto-fit Cree tantas columnas como lo permita el espacio disponible. Para el proyecto, es el mismo código usado para crear la forma hexagonal en el artículo anterior.

tu escribiste var(--s) dos veces. ¿Es esto un error tipográfico?

¡Que no es! Quiero que mi cuadrícula siempre tenga un número par de columnas, donde cada elemento abarque dos columnas (así es como uso grid-column-end: span 2). Con esta configuración, puedo controlar fácilmente el movimiento entre diferentes filas.

Acérquese a los espacios entre los hexágonos, resaltados en rosa.

Arriba hay una captura de pantalla de DevTools que muestra la estructura de la cuadrícula. Por ejemplo, si el elemento 2 abarca las columnas 3 y 4, el elemento 4 debe abarcar las columnas 2 y 3, el elemento 5 debe abarcar las columnas 4 y 5, y así sucesivamente.

Misma lógica que la parte de respuesta. Cada primer elemento de cada dos filas se mueve una columna y comienza en la segunda columna.

Acérquese a los espacios entre los hexágonos, resaltados en rosa.

Con esta configuración, el tamaño del proyecto será igual a 2*var(--s) + var(--g). Por lo tanto, el margen inferior negativo es diferente del ejemplo anterior.

Entonces, en lugar de esto:

margin-bottom: calc(var(--s)/(-4*cos(30deg)));

… estoy usando:

margin-bottom: calc((2*var(--s) + var(--g))/(-4*cos(30deg)));

Nada especial hasta ahora, pero ya tenemos el 80% del código. Lo creas o no, estamos a sólo una propiedad de completar toda la red. Todo lo que tenemos que hacer es configurar grid-column-start La ubicación correcta de algunos elementos y, como habrás adivinado, aquí está la parte más complicada que involucra cálculos complejos.

rejilla piramidal

Suponga que el contenedor es lo suficientemente grande como para contener la pirámide y todos los elementos. En otras palabras, ignoremos la parte de la respuesta por ahora. Analicemos la estructura e intentemos identificar patrones:

28 hexágonos apilados en una cuadrícula piramidal. La primera fila de líneas diagonales a la derecha está resaltada, mostrando cómo se alinean las formas en los lados.

Independientemente del número de proyectos, la estructura es hasta cierto punto estática. El elemento de la izquierda (es decir, el primer elemento de cada fila) es siempre el mismo (1, 2, 4, 7, 11, etc.). Una solución sencilla es utilizar :nth-child() selector.

:nth-child(1) { grid-column-start: ?? }
:nth-child(2) { grid-column-start: ?? }
:nth-child(4) { grid-column-start: ?? }
:nth-child(7) { grid-column-start: ?? }
:nth-child(11) { grid-column-start: ?? }
/* etc. */

Todas sus ubicaciones están conectadas. Si el elemento 1 se coloca en la columna xentonces el elemento 2 debe colocarse en la columna x - 1el cuarto elemento de la columna x - 2,etc.

:nth-child(1) { grid-column-start: x - 0 } /* 0 is not need but useful to see the pattern*/
:nth-child(2) { grid-column-start: x - 1 }
:nth-child(4) { grid-column-start: x - 2 }
:nth-child(7) { grid-column-start: x - 3 }
:nth-child(11) { grid-column-start: x - 4 }
/* etc. */

El elemento 1 se coloca lógicamente en el medio, por lo que si nuestra cuadrícula contiene N columna, entonces x igual N/2:

:nth-child(1) { grid-column-start: N/2 - 0 }
:nth-child(2) { grid-column-start: N/2 - 1 }
:nth-child(4) { grid-column-start: N/2 - 2 }
:nth-child(7) { grid-column-start: N/2 - 3 }
:nth-child(11){ grid-column-start: N/2 - 4 }

Dado que cada elemento abarca dos columnas, N/2 También se puede considerar como la cantidad de elementos que se pueden contener en un contenedor. Entonces, actualicemos nuestra lógica y consideremos N Es la cantidad de elementos, no la cantidad de columnas.

:nth-child(1) { grid-column-start: N - 0 }
:nth-child(2) { grid-column-start: N - 1 }
:nth-child(4) { grid-column-start: N - 2 }
:nth-child(7) { grid-column-start: N - 3 }
:nth-child(11){ grid-column-start: N - 4 }
/* etc. */

Para calcular el número de artículos usaré la misma fórmula que en el post anterior:

N = round(down, (container_size + gap)/ (item_size + gap));

La única diferencia es que el tamaño del artículo ya no es var(--s)pero 2*var(--s) + var(--g)que nos da el siguiente CSS:

.container {
  --s: 40px;  /* size  */
  --g: 5px;   /* gap */

  container-type: inline-size; /* we make it a container to use 100cqw */
}

.container > * {
  --_n: round(down,(100cqw + var(--g))/(2*(var(--s) + var(--g))));
}

.container > *:nth-child(1) { grid-column-start: calc(var(--_n) - 0) }
.container > *:nth-child(2) { grid-column-start: calc(var(--_n) - 1) }
.container > *:nth-child(4) { grid-column-start: calc(var(--_n) - 2) }
.container > *:nth-child(7) { grid-column-start: calc(var(--_n) - 3) }
.container > *:nth-child(11){ grid-column-start: calc(var(--_n) - 4) }
/* etc. */

¡funciona! Tenemos una estructura piramidal. Aún no responde, pero llegaremos allí. Por cierto, si su objetivo es construir una estructura de este tipo con una cantidad fija de elementos y no se requiere ningún comportamiento reactivo, entonces lo anterior funciona perfectamente y ¡listo!

¿Por qué todos los elementos están colocados correctamente? ¡Solo definimos columnas para algunos elementos y no especificamos ninguna fila!

Este es el poder del algoritmo de ubicación automática de la cuadrícula CSS. Cuando defines una columna para un elemento, el siguiente elemento se coloca automáticamente después de él. No necesitamos especificar manualmente un montón de columnas y filas para todos los elementos.

Mejorar la implementación

No te gustan esos largos :nth-child() selectora, ¿verdad? Yo también, así que eliminémoslos y tengamos una mejor implementación. Este tipo de pirámides son bien conocidas en los círculos matemáticos, tenemos algo llamado pirámide. Números trigonométricos Que voy a usar. No te preocupes, no voy a empezar una lección de matemáticas, así que aquí tienes la fórmula que usaré:

j*(j + 1)/2 + 1 = index

…Dónde j es un número entero positivo (incluido cero).

Teóricamente, todos :nth-child Esto se puede producir utilizando el siguiente pseudocódigo:

for(j = 0; j< ?? ;j++) {
  :nth-child(j*(j + 1)/2 + 1) { grid-column-start: N - j }
}

No tenemos bucles en CSS, así que seguiré la misma lógica que seguí en mi último artículo (que espero que leas, de lo contrario estarás un poco perdido). yo expreso j Utilice índices. Resolví la fórmula anterior, que es una ecuación cuadrática, pero estoy seguro de que no quieres saber todas las matemáticas.

j = sqrt(2*index - 1.75) - .5

Podemos obtener el índice usando sibling-index() Función. La lógica es probar si cada elemento sqrt(2*index - 1.75) - .5 es un número entero positivo.

.container {
  --s: 40px; /* size  */
  --g: 5px; /* gap */

  container-type: inline-size; /* we make it a container to use 100cqw */
}
.container > * {
  --_n: round(down,(100cqw + var(--g))/(2*(var(--s) + var(--g))));
  --_j: calc(sqrt(2*sibling-index() - 1.75) - .5);
  --_d: mod(var(--_j),1);
  grid-column-start: if(style(--_d: 0): calc(var(--_n) - var(--_j)););
}

cuando --_d variable es igual 0lo que significa --_j es un número entero; en este caso, configuro la columna en N - j. No necesito probar si --_j Sé positivo porque siempre es positivo. El valor mínimo del índice es 1, por lo que el valor mínimo --_j0.

¡Entonces! reemplazamos todos :nth-child() Un selector con tres líneas de CSS que puede cubrir cualquier cantidad de elementos. ¡Ahora hagámoslo responsivo!

comportamiento de respuesta

Volver a mis artículos de 2021cambio entre la cuadrícula piramidal y la cuadrícula clásica según el tamaño de la pantalla. Esta vez haré algo diferente. Seguiré construyendo la pirámide hasta que ya no sea posible y, a partir de ahí, se convertirá en una cuadrícula clásica.

Muestra una pila de formas hexagonales dispuestas en dos formas: una cuadrícula piramidal en la parte superior y una cuadrícula rectangular debajo.

Los elementos del 1 al 28 forman la pirámide. Después de eso obtenemos la misma malla clásica que construimos en el artículo anterior. Necesitamos ubicar los primeros elementos de ciertas filas (29, 42, etc.) y moverlos. Esta vez no vamos a establecer márgenes a la izquierda, pero necesitamos establecerlos. grid-column-start valor 2.

Como de costumbre, determinamos la fórmula para el elemento, representado por el índice, y luego probamos si el resultado es un número entero positivo:

N*i + (N - 1)*(i - 1) + 1 + N*(N - 1)/2 = index

entonces:

i = (index - 2 + N*(3 - N)/2)/(2*N - 1)

cuando i es un entero positivo (excluyendo cero), configuramos el inicio de la columna en 2.

.container {
  --s: 40px; /* size  */
  --g: 5px; /* gap */

  container-type: inline-size; /* we make it a container to use 100cqw */
}
.container > * {
  --_n: round(down,(100cqw + var(--g))/(2*(var(--s) + var(--g))));

  /* code for the pyramidal grid */
  --_j: calc(sqrt(2*sibling-index() - 1.75) - .5);
  --_d: mod(var(--_j),1);
  grid-column-start: if(style(--_d: 0): calc(var(--_n) - var(--_j)););

  /* code for the responsive grid */
  --_i: calc((sibling-index() - 2 + (var(--_n)*(3 - var(--_n)))/2)/(2*var(--_n) - 1));
  --_c: mod(var(--_i),1);
  grid-column-start: if(style((--_i > 0) and (--_c: 0)): 2;);
}

y --_j variable, necesito probar si --_i es un valor positivo porque puede ser negativo para algunos valores de índice. Para hacer esto, agregué una condición más que la primera.

¡Pero espera! Eso no es nada bueno. estamos anunciando grid-column-start dos veces, por lo que sólo se utilizará uno de ellos. Deberíamos tener una sola declaración y para ello podemos combinar estas dos condiciones usando una sola declaración. if() declaración:

grid-column-start:
if(
  style((--_i > 0) and (--_c: 0)): 2; /* first condition */
  style(--_d: 0): calc(var(--_n) - var(--_j)); /* second condition */
);

Si la primera condición es verdadera (cuadrícula receptiva), establecemos el valor en 2; De lo contrario, si la segunda condición es verdadera (cuadrícula piramidal), establecemos el valor en calc(var(--_n) - var(--_j)); De lo contrario no hacemos nada.

¿Por qué este orden en particular?

Porque la cuadrícula receptiva debería tener mayor prioridad. Mira la imagen a continuación:

Se muestra cómo un conjunto de formas hexagonales dispuestas en una cuadrícula piramidal deben responder a los cambios en el tamaño de la pantalla, resaltando el hexágono en el borde izquierdo y cómo debe ajustarse al nuevo diseño.

El elemento 29 es parte de la cuadrícula piramidal porque es el primer elemento de la fila. Esto significa que la condición de pirámide para este proyecto siempre se cumple. Sin embargo, cuando la cuadrícula responde, el elemento pasa a formar parte de la cuadrícula y otras condiciones se mantienen. Cuando ambas condiciones son verdaderas, la condición de reacción debería ganar; por eso esta es la primera condición que probamos.

Echemos un vistazo a esto:

¡ups! La pirámide se ve bien, pero después de eso, las cosas se vuelven confusas.

Para entender lo que está pasando, veamos específicamente el punto 37. Si examinas la imagen de arriba, notarás que es parte de una estructura piramidal. Entonces, aunque la cuadrícula responde, su condición sigue siendo verdadera y obtiene los valores de las columnas de la fórmula. calc(var(--_n) - var(--_j)) Esto no es bueno porque queremos mantener los valores predeterminados colocados automáticamente. Este es el caso de muchos proyectos, por lo que debemos solucionarlo.

Para encontrar una solución, veamos cómo se comportan los valores de la pirámide. Todos siguen la fórmula. N - jDónde j es un número entero positivo. Por ejemplo, si N Igual a 10 obtenemos:

10, 9, 8, 7, ... ,0, -1 , -2

En algún momento, estos valores se volverán negativos y, dado que los valores negativos están vigentes, los elementos se colocarán aleatoriamente, alterando la cuadrícula. Debemos asegurarnos de ignorar los valores negativos y utilizar el valor predeterminado.

Mantenemos solo los valores positivos y convertimos todos los valores negativos a cero usando:

max(0, var(--_n) - var(--_j))

Nos fijamos 0 como el límite mínimo (Para más contenido relacionado por favor haga clic aquí) y el valor se convierte en:

10, 9, 8, 7, ... , 0, 0, 0, 0

O obtenemos el valor positivo de la columna o obtenemos 0.

Pero dijiste que el valor debería ser el valor predeterminado en lugar de 0.

si, pero 0 es un valor no válido grid-column-startasí que usa 0 ¡Lo que significa que el navegador lo ignorará y volverá al valor predeterminado!

Nuestro nuevo código es:

grid-column-start:
  if(
    style((--_i > 0) and (--_c: 0)): 2; /* first condition */
    style(--_d: 0): max(0,var(--_n) - var(--_j)); /* second condition */
  );

¡Funciona!

¡Puedes agregar tantos elementos como quieras, ajustar el tamaño de la pantalla y todo encajará perfectamente!

Más ejemplos

¡Basta de codificación y matemáticas! Disfrutemos de más variaciones usando diferentes formas. Te dejaré analizar el código como tarea.

rejilla de diamante

Notarás que los métodos para establecer espacios entre elementos son ligeramente diferentes en las siguientes tres demostraciones.

rejilla octogonal

rejilla circular

Otra cuadrícula hexagonal:

en conclusión

¿Recuerdas cuando te dije que estábamos a una propiedad de completar la cuadrícula? Ese atributo (grid-column-start) ¡Leamos literalmente el artículo completo para discutirlo! Esto muestra que CSS ha evolucionado y requiere nuevas formas de pensar sobre su uso. CSS ya no es un lenguaje en el que solo es necesario establecer valores estáticos, p. color: red, margin: 10px, display: flexETC.

Ahora podemos definir el comportamiento dinámico mediante cálculos complejos. Este es todo un proceso de pensar, encontrar fórmulas, definir variables, crear condiciones, etc. Esto no es nada nuevo ya que pude hacer lo mismo en 2021. Sin embargo, ahora tenemos características más poderosas que nos permiten tener un código menos hacky y una implementación más flexible.

Home
Account
Cart
Search
¡Hola! ¡Pregúntame lo que quieras!
Explore
Drag