Como criar formas e padrões ondulados em CSS PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Como criar formas e padrões ondulados em CSS

A onda é provavelmente uma das formas mais difíceis de fazer em CSS. Sempre tentamos aproximá-lo com propriedades como border-radius e muitos números mágicos até conseguirmos algo que pareça um pouco próximo. E isso antes mesmo de entrarmos em padrões ondulados, que são mais difíceis.

“SVG isso!” você pode dizer, e provavelmente está certo de que é o melhor caminho a seguir. Mas veremos que o CSS pode fazer boas ondas e o código para isso não precisa ser todo maluco. E adivinha? Eu tenho um gerador online para torná-lo ainda mais trivial!

Se você brincar com o gerador, poderá ver que o CSS que ele produz são apenas dois gradientes e uma propriedade de máscara CSS - apenas essas duas coisas e podemos fazer qualquer tipo de forma ou padrão de onda. Sem mencionar que podemos controlar facilmente o tamanho e a curvatura das ondas enquanto estamos nisso.

Alguns dos valores podem se parecer com “números mágicos” mas na verdade há lógica por trás deles e vamos dissecar o código e descobrir todos os segredos por trás da criação de ondas.

Este artigo é uma continuação um anterior onde eu construí todos os tipos de bordas em ziguezague diferentes, com escopo, recortadas e sim, bordas onduladas. Eu recomendo verificar esse artigo, pois ele usa a mesma técnica que abordaremos aqui, mas com mais detalhes.

A matemática por trás das ondas

A rigor, não existe uma fórmula mágica por trás das formas onduladas. Qualquer forma com curvas que sobem e descem pode ser chamada de onda, então não vamos nos restringir a matemática complexa. Em vez disso, reproduziremos uma onda usando os fundamentos da geometria.

Vamos começar com um exemplo simples usando duas formas de círculo:

Como criar formas e padrões ondulados em CSS

Temos dois círculos com o mesmo raio próximos um do outro. Você vê aquela linha vermelha? Ele cobre a metade superior do primeiro círculo e a metade inferior do segundo. Agora imagine que você pega essa linha e a repete.

Uma linha vermelha ondulada em forma de ondas.
Como criar formas e padrões ondulados em CSS

Já vemos a onda. Agora vamos preencher a parte de baixo (ou a de cima) para obter o seguinte:

Padrão de onda vermelha.
Como criar formas e padrões ondulados em CSS

Tada! Temos uma forma ondulada e que podemos controlar usando uma variável para os raios do círculo. Esta é uma das ondas mais fáceis que podemos fazer e foi a que mostrei this artigo anterior

Vamos adicionar um pouco de complexidade pegando a primeira ilustração e movendo um pouco os círculos:

Dois círculos cinzas com duas linhas tracejadas bissectando indicando o espaçamento.
Como criar formas e padrões ondulados em CSS

Ainda temos dois círculos com os mesmos raios, mas eles não estão mais alinhados horizontalmente. Nesse caso, a linha vermelha não cobre mais metade da área de cada círculo, mas uma área menor. Esta área é limitada pela linha vermelha tracejada. Essa linha cruza o ponto onde os dois círculos se encontram.

Agora pegue essa linha e repita-a e você obtém outra onda, uma mais suave.

Uma linha ondulada vermelha.
Como criar formas e padrões ondulados em CSS
Um padrão de onda vermelha.
Como criar formas e padrões ondulados em CSS

Acho que você entendeu a ideia. Ao controlar a posição e o tamanho dos círculos, podemos criar qualquer onda que desejarmos. Podemos até criar variáveis ​​para eles, que chamarei P e S, Respectivamente.

Como criar formas e padrões ondulados em CSS PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.
Como criar formas e padrões ondulados em CSS

Você já deve ter notado que, no gerador online, controlamos a onda usando duas entradas. Eles mapeiam para as variáveis ​​acima. S é o “Tamanho da onda” e P é a “curvatura da onda”.

Eu estou definindo P as P = m*S onde m é a variável que você ajusta ao atualizar a curvatura da onda. Isso nos permite ter sempre a mesma curvatura, mesmo se atualizarmos S.

m pode ser qualquer valor entre 0 e 2. 0 nos dará o primeiro caso particular onde ambos os círculos estão alinhados horizontalmente. 2 é uma espécie de valor máximo. Podemos ir maior, mas depois de alguns testes descobri que qualquer coisa acima 2 produz formas ruins e planas.

Não vamos esquecer o raio do nosso círculo! Isso também pode ser definido usando S e P como isso:

R = sqrt(P² + S²)/2

Quando P é igual a 0, nós teremos R = S/2.

Temos tudo para começar a converter tudo isso em gradientes em CSS!

Criando gradientes

Nossas ondas usam círculos e, quando falamos de círculos, falamos de gradientes radiais. E como dois círculos definem nossa onda, logicamente estaremos usando dois gradientes radiais.

Começaremos com o caso particular em que P é igual a 0. Aqui está a ilustração do primeiro gradiente:

Esse gradiente cria a primeira curvatura enquanto preenche toda a área inferior – a “água” da onda, por assim dizer.

Como criar formas e padrões ondulados em CSS PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.
Como criar formas e padrões ondulados em CSS
.wave {
  --size: 50px;

  mask: radial-gradient(var(--size) at 50% 0%, #0000 99%, red 101%) 
    50% var(--size)/calc(4 * var(--size)) 100% repeat-x;
}

A --size variável define o raio e o tamanho do gradiente radial. Se compararmos com o S variável, então é igual a S/2.

Agora vamos adicionar o segundo gradiente:

O segundo gradiente nada mais é que um círculo para completar nossa onda:

radial-gradient(var(--size) at 50% var(--size), blue 99%, #0000 101%) 
  calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%

Se você verificar o artigo anterior você verá que estou simplesmente repetindo o que já fiz lá.

Eu segui os dois artigos, mas as configurações de gradiente não são as mesmas.

Isso porque podemos chegar ao mesmo resultado usando diferentes configurações de gradiente. Você notará uma pequena diferença no alinhamento se comparar as duas configurações, mas o truque é o mesmo. Isso pode ser confuso se você não estiver familiarizado com gradientes, mas não se preocupe. Com alguma prática, você se acostuma com eles e descobrirá por si mesmo que sintaxes diferentes podem levar ao mesmo resultado.

Aqui está o código completo para nossa primeira onda:

.wave {
  --size: 50px;

  mask:
    radial-gradient(var(--size) at 50% var(--size),#000 99%, #0000 101%) 
      calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--size) at 50% 0px, #0000 99%, #000 101%) 
      50% var(--size)/calc(4 * var(--size)) 100% repeat-x;
}

Agora vamos pegar este código e ajustá-lo para onde introduzimos uma variável que o torna totalmente reutilizável para criar qualquer onda que quisermos. Como vimos na seção anterior, o principal truque é mover os círculos para que não fiquem mais alinhados, então vamos atualizar a posição de cada um. Vamos mover o primeiro para cima e o segundo para baixo.

Nosso código ficará assim:

.wave {
  --size: 50px;
  --p: 25px;

  mask:
    radial-gradient(var(--size) at 50% calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--size) at 50% calc(-1*var(--p)), #0000 99%, #000 101%) 
      50% var(--size) / calc(4 * var(--size)) 100% repeat-x;
}

Eu apresentei um novo --p variável que é usada para definir a posição central de cada círculo. O primeiro gradiente está usando 50% calc(-1*var(--p)), então seu centro se move para cima enquanto o segundo está usando calc(var(--size) + var(--p)) para movê-lo para baixo.

Uma demonstração vale mais que mil palavras:

Os círculos não estão alinhados nem se tocam. Nós os espaçamos sem alterar seus raios, então perdemos nossa onda. Mas podemos consertar as coisas usando a mesma matemática que usamos anteriormente para calcular o novo raio. Lembre-se disso R = sqrt(P² + S²)/2. No nosso caso, --size é igual a S/2; o mesmo para --p que também é igual a P/2 já que estamos movendo ambos os círculos. Então, a distância entre seus pontos centrais é o dobro do valor de --p por esta:

R = sqrt(var(--size) * var(--size) + var(--p) * var(--p))

Isso nos dá um resultado de 55.9px.

Nossa onda está de volta! Vamos conectar essa equação em nosso CSS:

.wave {
  --size: 50px;
  --p: 25px;
  --R: sqrt(var(--p) * var(--p) + var(--size)*var(--size));

  mask:
    radial-gradient(var(--R) at 50% calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      calc(50% - 2*var(--size)) 0 / calc(4 * var(--size)) 100%,
    radial-gradient(var(--R) at 50% calc(-1*var(--p)), #0000 99%, #000 101%) 
      50% var(--size)/calc(4 * var(--size)) 100% repeat-x;
}

Este é um código CSS válido. sqrt() faz parte da especificação, mas no momento em que estou escrevendo isso, não há suporte de navegador para isso. Isso significa que precisamos de uma pitada de JavaScript ou Sass para calcular esse valor até chegarmos mais amplo sqrt() .

Isso é muito legal: bastam dois gradientes para obter uma onda legal que você pode aplicar a qualquer elemento usando o mask propriedade. Não há mais tentativa e erro - tudo que você precisa é atualizar duas variáveis ​​e pronto!

Invertendo a onda

E se quisermos que as ondas vão na outra direção, onde estamos preenchendo o “céu” ao invés da “água”. Acredite ou não, tudo o que precisamos fazer é atualizar dois valores:

.wave {
  --size: 50px;
  --p: 25px;
  --R: sqrt(var(--p) * var(--p) + var(--size) * var(--size));

  mask:
    radial-gradient(var(--R) at 50% calc(100% - (var(--size) + var(--p))), #000 99%, #0000 101%)
      calc(50% - 2 * var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--R) at 50% calc(100% + var(--p)), #0000 99%, #000 101%) 
      50% calc(100% - var(--size)) / calc(4 * var(--size)) 100% repeat-x;
}

Tudo o que fiz foi adicionar um deslocamento igual a 100%, destacado acima. Aqui está o resultado:

Podemos considerar uma sintaxe mais amigável usando valores de palavras-chave para facilitar ainda mais:

.wave {
  --size: 50px;
  --p: 25px;
  --R: sqrt(var(--p)*var(--p) + var(--size) * var(--size));

  mask:
    radial-gradient(var(--R) at left 50% bottom calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      calc(50% - 2 * var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), #0000 99%, #000 101%) 
      left 50% bottom var(--size) / calc(4 * var(--size)) 100% repeat-x;
}

Estamos usando o left e bottom palavras-chave para especificar os lados e o deslocamento. Por padrão, o padrão do navegador é left e top - por isso usamos 100% para mover o elemento para baixo. Na realidade, estamos movendo-o do top by 100%, então é realmente o mesmo que dizer bottom. Muito mais fácil de ler do que matemática!

Com esta sintaxe atualizada, tudo o que temos a fazer é trocar bottom para top — ou vice-versa — para mudar a direção da onda.

E se você deseja obter ondas superiores e inferiores, combinamos todos os gradientes em uma única declaração:

.wave {
  --size: 50px;
  --p: 25px;
  --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size));

  mask:
    /* Gradient 1 */
    radial-gradient(var(--R) at left 50% bottom calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      left calc(50% - 2*var(--size)) bottom 0 / calc(4 * var(--size)) 51% repeat-x,
    /* Gradient 2 */
    radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), #0000 99%, #000 101%) 
      left 50% bottom var(--size) / calc(4 * var(--size)) calc(51% - var(--size)) repeat-x,
    /* Gradient 3 */
    radial-gradient(var(--R) at left 50% top calc(var(--size) + var(--p)), #000 99%, #0000 101%) 
      left calc(50% - 2 * var(--size)) top 0 / calc(4 * var(--size)) 51% repeat-x,
    /* Gradient 4 */
    radial-gradient(var(--R) at left 50% top calc(-1 * var(--p)), #0000 99%, #000 101%) 
      left 50% top var(--size) / calc(4 * var(--size)) calc(51% - var(--size)) repeat-x;
}

Se você verificar o código, verá que além de combinar todos os gradientes, também reduzi sua altura de 100% para 51% de modo que ambos cubram metade do elemento. Sim, 51%. Precisamos desse pequeno percentual extra para uma pequena sobreposição que evita lacunas.

E os lados esquerdo e direito?

É o seu dever de casa! Pegue o que fizemos com os lados superior e inferior e tente atualizar os valores para obter os valores direito e esquerdo. Não se preocupe, é fácil e a única coisa que você precisa fazer é trocar os valores.

Se você tiver problemas, você sempre pode usar o gerador online para verificar o código e visualizar o resultado.

Linhas onduladas

Anteriormente, fizemos nossa primeira onda usando uma linha vermelha e preenchemos a parte inferior do elemento. Que tal essa linha ondulada? Isso é uma onda também! Melhor ainda é se pudermos controlar sua espessura com uma variável para que possamos reutilizá-la. Vamos fazer isso!

Não vamos começar do zero, mas sim pegar o código anterior e atualizá-lo. A primeira coisa a fazer é atualizar as paradas de cor dos gradientes. Ambos os gradientes começam de uma cor transparente para uma opaca, ou vice-versa. Para simular uma linha ou borda, precisamos começar de transparente, ir para opaque e depois voltar para transparente novamente:

#0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%

Acho que você já adivinhou que o --b variável é o que estamos usando para controlar a espessura da linha. Vamos aplicar isso aos nossos gradientes:

Sim, o resultado está longe de ser uma linha ondulada. Mas olhando de perto, podemos ver que um gradiente está criando corretamente a curvatura inferior. Então, tudo o que realmente precisamos fazer é retificar o segundo gradiente. Em vez de manter um círculo completo, vamos fazer um parcial como o outro gradiente.

Ainda longe, mas temos as duas curvaturas que precisamos! Se você verificar o código, verá que temos dois gradientes idênticos. A única diferença é o seu posicionamento:

.wave {
  --size: 50px;
  --b: 10px;
  --p: 25px;
  --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size));

  --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%;
  mask:
    radial-gradient(var(--R) at left 50% bottom calc(-1*var(--p)), var(--_g)) 
      calc(50% - 2*var(--size)) 0/calc(4*var(--size)) 100%,
    radial-gradient(var(--R) at left 50% top    calc(-1*var(--p)), var(--_g)) 
      50% var(--size)/calc(4*var(--size)) 100%;
}

Agora precisamos ajustar o tamanho e a posição para a forma final. Não precisamos mais que o gradiente seja de altura total, para que possamos substituir 100% com isso:

/* Size plus thickness */
calc(var(--size) + var(--b))

Não há lógica matemática por trás desse valor. Só precisa ser grande o suficiente para a curvatura. Veremos seu efeito no padrão daqui a pouco. Enquanto isso, vamos também atualizar a posição para centralizar verticalmente os gradientes:

.wave {
  --size: 50px;
  --b: 10px;
  --p: 25px;
  --R: sqrt(var(--p)*var(--p) + var(--size)*var(--size));

  --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%;  
  mask:
    radial-gradient(var(--R) at left 50% bottom calc(-1*var(--p)), var(--_g)) 
      calc(50% - 2*var(--size)) 50%/calc(4 * var(--size)) calc(var(--size) + var(--b)) no-repeat,
    radial-gradient(var(--R) at left 50% top calc(-1 * var(--p)), var(--_g)) 50%
      50%/calc(4 * var(--size)) calc(var(--size) + var(--b)) no-repeat;
}

Ainda não está lá:

Um gradiente precisa se mover um pouco para baixo e o outro um pouco para cima. Ambos precisam se mover pela metade de sua altura.

Estamos quase lá! Precisamos de uma pequena correção para que o raio tenha uma sobreposição perfeita. Ambas as linhas precisam se deslocar pela metade da borda (--b) espessura:

Conseguimos! Uma linha ondulada perfeita que podemos ajustar facilmente controlando algumas variáveis:

.wave {
  --size: 50px;
  --b: 10px;
  --p: 25px;
  --R: calc(sqrt(var(--p) * var(--p) + var(--size) * var(--size)) + var(--b) / 2);

  --_g: #0000 calc(99% - var(--b)), #000 calc(101% - var(--b)) 99%, #0000 101%;
  mask:
    radial-gradient(var(--R) at left 50% bottom calc(-1 * var(--p)), var(--_g)) 
     calc(50% - 2*var(--size)) calc(50% - var(--size)/2 - var(--b)/2) / calc(4 * var(--size)) calc(var(--size) + var(--b)) repeat-x,
    radial-gradient(var(--R) at left 50% top calc(-1*var(--p)),var(--_g)) 
     50%  calc(50% + var(--size)/2 + var(--b)/2) / calc(4 * var(--size)) calc(var(--size) + var(--b)) repeat-x;
}

Eu sei que a lógica demora um pouco para entender. Tudo bem e como eu disse, criar uma forma ondulada em CSS não é fácil, sem mencionar a matemática complicada por trás disso. Por isso o gerador online é um salva-vidas — você pode obter facilmente o código final, mesmo que não entenda completamente a lógica por trás dele.

Padrões ondulados

Podemos fazer um padrão a partir da linha ondulada que acabamos de criar!

Ah não, o código do padrão será ainda mais difícil de entender!

De jeito nenhum! Já temos o código. Tudo o que precisamos fazer é remover repeat-x do que já temos, e tada. 🎉

Um belo padrão ondulado. Lembra-se da equação que eu disse que iríamos revisitar?

/* Size plus thickness */
calc(var(--size) + var(--b))

Bem, isso é o que controla a distância entre as linhas no padrão. Podemos fazer disso uma variável, mas não há necessidade de mais complexidade. Eu nem estou usando uma variável para isso no gerador. Talvez eu mude isso mais tarde.

Aqui está o mesmo padrão indo em uma direção diferente:

Estou fornecendo a você o código nessa demonstração, mas gostaria que você dissesse e entendesse quais alterações fiz para que isso acontecesse.

Simplificando o código

Em todas as demos anteriores, sempre definimos o --size e --p independentemente. Mas você se lembra de como mencionei anteriormente que o gerador online avalia P como igual a m*S, Onde m controla a curvatura da onda? Ao definir um multiplicador fixo, podemos trabalhar com uma determinada onda e o código pode se tornar mais fácil. Isso é o que precisaremos na maioria dos casos: uma forma ondulada específica e uma variável para controlar seu tamanho.

Vamos atualizar nosso código e apresentar o m variável:

.wave {
  --size: 50px;
  --R: calc(var(--size) * sqrt(var(--m) * var(--m) + 1));

  mask:
    radial-gradient(var(--R) at 50% calc(var(--size) * (1 + var(--m))), #000 99%, #0000 101%) 
      calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%,
    radial-gradient(var(--R) at 50% calc(-1 * var(--size) * var(--m)), #0000 99%, #000 101%) 
      50% var(--size) / calc(4 * var(--size)) 100% repeat-x;
  }

Como você pode ver, não precisamos mais do --p variável. eu troquei por var(--m)*var(--size), e otimizou parte da matemática de acordo. Agora, se quisermos trabalhar com uma forma ondulada particular, podemos omitir o --m variável e substitua-a por um valor fixo. Vamos tentar .8 por exemplo.

--size: 50px;
--R: calc(var(--size) * 1.28);

mask:
  radial-gradient(var(--R) at 50% calc(1.8 * var(--size)), #000 99%, #0000 101%) 
    calc(50% - 2*var(--size)) 0/calc(4 * var(--size)) 100%,
  radial-gradient(var(--R) at 50% calc(-.8 * var(--size)), #0000 99%, #000 101%) 
    50% var(--size) / calc(4 * var(--size)) 100% repeat-x;

Viu como o código ficou mais fácil agora? Apenas uma variável para controlar sua onda, além de você não precisar mais depender sqrt() que não tem suporte para navegador!

Você pode aplicar a mesma lógica a todas as demos que vimos, mesmo para as linhas onduladas e o padrão. Comecei com uma explicação matemática detalhada e dei o código genérico, mas você pode precisar de um código mais fácil em um caso de uso real. Isso é o que eu estou fazendo o tempo todo. Raramente uso o código genérico, mas sempre considero uma versão simplificada principalmente que, na maioria dos casos, estou usando alguns valores conhecidos que não precisam ser armazenados como variáveis. (Spoiler alert: Vou compartilhar alguns exemplos no final!)

Limitações desta abordagem

Matematicamente, o código que criamos deve nos dar formas e padrões ondulados perfeitos, mas, na realidade, enfrentaremos alguns resultados estranhos. Então, sim, esse método tem suas limitações. Por exemplo, o gerador online é capaz de produzir resultados ruins, especialmente com linhas onduladas. Parte do problema se deve a uma combinação específica de valores em que o resultado é embaralhado, como usar um valor grande para a espessura da borda em comparação com o tamanho:

Como criar formas e padrões ondulados em CSS PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.
Como criar formas e padrões ondulados em CSS

Para os outros casos, é o problema relacionado a alguns arredondamentos que resultará em desalinhamento e lacunas entre as ondas:

Como criar formas e padrões ondulados em CSS PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.
Como criar formas e padrões ondulados em CSS

Dito isso, ainda acho que o método que abordamos continua sendo bom porque produz ondas suaves na maioria dos casos, e podemos facilmente evitar os maus resultados jogando com valores diferentes até chegarmos à perfeição.

Resumindo

Espero que, após este artigo, você não precise mais se atrapalhar com tentativa e erro para criar uma forma ou padrão ondulado. Além disso para o gerador online, você tem todos os segredos matemáticos por trás da criação de qualquer tipo de onda que quiser!

O artigo termina aqui, mas agora você tem uma ferramenta poderosa para criar designs sofisticados que usam formas onduladas. Aqui está a inspiração para você começar…

E você? Use meu gerador online (ou escreva o código manualmente se você já aprendeu toda a matemática de cor) e me mostre suas criações! Vamos ter uma boa coleção na seção de comentários.

Carimbo de hora:

Mais de Truques CSS