Come creare forme e modelli ondulati in CSS PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Come creare forme e motivi ondulati in CSS

L'onda è probabilmente una delle forme più difficili da realizzare in CSS. Cerchiamo sempre di approssimarlo con proprietà come border-radius e un sacco di numeri magici finché non otteniamo qualcosa che sembra un po' vicino. E questo prima ancora di entrare in schemi ondulati, che sono più difficili.

"SVG!" potresti dire, e probabilmente hai ragione, è un modo migliore di andare. Ma vedremo che i CSS possono fare belle onde e il codice non deve essere del tutto pazzo. E indovina cosa? io ho un generatore in linea per renderlo ancora più banale!

Se giochi con il generatore, puoi vedere che il CSS che sputa è solo due gradienti e una proprietà della maschera CSS: solo queste due cose e possiamo creare qualsiasi tipo di forma d'onda o modello. Per non parlare del fatto che possiamo facilmente controllare le dimensioni e la curvatura delle onde mentre ci siamo.

Alcuni dei valori potrebbero apparire come "numeri magici" ma in realtà c'è una logica dietro di loro e analizzeremo il codice e scopriremo tutti i segreti dietro la creazione delle onde.

Questo articolo è un seguito di una precedente dove ho costruito tutti i tipi di diversi bordi di confine a zig-zag, con mirino, smerlati e sì, ondulati. Consiglio vivamente di controllare quell'articolo poiché utilizza la stessa tecnica che tratteremo qui, ma in modo più dettagliato.

La matematica dietro le onde

A rigor di termini, non c'è una formula magica dietro le forme ondulate. Qualsiasi forma con curve che salgono e scendono può essere chiamata onda, quindi non ci limiteremo a calcoli complessi. Invece, riprodurremo un'onda usando le basi della geometria.

Iniziamo con un semplice esempio utilizzando due forme circolari:

Come creare forme e motivi ondulati in CSS

Abbiamo due cerchi con lo stesso raggio uno accanto all'altro. Vedi quella linea rossa? Copre la metà superiore del primo cerchio e la metà inferiore del secondo. Ora immagina di prendere quella linea e di ripeterla.

Una linea rossa ondulata a forma di onde.
Come creare forme e motivi ondulati in CSS

Vediamo già l'onda. Ora riempiamo la parte inferiore (o quella superiore) per ottenere quanto segue:

Motivo a onde rosse.
Come creare forme e motivi ondulati in CSS

Tada! Abbiamo una forma ondulata e una che possiamo controllare usando una variabile per i raggi del cerchio. Questa è una delle onde più facili che possiamo creare ed è quella in cui mi sono messo in mostra this precedente articolo

Aggiungiamo un po' di complessità prendendo la prima illustrazione e spostando un po' i cerchi:

Due cerchi grigi con due linee tratteggiate bisecanti che indicano la spaziatura.
Come creare forme e motivi ondulati in CSS

Abbiamo ancora due cerchi con gli stessi raggi ma non sono più allineati orizzontalmente. In questo caso, la linea rossa non copre più metà dell'area di ciascun cerchio, ma un'area più piccola. Quest'area è delimitata dalla linea rossa tratteggiata. Quella linea attraversa il punto in cui entrambi i cerchi si incontrano.

Ora prendi quella linea e ripetila e otterrai un'altra onda, più liscia.

Una linea ondulata rossa.
Come creare forme e motivi ondulati in CSS
Un motivo a onde rosse.
Come creare forme e motivi ondulati in CSS

Penso che tu abbia l'idea. Controllando la posizione e la dimensione dei cerchi, possiamo creare qualsiasi onda desideriamo. Possiamo anche creare variabili per loro, che chiamerò P ed S, Rispettivamente.

Come creare forme e modelli ondulati in CSS PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.
Come creare forme e motivi ondulati in CSS

Probabilmente avrai notato che, nel generatore online, controlliamo l'onda utilizzando due ingressi. Si associano alle variabili di cui sopra. S è la "dimensione dell'onda" e P è la “curvatura dell'onda”.

Sto definendo P as P = m*S where m è la variabile che regoli quando aggiorni la curvatura dell'onda. Questo ci permette di avere sempre la stessa curvatura, anche se aggiorniamo S.

