Grade CSS e formas personalizadas, parte 2 PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Grade CSS e formas personalizadas, parte 2

Tudo bem, então o última vez que fizemos o check-in, estávamos usando CSS Grid e combinando-os com CSS clip-path e mask técnicas para criar grades com formas extravagantes.

Aqui está apenas uma das grades fantásticas que fizemos juntos:

Preparados para o segundo turno? Ainda estamos trabalhando com CSS Grid, clip-path e mask, mas até o final deste artigo, teremos diferentes maneiras de organizar imagens na grade, incluindo alguns efeitos de foco rad que proporcionam uma experiência autêntica e interativa para visualizar imagens.

E adivinha? Estamos usando o mesma marcação que usamos da última vez. Aqui está isso novamente:

<div class="gallery">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <!-- as many times as we want -->
</div>

Assim como no artigo anterior, precisamos apenas de um container com imagens dentro. Nada mais!

Grade de imagens aninhadas

Da última vez, nossas grades eram, bem, grades de imagem típicas. Além das formas elegantes com as quais as mascaramos, elas eram grades simétricas bastante padrão na medida em que posicionamos as imagens dentro.

Vamos tentar aninhar uma imagem no centro da grade:

Começamos definindo uma grade 2✕2 para quatro imagens:

.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 complexo ainda. O próximo passo é cortar o canto de nossas imagens para criar o espaço para a imagem aninhada. Já tenho um artigo detalhado sobre como cortar cantos usando clip-path e mask. Você também pode usar o meu gerador online para obter o CSS para mascarar cantos.

O que precisamos aqui é cortar os cantos em um ângulo igual a 90deg. Podemos usar o mesmo técnica de gradiente cônico desse artigo para fazer isso:

.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; }

Poderíamos usar o clip-path para cortar cantos desse mesmo artigo, mas mascarar com gradientes é mais adequado aqui porque temos a mesma configuração para todas as imagens — tudo o que precisamos é de uma rotação (definida com a variável --_a) obtém o efeito, então estamos mascarando de dentro em vez das bordas externas.

Grade CSS e formas personalizadas, parte 2

Agora podemos colocar a imagem aninhada dentro do espaço mascarado. Primeiro, vamos ter certeza de que temos um quinto elemento de imagem no HTML:

<div class="gallery">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
  <img src="..." alt="...">
</div>

Vamos confiar no bom e velho posicionamento absoluto para colocá-lo lá:

.gallery > img:nth-child(5) {
  position: absolute;
  inset: calc(50% - .5*var(--s));
  clip-path: inset(calc(var(--g) / 4));
}

A inset A propriedade nos permite colocar a imagem no centro usando uma única declaração. Sabemos o tamanho da imagem (definida com a variável --s), e sabemos que o tamanho do contêiner é igual a 100%. Fazemos algumas contas, e a distância de cada aresta deve ser igual a (100% - var(--s))/2.

Diagrama das larguras necessárias para completar o projeto.
Grade CSS e formas personalizadas, parte 2

Você pode estar se perguntando por que estamos usando clip-path em tudo aqui. Estamos usando com a imagem aninhada para ter uma lacuna consistente. Se tivéssemos de removê-lo, você notaria que não temos a mesma lacuna entre todas as imagens. Dessa forma, estamos cortando um pouco da quinta imagem para obter o espaçamento adequado ao redor dela.

O código completo novamente:

.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));
}

Agora, muitos de vocês também podem estar se perguntando: por que todas as coisas complexas quando podemos colocar a última imagem no topo e adicionar uma borda a ela? Isso esconderia as imagens abaixo da imagem aninhada sem máscara, certo?

Isso é verdade, e obteremos o seguinte:

Não masknão clip-path. Sim, o código é fácil de entender, mas há uma pequena desvantagem: a cor da borda precisa ser a mesma do plano de fundo principal para tornar a ilusão perfeita. Esta pequena desvantagem é suficiente para eu tornar o código mais complexo em troca de uma transparência real independente do fundo. Não estou dizendo que uma abordagem de fronteira é ruim ou errada. Eu o recomendaria na maioria dos casos em que o plano de fundo é conhecido. Mas estamos aqui para explorar coisas novas e, mais importante, construir componentes que não dependam de seu ambiente.

Vamos tentar outra forma desta vez:

