Cuadrícula CSS y formas personalizadas Parte 2 | trucos CSS
Ok, la última vez que nos registramos usamos CSS Grid y los combinamos con CSS clip-path
y mask
técnicas para crear mallas con formas fantásticas.
Esta es solo una de las fantásticas cuadrículas que hicimos juntos:
¿Estás listo para la segunda ronda? Seguimos trabajando con CSS Grid, clip-path
y mask
pero al final de este artículo, veremos diferentes formas de organizar las imágenes en la cuadrícula, incluidos algunos efectos de pasar el mouse que crean una experiencia de visualización de imágenes auténtica e interactiva.
¿Y adivina qué? Usamos el mismo marcado que usamos la última vezAquí está otra vez:
<div class="gallery">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
<!-- as many times as we want -->
</div>
Como en el artículo anterior, solo necesitamos un contenedor con imágenes dentro. ¡Nada mas!
Cuadrícula con imágenes anidadas
La última vez, nuestras cuadrículas eran las típicas cuadrículas de imágenes. Aparte de las formas ordenadas con las que las disfrazamos, eran cuadrículas simétricas bastante estándar en términos de cómo colocamos las imágenes en el interior.
Intentemos incrustar una imagen en el centro de la cuadrícula:
Comenzamos configurando una cuadrícula de 2✕2 para cuatro imágenes:
.gallery {
--s: 200px; /* controls the image size */
--g: 10px; /* controls the gap between images */
display: grid;
gap: var(--g);
grid-template-columns: repeat(2, auto);
}
.gallery > img {
width: var(--s);
aspect-ratio: 1;
object-fit: cover;
}
Nada complicado todavía. El siguiente paso es recortar la esquina de nuestras imágenes para crear espacio para la imagen insertada. Ya tengo un artículo detallado sobre cómo cortar esquinas con clip-path
y mask
También puedes usar el mío. generador en línea para obtener CSS para enmascarar las esquinas.
Lo que necesitamos aquí es cortar las esquinas en un ángulo igual a 90deg
Podemos usar la misma técnica de gradiente cónico de este artículo para hacer esto:
.gallery > img {
mask: conic-gradient(from var(--_a), #0000 90deg, #000 0);
}
.gallery > img:nth-child(1) { --_a: 90deg; }
.gallery > img:nth-child(2) { --_a: 180deg; }
.gallery > img:nth-child(3) { --_a: 0deg; }
.gallery > img:nth-child(4) { --_a:-90deg; }
Nosotros podemos usar clip-path
método de corte de esquina del mismo artículo, pero el enmascaramiento de degradado es más apropiado aquí porque tenemos la misma configuración para todas las imágenes; todo lo que necesitamos es una rotación (definida con la variable --_a
) obtenemos el efecto, por lo que enmascaramos desde el interior en lugar de los bordes exteriores.
Ahora podemos colocar la imagen anidada dentro del espacio enmascarado. Primero, asegurémonos de tener un quinto elemento de imagen en el HTML:
<div class="gallery">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
<img src="https://css-tricks.com/css-grid-and-custom-shapes-part-2/..." alt="https://css-tricks.com/css-grid-and-custom-shapes-part-2/...">
</div>
Confiaremos en el buen posicionamiento absoluto antiguo para ponerlo allí:
.gallery > img:nth-child(5) {
position: absolute;
inset: calc(50% - .5*var(--s));
clip-path: inset(calc(var(--g) / 4));
}
Los inset
nos permite centrar la imagen mediante una única declaración.Conocemos el tamaño de la imagen (definido con la variable --s
), y sabemos que el tamaño del contenedor es igual al 100%. Hacemos algunos cálculos y la distancia desde cada borde debe ser igual a (100% - var(--s))/2
.
Tal vez se pregunte por qué usamos clip-path
aquí en absoluto. Lo usamos con la imagen insertada para tener un espacio constante. Si tuviéramos que eliminarlo, notará que no tenemos el mismo espacio entre todas las imágenes. De esta manera, recortamos un poco de la quinta imagen para obtener la distancia correcta a su alrededor.
El código completo de nuevo:
.gallery {
--s: 200px; /* controls the image size */
--g: 10px; /* controls the gap between images */
display: grid;
gap: var(--g);
grid-template-columns: repeat(2, auto);
position: relative;
}
.gallery > img {
width: var(--s);
aspect-ratio: 1;
object-fit: cover;
mask: conic-gradient(from var(--_a), #0000 90deg, #000 0);
}
.gallery > img:nth-child(1) {--_a: 90deg}
.gallery > img:nth-child(2) {--_a:180deg}
.gallery > img:nth-child(3) {--_a: 0deg}
.gallery > img:nth-child(4) {--_a:-90deg}
.gallery > img:nth-child(5) {
position: absolute;
inset: calc(50% - .5*var(--s));
clip-path: inset(calc(var(--g) / 4));
}
Ahora, muchos de ustedes también se estarán preguntando: ¿por qué todas estas cosas complicadas cuando podemos colocar la última imagen en la parte superior y agregarle un marco? Esto ocultará las imágenes debajo de la imagen anidada sin máscara, ¿verdad?
Esto es cierto y obtenemos lo siguiente:
No mask
no clip-path
Sí, el código es fácil de entender, pero hay un pequeño inconveniente: el color del marco debe ser el mismo que el del fondo principal para que la ilusión sea perfecta. Este pequeño defecto me basta para hacer el código más complejo a cambio de una verdadera transparencia independientemente del fondo. No estoy diciendo que el enfoque de los límites sea malo o incorrecto. Lo recomendaría en la mayoría de los casos en los que se conocen los antecedentes. Pero estamos aquí para explorar cosas nuevas y, lo que es más importante, construir componentes que no dependan de su entorno.
Probemos una forma diferente esta vez:
Esta vez hicimos el recuadro de un círculo en lugar de un cuadrado. Esta es una tarea fácil. border-radius
Pero necesitamos usar recorte circular para las otras imágenes. Esta vez, sin embargo, nos basaremos en radial-gradient()
en lugar de un conic-gradient()
para conseguir ese bonito aspecto redondeado.
.gallery > img {
mask:
radial-gradient(farthest-side at var(--_a),
#0000 calc(50% + var(--g)/2), #000 calc(51% + var(--g)/2));
}
.gallery > img:nth-child(1) { --_a: calc(100% + var(--g)/2) calc(100% + var(--g)/2); }
.gallery > img:nth-child(2) { --_a: calc(0% - var(--g)/2) calc(100% + var(--g)/2); }
.gallery > img:nth-child(3) { --_a: calc(100% + var(--g)/2) calc(0% - var(--g)/2); }
.gallery > img:nth-child(4) { --_a: calc(0% - var(--g)/2) calc(0% - var(--g)/2); }
Todas las imágenes usan la misma configuración que el ejemplo anterior, pero actualizamos el punto central cada vez.
La figura de arriba ilustra el punto central de cada círculo. Sin embargo, en el código real notará que también tomo en cuenta la brecha para asegurar que todos los puntos estén en la misma posición (el centro de la cuadrícula) para obtener un círculo continuo si los combinamos.
Ahora que tenemos nuestro diseño, hablemos sobre el efecto de retención. En caso de que no lo hayas notado, el efecto de retención en frío aumenta el tamaño de la imagen incrustada y ajusta todo lo demás en consecuencia. El aumento de El tamaño es una tarea relativamente fácil, pero actualizar el degradado es más complicado porque los degradados no se pueden animar de forma predeterminada. Para superar esto usaré font-size
hackear para poder animar el degradado radial.
Si revisa el código de gradiente, puede ver que estoy agregando 1em
:
mask:
radial-gradient(farthest-side at var(--_a),
#0000 calc(50% + var(--g)/2 + 1em), #000 calc(51% + var(--g)/2 + 1em));
Esto es conocido em
las unidades son relativas al elemento padre font-size
por lo que el cambio de font-size
de .gallery
también cambiará el calculado em
valor: este es el truco que usamos font-size
de valor de 0
a un valor dado y, como resultado, el degradado se anima, haciendo que la parte recortada sea más grande, siguiendo el tamaño de la imagen anidada que se amplía.
Aquí está el código que resalta las partes involucradas en el efecto de retención:
.gallery {
--s: 200px; /* controls the image size */
--g: 10px; /* controls the gaps between images */
font-size: 0; /* initially we have 1em = 0 */
transition: .5s;
}
/* we increase the cut-out by 1em */
.gallery > img {
mask:
radial-gradient(farthest-side at var(--_a),
#0000 calc(50% + var(--g)/2 + 1em), #000 calc(51% + var(--g)/2 + 1em));
}
/* we increase the size by 2em */
.gallery > img:nth-child(5) {
width: calc(var(--s) + 2em);
}
/* on hover 1em = S/5 */
.gallery:hover {
font-size: calc(var(--s) / 5);
}
Los font-size
truco es útil si queremos animar degradados u otras propiedades que no se pueden animar. Las propiedades personalizadas definidas con @property pueden resolver este problema, pero apoyo para el todavía falta en el momento de la escritura.
Yo descubrí font-size
truco de @SelenIT2 mientras intentaba decidir reto de twitter.
¿Otra forma? ¡Vamos!
Esta vez cortamos la imagen insertada en forma de diamante. Te dejaré analizar el código como ejercicio para entender cómo llegamos aquí. Notarás que la estructura es la misma que en nuestros ejemplos. Las únicas diferencias son las formas en que usamos el degradado para crear la forma. ¡Sumérgete y aprende!
Cuadrícula de imagen redonda
Podemos combinar lo que hemos aprendido aquí y en artículos anteriores para crear una cuadrícula de imágenes aún más interesante. Esta vez, hagamos que todas las imágenes en nuestra cuadrícula sean redondas y, al pasar el mouse, expandamos una imagen para revelar todo lo que cubre el resto de las fotos.
La estructura HTML y CSS de la cuadrícula no es nada nuevo, así que omitamos esa parte y, en su lugar, concentrémonos en la forma redonda y el efecto de retención que queremos.
Usaremos clip-path
y es circle()
función para - ¡lo has adivinado! — recortar un círculo de las imágenes.
Esta figura ilustra clip-path
utilizado para la primera imagen. El lado izquierdo muestra el estado inicial de la imagen, mientras que el lado derecho muestra el estado de espera. Puedes usar esta herramienta en línea para jugar y visualizar clip-path
valores.
Para las otras imágenes, podemos actualizar el centro del círculo (70% 70%
) para obtener el siguiente código:
.gallery > img:hover {
--_c: 50%; /* same as "50% at 50% 50%" */
}
.gallery > img:nth-child(1) {
clip-path: circle(var(--_c, 55% at 70% 70%));
}
.gallery > img:nth-child(2) {
clip-path: circle(var(--_c, 55% at 30% 70%));
}
.gallery > img:nth-child(3) {
clip-path: circle(var(--_c, 55% at 70% 30%));
}
.gallery > img:nth-child(4) {
clip-path: circle(var(--_c, 55% at 30% 30%));
}
Tenga en cuenta cómo definimos clip-path
valores como repuesto en el interior var()
Esto nos permite actualizar más fácilmente el valor en espera estableciendo el valor en --_c
variable Cuando se usa circle()
la posición predeterminada del punto central es 50% 50%
así que podemos omitir esto para obtener un código más conciso. Es por eso que ves que solo estamos sintonizando 50%
en cambio 50% at 50% 50%
.
Luego aumentamos el tamaño de nuestra imagen flotante al tamaño total de la cuadrícula para que podamos cubrir las otras imágenes. z-index
tiene un valor más alto en la imagen de retención, por lo que es superior en nuestro contexto de apilamiento.
.gallery {
--s: 200px; /* controls the image size */
--g: 8px; /* controls the gap between images */
display: grid;
grid: auto-flow var(--s) / repeat(2, var(--s));
gap: var(--g);
}
.gallery > img {
width: 100%;
aspect-ratio: 1;
cursor: pointer;
z-index: 0;
transition: .25s, z-index 0s .25s;
}
.gallery > img:hover {
--_c: 50%; /* change the center point on hover */
width: calc(200% + var(--g));
z-index: 1;
transition: .4s, z-index 0s;
}
.gallery > img:nth-child(1){
clip-path: circle(var(--_c, 55% at 70% 70%));
place-self: start;
}
.gallery > img:nth-child(2){
clip-path: circle(var(--_c, 55% at 30% 70%));
place-self: start end;
}
.gallery > img:nth-child(3){
clip-path: circle(var(--_c, 55% at 70% 30%));
place-self: end start;
}
.gallery > img:nth-child(4){
clip-path: circle(var(--_c, 55% at 30% 30%));
place-self: end;
}
que esta pasando con
place-self
¿Por qué lo necesitamos y por qué cada imagen tiene un valor determinado?
¿Recuerdas el problema que tuvimos en el artículo anterior cuando estábamos creando la cuadrícula de piezas del rompecabezas? Aumentamos el tamaño de las imágenes para crear un desbordamiento, pero el desbordamiento de algunas imágenes era incorrecto. Los arreglamos con la ayuda de place-self
Propiedad.
El mismo problema aqui. Aumentamos el tamaño de las imágenes para que cada una de ellas desborde sus celdas de cuadrícula. Pero si no hacemos nada, todos desbordarán los lados derecho e inferior de la cuadrícula. Lo que necesitamos es:
- la primera imagen que se desborda desde el borde inferior derecho (el comportamiento predeterminado),
- la segunda imagen se desborda desde el borde inferior izquierdo,
- la tercera imagen para exceder el borde superior derecho y
- la cuarta imagen se desborde desde el borde superior izquierdo.
Para conseguir esto necesitamos colocar cada imagen correctamente usando place-self
Propiedad.
En caso de que no esté familiarizado con place-self
es corto para justify-self
y align-self
para colocar el elemento horizontal y verticalmente. Cuando toma un solo valor, ambas alineaciones usan el mismo valor.
Paneles de imágenes expandibles
En un artículo anterior, creé un efecto de escala genial que se aplicó a una cuadrícula de imágenes donde podemos controlarlo todo: número de filas, número de columnas, dimensiones, factor de escala, etc.
Un ejemplo de ello fueron los clásicos paneles expansibles, donde solo tenemos una fila y un contenedor de ancho completo.
¡Vamos a tomar este ejemplo y combinarlo con formas!
Antes de continuar, recomiendo leer mi otro artículo para entender cómo funcionan los trucos que vamos a cubrir. Compruébelo y continuaremos aquí para centrarnos en la creación de las formas del panel.
Primero, comencemos simplificando el código y eliminando algunas variables.
Solo necesitamos una fila y la cantidad de columnas debe ajustarse según la cantidad de imágenes. Esto significa que ya no necesitamos variables de conteo de filas (--n
) y columnas (--m
), pero tenemos que usar grid-auto-flow: column
lo que permite que la cuadrícula genere columnas automáticamente a medida que agregamos nuevas imágenes. Consideraremos una altura fija para nuestro contenedor; por defecto será de ancho completo.
Cortemos las imágenes en forma inclinada:
clip-path: polygon(S 0%, 100% 0%, (100% - S) 100%, 0% 100%);
Una vez más, cada imagen está contenida dentro de su propia celda de cuadrícula, por lo que hay más espacio entre las imágenes de lo que nos gustaría:
Necesitamos aumentar el ancho de las imágenes para crear una superposición. min-width:
100%
con min-width: calc(100% + var(--s))
dónde --s
es una nueva variable que controla la forma.
Ahora debemos corregir la primera y la última imagen para que desaparezcan de la página sin espacios. En otras palabras, podemos eliminar el bisel del lado izquierdo de la primera imagen y el bisel del lado derecho de la última imagen. Necesitamos un nuevo clip-path
especialmente por estas dos fotos.
También tenemos que corregir el desbordamiento. De forma predeterminada, todas las imágenes se desbordarán en ambos lados, pero para la primera necesitamos un desbordamiento derecho, mientras que para la última imagen necesitamos un desbordamiento izquierdo.
.gallery > img:first-child {
min-width: calc(100% + var(--s)/2);
place-self: start;
clip-path: polygon(0 0,100% 0,calc(100% - var(--s)) 100%,0 100%);
}
.gallery > img:last-child {
min-width: calc(100% + var(--s)/2);
place-self: end;
clip-path: polygon(var(--s) 0,100% 0,100% 100%,0 100%);
}
¡El resultado final es un bonito panel en expansión de imágenes inclinadas!
Podemos añadir tantas imágenes como queramos y la cuadrícula se ajustará automáticamente. Además, ¡solo necesitamos controlar un valor para controlar la forma!
Podríamos haber hecho el mismo diseño con flexbox ya que estamos tratando con una sola fila de elementos. Aquí está mi desempeño.
Claro, las imágenes inclinadas son geniales, pero ¿qué pasa con un patrón en zigzag? Ya me burlé de este al final del último artículo.
Todo lo que estoy haciendo aquí es reemplazar clip-path
con mask
… ¿y adivina qué? Ya tengo un artículo detallado sobre cómo crear esta forma en zig-zag, sin mencionar en línea generador para obtener el código. ¿Ves cómo se une todo?
La parte más complicada aquí es asegurarse de que los zigzags estén perfectamente alineados y para eso necesitamos agregar un desplazamiento a cada uno. :nth-child(odd)
elemento de imagen
.gallery > img {
mask:
conic-gradient(from -135deg at right, #0000, #000 1deg 89deg, #0000 90deg)
100% calc(50% + var(--_p, 0%))/51% calc(2*var(--s)) repeat-y,
conic-gradient(from 45deg at left, #0000, #000 1deg 89deg, #0000 90deg)
0% calc(50% + var(--_p, 0%))/51% calc(2*var(--s)) repeat-y;
}
/* we add an offset to the odd elements */
.gallery > img:nth-child(odd) {
--_p: var(--s);
}
.gallery > img:first-child {
mask:
conic-gradient(from -135deg at right, #0000, #000 1deg 89deg, #0000 90deg)
0 calc(50% + var(--_p, 0%))/100% calc(2*var(--s));
}
.gallery > img:last-child {
mask:
conic-gradient(from 45deg at left, #0000, #000 1deg 89deg, #0000 90deg)
0 calc(50% + var(--_p, 0%)) /100% calc(2*var(--s));
}
Nótese el uso de --_p
variable que volverá a 0%
pero será igual a --_s
por las extrañas imágenes.
Aquí hay una demostración que ilustra el problema. Pasa el cursor por encima para ver cómo se compensa, definido por --_p
— corrige la alineación.
También observe cómo usamos una máscara diferente para la primera y la última imagen como lo hicimos en el ejemplo anterior. Solo necesitamos un zigzag en el lado derecho de la primera imagen y en el lado izquierdo de la última imagen.
¿Y por qué no los lados redondeados? ¡Vamos a hacerlo!
Sé que el código puede parecer intimidante y difícil de entender, pero todo lo que sucede es una combinación de diferentes trucos que hemos cubierto en este y otros artículos que ya he compartido. En este caso, estoy usando la misma estructura de código que las formas en zig-zag e inclinadas. ¡Compárelo con estos ejemplos y no encontrará la diferencia! Estos son los mismos trucos de mi artículo anterior sobre el efecto zoom. Luego uso mi otra escritura y mi generador en linea para obtener el código de la máscara que crea estas formas redondeadas.
Si recuerda lo que hicimos para el zigzag, usamos la misma máscara para todas las imágenes, pero luego tuvimos que agregar un desplazamiento a las imágenes impares para crear una superposición perfecta. En este caso, necesitamos una máscara diferente para las imágenes extrañas.
La primera máscara:
mask:
linear-gradient(-90deg,#0000 calc(2*var(--s)),#000 0) var(--s),
radial-gradient(var(--s),#000 98%,#0000) 50% / calc(2*var(--s)) calc(1.8*var(--s)) space repeat;
El segundo:
mask:
radial-gradient(calc(var(--s) + var(--g)) at calc(var(--s) + var(--g)) 50%,#0000 98% ,#000)
calc(50% - var(--s) - var(--g)) / 100% calc(1.8*var(--s))
El único esfuerzo que he hecho aquí es actualizar la segunda máscara para incluir la variable gap (--g
) para crear esto espacio entre imágenes.
El toque final es ajustar la primera y la última imagen.Como todos los ejemplos anteriores, la primera imagen necesita un borde izquierdo recto, mientras que la última necesita un borde derecho recto.
Para la primera imagen, siempre sabemos la máscara que debe tener, que es la siguiente:
.gallery > img:first-child {
mask:
radial-gradient(calc(var(--s) + var(--g)) at right, #0000 98%, #000) 50% / 100% calc(1.8 * var(--s));
}
Para la última imagen, depende de la cantidad de elementos, por lo que importa si este elemento es uno. :nth-child(odd)
o :nth-child(even)
.
.gallery > img:last-child:nth-child(even) {
mask:
linear-gradient(to right,#0000 var(--s),#000 0),
radial-gradient(var(--s),#000 98%,#0000) left / calc(2*var(--s)) calc(1.8*var(--s)) repeat-y
}
.gallery > img:last-child:nth-child(odd) {
mask:
radial-gradient(calc(var(--s) + var(--g)) at left,#0000 98%,#000) 50% / 100% calc(1.8*var(--s))
}
¡Eso es todo! Tres diseños diferentes, pero los mismos trucos de CSS cada vez:
- la estructura del código para crear el efecto de escala
- máscara o clip-path para crear las formas
- configuración separada para los elementos impares en algunos casos para asegurarnos de que tenemos una superposición perfecta
- configuración específica para la primera y la última imagen para mantener la forma en un solo lado.
Y aquí hay una gran demostración con todos ellos juntos. Todo lo que necesita hacer es agregar una clase para habilitar el diseño que desea ver.
Y aquí está el que tiene la implementación de Flexbox.
resumiendo
¡Uf, hemos terminado! Sé que hay muchos trucos y ejemplos de CSS entre este artículo y el último, sin mencionar todos los otros trucos que he mencionado aquí de otros artículos que he escrito. Me tomó un tiempo poner todo junto y no tienes que entender todo a la vez. Una lectura le dará una buena visión general de todos los diseños, pero es posible que deba leer el artículo más de una vez y concentrarse en cada ejemplo para comprender todos los trucos.
¿Notaste que no hemos tocado el HTML en absoluto, excepto tal vez la cantidad de imágenes en el marcado? Todos los diseños que hicimos comparten el mismo código HTML, que no es más que una lista de imágenes.
Antes de terminar, los dejo con un último ejemplo, este es un "vs" entre dos personajes de anime con un efecto de movimiento genial.
¿Y usted? ¿Puedes crear algo basado en lo que has aprendido? No tiene que ser complicado: imagina algo genial o divertido, como hice con ese partido de anime. Puede ser un buen ejercicio para ti y podemos terminar con una excelente colección en la sección de comentarios.
Deja una respuesta