m può essere qualsiasi valore tra 0 ed 2. 0 ci darà il primo caso particolare in cui entrambi i cerchi sono allineati orizzontalmente. 2 è una specie di valore massimo. Possiamo andare più grandi, ma dopo alcuni test ho trovato qualcosa sopra 2 produce forme brutte e piatte.

Non dimentichiamo il raggio del nostro cerchio! Questo può anche essere definito usando S ed P come questo:

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

Quando P è uguale a 0, avremo R = S/2.

Abbiamo tutto per iniziare a convertire tutto questo in gradienti in CSS!

Creazione di gradienti

Le nostre onde usano i cerchi e quando parliamo di cerchi parliamo di gradienti radiali. E poiché due cerchi definiscono la nostra onda, useremo logicamente due gradienti radiali.

Inizieremo con il caso particolare in cui P è uguale a 0. Ecco l'illustrazione del primo gradiente:

Questo gradiente crea la prima curvatura mentre riempie l'intera area del fondale, l '"acqua" dell'onda per così dire.

Come creare forme e modelli ondulati in CSS PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.
Come creare forme e motivi ondulati in 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;
}

I --size variabile definisce il raggio e la dimensione del gradiente radiale. Se lo confrontiamo con il S variabile, allora è uguale a S/2.

Ora aggiungiamo il secondo gradiente:

Il secondo gradiente non è altro che un cerchio per completare la nostra 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 si seleziona l'articolo precedente vedrai che sto semplicemente ripetendo quello che ho già fatto lì.

Ho seguito entrambi gli articoli ma le configurazioni del gradiente non sono le stesse.

Questo perché possiamo raggiungere lo stesso risultato utilizzando diverse configurazioni di gradiente. Noterai una leggera differenza nell'allineamento se confronti entrambe le configurazioni, ma il trucco è lo stesso. Questo può creare confusione se non hai familiarità con i gradienti, ma non preoccuparti. Con un po' di pratica, ti ci abitui e scoprirai da solo che una sintassi diversa può portare allo stesso risultato.

Ecco il codice completo per la nostra prima ondata:

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

Ora prendiamo questo codice e lo adattiamo al punto in cui introduciamo una variabile che lo rende completamente riutilizzabile per creare qualsiasi onda desideriamo. Come abbiamo visto nella sezione precedente, il trucco principale è spostare i cerchi in modo che non siano più allineati, quindi aggiorniamo la posizione di ciascuno. Sposteremo il primo in alto e il secondo in basso.

Il nostro codice sarà simile a questo:

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

Ho introdotto un nuovo --p variabile che l'ha usata per definire la posizione centrale di ogni cerchio. Il primo gradiente sta usando 50% calc(-1*var(--p)), quindi il suo centro si sposta verso l'alto mentre viene utilizzato il secondo calc(var(--size) + var(--p)) per spostarlo verso il basso.

Una demo vale più di mille parole:

I cerchi non sono né allineati né si toccano. Li abbiamo distanziati l'uno dall'altro senza cambiare i loro raggi, quindi abbiamo perso la nostra onda. Ma possiamo sistemare le cose usando la stessa matematica che abbiamo usato in precedenza per calcolare il nuovo raggio. Ricordati che R = sqrt(P² + S²)/2. Nel nostro caso, --size è uguale a S/2; lo stesso per --p che è anche uguale a P/2 poiché stiamo muovendo entrambi i cerchi. Quindi, la distanza tra i loro punti centrali è il doppio del valore di --p per questo:

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

Questo ci dà un risultato di 55.9px.

La nostra onda è tornata! Inseriamo quell'equazione nel nostro 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;
}

Questo è un codice CSS valido. sqrt() fa parte delle specifiche, ma al momento in cui scrivo questo, non è disponibile il supporto del browser. Ciò significa che abbiamo bisogno di una spruzzata di JavaScript o Sass per calcolare quel valore fino a quando non diventiamo più ampi sqrt() supporto.

Questo è dannatamente interessante: bastano due gradienti per ottenere un'onda interessante che puoi applicare a qualsiasi elemento usando mask proprietà. Niente più tentativi ed errori: tutto ciò che serve è aggiornare due variabili e sei a posto!

Invertire l'onda

E se vogliamo che le onde vadano nell'altra direzione, dove stiamo riempiendo il "cielo" invece dell'"acqua". Che ci crediate o no, tutto ciò che dobbiamo fare è aggiornare due valori:

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