Desta vez, transformamos a imagem aninhada em um círculo em vez de um quadrado. Essa é uma tarefa fácil com border-radius Mas precisamos usar um recorte circular para as outras imagens. Desta vez, porém, contaremos com um radial-gradient() em vez de um conic-gradient() para obter aquele visual arredondado.

.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 as imagens usam a mesma configuração do exemplo anterior, mas atualizamos o ponto central a cada vez.

Diagrama mostrando os valores centrais para cada quadrante da grade.
Grade CSS e formas personalizadas, parte 2

A figura acima ilustra o ponto central de cada círculo. Ainda assim, no código real, você notará que também estou contabilizando a lacuna para garantir que todos os pontos estejam na mesma posição (no centro da grade) para obter um círculo contínuo se os combinarmos.

Agora que temos nosso layout vamos falar sobre o efeito hover. Caso você não tenha notado, um efeito de foco legal aumenta o tamanho da imagem aninhada e ajusta todo o resto de acordo. Aumentar o tamanho é uma tarefa relativamente fácil, mas atualizar o gradiente é mais complicado, pois, por padrão, os gradientes não podem ser animados. Para superar isso, usarei um font-size hack para poder animar o gradiente radial.

Se você verificar o código do gradiente, verá que estou adicionando 1em:

mask: 
    radial-gradient(farthest-side at var(--_a),
      #0000 calc(50% + var(--g)/2 + 1em), #000 calc(51% + var(--g)/2 + 1em));

Sabe-se que em unidades são relativas ao elemento pai font-size, alterando assim o font-size da .gallery também alterará o cálculo em value — este é o truque que estamos usando. Estamos animando o font-size de um valor de 0 a um determinado valor e, como resultado, o gradiente é animado, tornando a parte recortada maior, seguindo o tamanho da imagem aninhada que está ficando maior.

Aqui está o código que destaca as partes envolvidas no efeito hover:

.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);
}

A font-size O truque é útil se quisermos animar gradientes ou outras propriedades que não podem ser animadas. Propriedades personalizadas definidas com @property podem resolver esse problema, mas suporte para isso ainda está faltando no momento da escrita.

Eu descobri o font-size truque de @SelenIT2 ao tentar resolver um desafio no Twitter.

Outra forma? Vamos lá!

Desta vez, recortamos a imagem aninhada na forma de um losango. Vou deixar você dissecar o código como um exercício para descobrir como chegamos aqui. Você notará que a estrutura é a mesma de nossos exemplos. As únicas diferenças são como estamos usando o gradiente para criar a forma. Mergulhe e aprenda!

Grade de imagem circular

Podemos combinar o que aprendemos aqui e em artigos anteriores para criar uma grade de imagens ainda mais empolgante. Desta vez, vamos fazer com que todas as imagens em nossa grade sejam circulares e, ao passar o mouse, expanda uma imagem para revelar a coisa toda enquanto cobre o resto das fotos.

A estrutura HTML e CSS da grade não é novidade, então vamos pular essa parte e focar na forma circular e no efeito de foco que queremos.

Vamos usar clip-path ea sua circle() função para - você adivinhou! — corte um círculo das imagens.

Mostrando os dois estados de uma imagem, o estado natural à esquerda e o estado suspenso à direita, incluindo os valores do caminho de recorte para criá-los.
Grade CSS e formas personalizadas, parte 2

Essa figura ilustra a clip-path usado para a primeira imagem. O lado esquerdo mostra o estado inicial da imagem, enquanto o direito mostra o estado em foco. Você pode usar esta ferramenta online para jogar e visualizar o clip-path valores.

Para as outras imagens, podemos atualizar o centro do círculo (70% 70%) para obter o seguinte 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%));
}

Observe como estamos definindo o clip-path valores como um fallback dentro var(). Dessa forma, podemos atualizar mais facilmente o valor ao passar o mouse, definindo o valor do --_c variável. Ao usar circle(), a posição padrão do ponto central é 50% 50%, então podemos omitir isso para um código mais conciso. É por isso que você vê que estamos apenas configurando 50% em vez de 50% at 50% 50%.

Em seguida, aumentamos o tamanho da nossa imagem ao passar o mouse para o tamanho geral da grade para que possamos cobrir as outras imagens. Asseguramos também a z-index tem um valor mais alto na imagem pairada, então é o primeiro em nosso contexto de empilhamento.

.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;
}

O que está acontecendo com o place-self propriedade? Por que precisamos dela e por que cada imagem tem um valor específico?

Você se lembra do problema que tivemos no artigo anterior quando criando a grade de peças do quebra-cabeça? Aumentamos o tamanho das imagens para criar um estouro, mas o estouro de algumas imagens estava incorreto. Nós os corrigimos usando o place-self propriedade.

Mesma questão aqui. Estamos aumentando o tamanho das imagens para que cada uma transborde suas células da grade. Mas se não fizermos nada, todos eles transbordarão nos lados direito e inferior da grade. O que precisamos é:

  1. a primeira imagem a ultrapassar a borda inferior direita (o comportamento padrão),
  2. a segunda imagem para transbordar a borda inferior esquerda,
  3. a terceira imagem para ultrapassar a borda superior direita e
  4. a quarta imagem para transbordar a borda superior esquerda.

Para conseguir isso, precisamos colocar cada imagem corretamente usando o place-self propriedade.

Diagrama mostrando os valores da propriedade place-self para cada quadrante da grade.
Grade CSS e formas personalizadas, parte 2

Caso você não esteja familiarizado com place-self, é a abreviação de justify-self e align-self para colocar o elemento na horizontal e na vertical. Quando leva um valor, ambos os alinhamentos usam esse mesmo valor.

Expansão de painéis de imagem

Em um artigo anterior, criei um efeito de zoom legal que se aplica a uma grade de imagens onde podemos controlar tudo: número de linhas, número de colunas, tamanhos, fator de escala, etc.

Um caso particular foram os painéis expansíveis clássicos, onde temos apenas uma linha e um contêiner de largura total.

Vamos pegar este exemplo e combiná-lo com formas!

Antes de continuarmos, eu recomendo a leitura do meu outro artigo para entender como funcionam os truques que vamos abordar. Verifique isso, e continuaremos aqui focando na criação das formas do painel.

Primeiro, vamos começar simplificando o código e removendo algumas variáveis

Precisamos apenas de uma linha e o número de colunas deve ser ajustado com base no número de imagens. Isso significa que não precisamos mais de variáveis ​​para o número de linhas (--n) e colunas (--m ), mas precisamos usar grid-auto-flow: column, permitindo que a grade gere colunas automaticamente à medida que adicionamos novas imagens. Consideraremos uma altura fixa para nosso contêiner; por padrão, será de largura total.

Vamos recortar as imagens em uma forma inclinada:

Um tiro na cabeça de um lobo vermelho calmo olhando para baixo com vértices sobrepostos mostrando os pontos de propriedade do caminho de recorte.
clip-path: polygon(S 0%, 100% 0%, (100% - S) 100%, 0% 100%);

Mais uma vez, cada imagem está contida em sua célula de grade, então há mais espaço entre as imagens do que gostaríamos:

Uma grade de seis painéis de imagens inclinadas de vários animais selvagens mostrando as linhas e lacunas da grade.
Grade CSS e formas personalizadas, parte 2

Precisamos aumentar a largura das imagens para criar uma sobreposição. Nós substituímos min-width: 100% de min-width: calc(100% + var(--s)), Onde --s é uma nova variável que controla a forma.

Agora precisamos corrigir a primeira e a última imagens, para que elas saiam da página sem lacunas. Em outras palavras, podemos remover a inclinação do lado esquerdo da primeira imagem e a inclinação do lado direito da última imagem. Precisamos de um novo clip-path especificamente para essas duas imagens.

Também precisamos corrigir o overflow. Por padrão, todas as imagens transbordarão em ambos os lados, mas para a primeira, precisamos de um estouro no lado direito enquanto precisamos de um estouro esquerdo para a última imagem.

.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%);
}

O resultado final é um belo painel em expansão de imagens inclinadas!

Podemos adicionar quantas imagens você quiser e a grade se ajustará automaticamente. Além disso, só precisamos controlar um valor para controlar a forma!

Poderíamos ter feito esse mesmo layout com o flexbox já que estamos lidando com uma única linha de elementos. Aqui está minha implementação.

Claro, imagens inclinadas são legais, mas e um padrão em ziguezague? Eu já brinquei com isso em o final do último artigo.

Tudo o que estou fazendo aqui é substituir clip-path de mask… e adivinha? Já tenho um artigo detalhado sobre criando essa forma de zig-zag — para não falar de um online gerador para obter o código. Viu como tudo se encaixa?

A parte mais complicada aqui é garantir que os zig-zags estejam perfeitamente alinhados e, para isso, precisamos adicionar um deslocamento para cada :nth-child(odd) elemento de imagem.