Tutto quello che ho fatto è aggiungere un offset uguale a 100%, evidenziato sopra. Ecco il risultato:

Possiamo considerare una sintassi più amichevole utilizzando i valori delle parole chiave per renderlo ancora più semplice:

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

Stiamo usando il left ed bottom parole chiave per specificare i lati e l'offset. Per impostazione predefinita, il browser è impostato su left ed top — ecco perché usiamo 100% per spostare l'elemento in basso. In realtà, lo stiamo spostando dal top by 100%, quindi è davvero come dire bottom. Molto più facile da leggere della matematica!

Con questa sintassi aggiornata, tutto ciò che dobbiamo fare è scambiare bottom per top — o viceversa — per cambiare la direzione dell'onda.

E se vuoi ottenere sia l'onda superiore che quella inferiore, combiniamo tutti i gradienti in un'unica dichiarazione:

.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 controlli il codice, vedrai che oltre a combinare tutti i gradienti, ho anche ridotto la loro altezza da 100% a 51% in modo che entrambi coprano metà dell'elemento. Sì, 51%. Abbiamo bisogno di quella piccola percentuale in più per una piccola sovrapposizione che eviti le lacune.

E i lati sinistro e destro?

Sono i tuoi compiti! Prendi quello che abbiamo fatto con i lati superiore e inferiore e prova ad aggiornare i valori per ottenere i valori destro e sinistro. Non preoccuparti, è facile e l'unica cosa che devi fare è scambiare i valori.

Se hai problemi, puoi sempre usare il generatore in linea per controllare il codice e visualizzare il risultato.

Linee ondulate

In precedenza, abbiamo creato la nostra prima ondata utilizzando una linea rossa, quindi abbiamo riempito la parte inferiore dell'elemento. Che ne dici di quella linea ondulata? Anche quella è un'onda! Ancora meglio è se possiamo controllarne lo spessore con una variabile in modo da poterlo riutilizzare. Facciamolo!

Non inizieremo da zero, ma prenderemo il codice precedente e lo aggiorneremo. La prima cosa da fare è aggiornare le interruzioni di colore dei gradienti. Entrambi i gradienti partono da un colore trasparente a uno opaco, o viceversa. Per simulare una linea o un bordo, dobbiamo iniziare da trasparente, passare a opaco, quindi di nuovo di nuovo trasparente:

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

Penso che tu abbia già intuito che il --b variabile è ciò che stiamo usando per controllare lo spessore della linea. Applichiamo questo ai nostri gradienti:

Sì, il risultato è tutt'altro che una linea ondulata. Ma guardando da vicino, possiamo vedere che un gradiente sta creando correttamente la curvatura del fondo. Quindi, tutto ciò che dobbiamo davvero fare è rettificare il secondo gradiente. Invece di mantenere un cerchio completo, facciamone uno parziale come l'altro gradiente.

Ancora lontano, ma abbiamo entrambe le curvature di cui abbiamo bisogno! Se controlli il codice, vedrai che abbiamo due gradienti identici. L'unica differenza è il loro posizionamento:

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

Ora dobbiamo regolare le dimensioni e la posizione per la forma finale. Non abbiamo più bisogno che il gradiente sia a tutta altezza, quindi possiamo sostituirlo 100% con questo:

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

Non c'è una logica matematica dietro questo valore. Deve solo essere abbastanza grande per la curvatura. Vedremo il suo effetto sul modello tra poco. Nel frattempo aggiorniamo anche la posizione per centrare verticalmente i gradienti:

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

Ancora non proprio lì:

Un gradiente deve spostarsi leggermente verso il basso e l'altro leggermente verso l'alto. Entrambi devono muoversi della metà della loro altezza.

Siamo quasi li! Abbiamo bisogno di una piccola correzione affinché il raggio abbia una sovrapposizione perfetta. Entrambe le linee devono essere sfalsate della metà del bordo (--b) spessore:

Ce l'abbiamo! Una linea ondulata perfetta che possiamo facilmente regolare controllando alcune variabili:

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

So che ci vuole un po' per capire la logica. Va bene e, come ho detto, creare una forma ondulata in CSS non è facile, per non parlare della complicata matematica che c'è dietro. Ecco perché il generatore online è un vero toccasana: puoi facilmente ottenere il codice finale anche se non ne comprendi appieno la logica.

Motivi ondulati

Possiamo creare un motivo dalla linea ondulata che abbiamo appena creato!

Eh no, il codice del pattern sarà ancora più difficile da capire!

Affatto! Abbiamo già il codice. Tutto quello che dobbiamo fare è rimuovere repeat-x da quello che abbiamo già, e tada. 🎉

Un bel motivo ondulato. Ricordi l'equazione che ho detto che avremmo rivisitato?

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

Bene, questo è ciò che controlla la distanza tra le linee nel motivo. Possiamo ricavarne una variabile, ma non c'è bisogno di maggiore complessità. Non sto nemmeno usando una variabile per quello nel generatore. Forse lo cambierò più tardi.

Ecco lo stesso schema che va in una direzione diversa:

Ti sto fornendo il codice in quella demo, ma vorrei che lo sezionassi e capissi quali modifiche ho apportato per farlo accadere.

Semplificare il codice

In tutte le demo precedenti, definiamo sempre il --size ed --p indipendentemente. Ma ti ricordi come ho detto prima che il generatore online valuta P come uguale a m*S, Dove m controlla la curvatura dell'onda? Definendo un moltiplicatore fisso, possiamo lavorare con un'onda particolare e il codice può diventare più semplice. Questo è ciò di cui avremo bisogno nella maggior parte dei casi: una forma ondulata specifica e una variabile per controllarne le dimensioni.

Aggiorniamo il nostro codice e introduciamo il m variabili:

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

Come puoi vedere, non abbiamo più bisogno del --p variabile. L'ho sostituito con var(--m)*var(--size)e ottimizzato di conseguenza alcuni dei calcoli. Ora, se vogliamo lavorare con una particolare forma ondulata, possiamo omettere il --m variabile e sostituirla con un valore fisso. Proviamo .8 per esempio.

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

Vedi come il codice è più semplice ora? Solo una variabile per controllare la tua onda, in più non devi più fare affidamento sqrt() che non ha il supporto del browser!

Puoi applicare la stessa logica a tutte le demo che abbiamo visto anche per le linee ondulate e il pattern. Ho iniziato con una spiegazione matematica dettagliata e ho fornito il codice generico, ma potresti trovarti ad aver bisogno di un codice più semplice in un caso d'uso reale. Questo è quello che faccio sempre. Uso raramente il codice generico, ma considero sempre una versione semplificata, in particolare che, nella maggior parte dei casi, sto utilizzando alcuni valori noti che non devono essere archiviati come variabili. (Spoiler alert: Condividerò alcuni esempi alla fine!)

Limitazioni a questo approccio

Matematicamente, il codice che abbiamo creato dovrebbe darci forme e modelli ondulati perfetti, ma in realtà ci troveremo di fronte a strani risultati. Quindi, sì, questo metodo ha i suoi limiti. Ad esempio, il generatore online è in grado di produrre scarsi risultati, soprattutto con linee ondulate. Parte del problema è dovuto a una particolare combinazione di valori in cui il risultato viene criptato, come l'utilizzo di un valore elevato per lo spessore del bordo rispetto alla dimensione:

Come creare forme e modelli ondulati in CSS PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.
Come creare forme e motivi ondulati in CSS

Per gli altri casi, è il problema relativo ad alcuni arrotondamenti che si tradurranno in disallineamenti e spazi tra le onde:

Come creare forme e modelli ondulati in CSS PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.
Come creare forme e motivi ondulati in CSS

Detto questo, penso ancora che il metodo che abbiamo trattato rimanga buono perché nella maggior parte dei casi produce onde morbide e possiamo facilmente evitare i cattivi risultati giocando con valori diversi fino a quando non lo otteniamo perfetto.

Concludendo

Spero che dopo questo articolo, non dovrai più armeggiare con tentativi ed errori per costruire una forma o un motivo ondulato. Inoltre al generatore in linea, hai tutti i segreti matematici dietro la creazione di qualsiasi tipo di onda tu voglia!

L'articolo finisce qui ma ora hai un potente strumento per creare disegni fantasiosi che utilizzano forme ondulate. Ecco l'ispirazione per iniziare...

E tu? Usa il mio generatore online (o scrivi il codice manualmente se hai già imparato tutta la matematica a memoria) e mostrami le tue creazioni! Facciamo una buona raccolta nella sezione commenti.

Timestamp:

Di più da Trucchi CSS