.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));
}

Observe o uso do --_p variável, que retornará para 0% mas será igual a --_s para as imagens estranhas.

Aqui está uma demonstração que ilustra o problema. Passe o mouse para ver como o deslocamento — definido por --_p — está corrigindo o alinhamento.

Além disso, observe como usamos uma máscara diferente para a primeira e a última imagem, como fizemos no exemplo anterior. Precisamos apenas de um zig-zag no lado direito da primeira imagem e no lado esquerdo da última imagem.

E por que não lados arredondados? Vamos fazer isso!

Eu sei que o código pode parecer assustador e difícil de entender, mas tudo o que está acontecendo é uma combinação de truques diferentes que abordamos neste e em outros artigos que já compartilhei. Neste caso, utilizo a mesma estrutura de código do zig-zag e das formas inclinadas. Compare-o com esses exemplos e você não encontrará nenhuma diferença! Esses são os mesmos truques em meu artigo anterior sobre o efeito de zoom. Então, estou usando meu outra escrita e meu gerador online para obter o código da máscara que cria essas formas arredondadas.

Se você se lembra do que fizemos para o zig-zag, usamos a mesma máscara para todas as imagens, mas tivemos que adicionar um deslocamento às imagens ímpares para criar uma sobreposição perfeita. Nesse caso, precisamos de uma máscara diferente para as imagens ímpares.

A primeira 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;
Grade CSS e formas personalizadas, parte 2 PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

O 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))
Grade CSS e formas personalizadas, parte 2 PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

O único esforço que fiz aqui foi atualizar a segunda máscara para incluir a variável gap (--g) para criar esse espaço entre as imagens.

O toque final é fixar a primeira e a última imagem. Como todos os exemplos anteriores, a primeira imagem precisa de uma borda direita reta enquanto a última precisa de uma borda direita reta.

Para a primeira imagem, sempre sabemos a máscara que ela precisa ter, que é a seguinte:

.gallery > img:first-child {
  mask: 
    radial-gradient(calc(var(--s) + var(--g)) at right, #0000 98%, #000) 50% / 100% calc(1.8 * var(--s));
}
Um headshot de urso pardo com um padrão ondulado para a borda direita.
Grade CSS e formas personalizadas, parte 2

Para a última imagem, depende do número de elementos, por isso importa se esse elemento é :nth-child(odd) or :nth-child(even).

A grade completa de fotos de animais selvagens com todas as bordas e lacunas corretas entre as imagens.
Grade CSS e formas personalizadas, parte 2
.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
}
Uma grade de linha única de três fotos de animais selvagens com bordas onduladas onde a última imagem é um elemento ímpar.
Grade CSS e formas personalizadas, parte 2
.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))
}

Isso é tudo! Três layouts diferentes, mas sempre os mesmos truques de CSS:

  • a estrutura do código para criar o efeito de zoom
  • uma máscara ou caminho de clipe para criar as formas
  • uma configuração separada para os elementos ímpares em alguns casos para garantir uma sobreposição perfeita
  • uma configuração específica para a primeira e última imagem para manter a forma em apenas um lado.

E aqui está uma grande demonstração com todos eles juntos. Tudo o que você precisa é adicionar uma classe para ativar o layout que deseja ver.

E aqui está aquele com a implementação do Flexbox

Resumindo

Ufa, terminamos! Eu sei que existem muitos truques e exemplos de CSS entre este artigo e o último, sem mencionar todos os outros truques que mencionei aqui em outros artigos que escrevi. Levei tempo para juntar tudo, e você não precisa entender tudo de uma vez. Uma leitura lhe dará uma boa visão geral de todos os layouts, mas você pode precisar ler o artigo mais de uma vez e se concentrar em cada exemplo para entender todos os truques.

Você notou que não tocamos no HTML, exceto talvez no número de imagens na marcação? Todos os layouts que fizemos compartilham o mesmo código HTML, que nada mais é do que uma lista de imagens.

Antes de terminar, vou deixar um último exemplo. É um “versus” entre dois personagens de anime com um efeito de foco legal.

E você? Você pode criar algo baseado no que aprendeu? Não precisa ser complexo – imagine algo legal ou engraçado como eu fiz com aquele confronto de anime. Pode ser um bom exercício para você, e podemos terminar com uma excelente coleção na seção de comentários.

Carimbo de hora:

Mais de Truques CSS