Hur man skapar vågiga former och mönster i CSS PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.

Hur man skapar vågiga former och mönster i CSS

Vågen är förmodligen en av de svåraste formerna att göra i CSS. Vi försöker alltid approximera det med egenskaper som border-radius och massor av magiska siffror tills vi får något som känns ganska nära. Och det är innan vi ens kommit in i vågiga mönster, som är svårare.

"SVG det!" kan du säga, och du har förmodligen rätt i att det är en bättre väg att gå. Men vi kommer att se att CSS kan skapa fina vågor och koden för det behöver inte vara helt galen. Och gissa vad? jag har en onlinegenerator för att göra det ännu mer trivialt!

Om du spelar med generatorn kan du se att CSS den spottar ut bara är två gradienter och en CSS-maskegenskap — bara de två sakerna och vi kan göra vilken form som helst av vågform eller mönster. För att inte tala om att vi enkelt kan kontrollera vågornas storlek och krökning medan vi håller på.

Vissa av värdena kan se ut som "magiska nummer” men det finns faktiskt logik bakom dem och vi kommer att dissekera koden och upptäcka alla hemligheter bakom att skapa vågor.

Den här artikeln är en uppföljning till en tidigare där jag byggde alla möjliga olika sicksack, scoped, bågade och ja, vågiga gränser. Jag rekommenderar starkt att du kontrollerar den artikeln eftersom den använder samma teknik som vi kommer att täcka här, men mer detaljerat.

Matematiken bakom vågor

Strängt taget finns det inte en magisk formel bakom vågiga former. Vilken form som helst med kurvor som går upp och ner kan kallas en våg, så vi kommer inte att begränsa oss till komplex matematik. Istället kommer vi att reproducera en våg med hjälp av geometrins grunder.

Låt oss börja med ett enkelt exempel med två cirkelformer:

Hur man skapar vågiga former och mönster i CSS

Vi har två cirklar med samma radie bredvid varandra. Ser du den röda linjen? Den täcker den övre halvan av den första cirkeln och den nedre halvan av den andra. Föreställ dig nu att du tar den linjen och upprepar den.

En snirklig röd linje i form av vågor.
Hur man skapar vågiga former och mönster i CSS

Vi ser redan vågen. Låt oss nu fylla den nedre delen (eller den översta) för att få följande:

Röd vågmönster.
Hur man skapar vågiga former och mönster i CSS

Tada! Vi har en vågig form, och en som vi kan styra med en variabel för cirkelradier. Det här är en av de enklaste vågorna vi kan göra och det är den jag visade upp mig i this tidigare artikel

Låt oss lägga till lite komplexitet genom att ta den första illustrationen och flytta cirklarna lite:

Två grå cirklar med två halverade streckade linjer som indikerar avstånd.
Hur man skapar vågiga former och mönster i CSS

Vi har fortfarande två cirklar med samma radier men de är inte längre horisontellt inriktade. I det här fallet täcker den röda linjen inte längre halva arean av varje cirkel, utan ett mindre område istället. Detta område begränsas av den streckade röda linjen. Den linjen korsar punkten där båda cirklarna möts.

Ta nu den linjen och upprepa den och du får ytterligare en våg, en jämnare.

En röd snirklig linje.
Hur man skapar vågiga former och mönster i CSS
Ett rött vågmönster.
Hur man skapar vågiga former och mönster i CSS

Jag tror att du förstår idén. Genom att kontrollera cirklarnas position och storlek kan vi skapa vilken våg vi vill. Vi kan till och med skapa variabler för dem, som jag kommer att kalla P och S, Respektive.

Hur man skapar vågiga former och mönster i CSS PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.
Hur man skapar vågiga former och mönster i CSS

Du har säkert märkt att vi i onlinegeneratorn styr vågen med två ingångar. De mappar till ovanstående variabler. S är "Size of the wave" och P är "vågens krökning".

Jag definierar P as P = m*S var m är variabeln du justerar när du uppdaterar kurvans kurvatur. Detta gör att vi alltid kan ha samma krökning, även om vi uppdaterar S.

m kan vara vilket värde som helst mellan 0 och 2. 0 kommer att ge oss det första speciella fallet där båda cirklarna är horisontellt riktade. 2 är ett slags maxvärde. Vi kan bli större, men efter några tester fann jag att något ovan 2 ger dåliga, platta former.

Låt oss inte glömma radien på vår cirkel! Det kan också definieras med hjälp av S och P så här:

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

När P är lika med 0, vi kommer att ha R = S/2.

Vi har allt för att börja konvertera allt detta till gradienter i CSS!

Skapa gradienter

Våra vågor använder cirklar, och när vi talar om cirklar talar vi om radiella gradienter. Och eftersom två cirklar definierar vår våg, kommer vi logiskt att använda två radiella gradienter.

Vi börjar med det särskilda fallet där P är lika med 0. Här är illustrationen av den första gradienten:

Denna gradient skapar den första krökningen medan den fyller ut hela bottenområdet - vågens "vatten" så att säga.

Hur man skapar vågiga former och mönster i CSS PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.
Hur man skapar vågiga former och mönster i 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;
}

Smakämnen --size variabel definierar radien och storleken på den radiella gradienten. Om vi ​​jämför det med S variabel, då är den lika med S/2.

Låt oss nu lägga till den andra gradienten:

Den andra gradienten är inget annat än en cirkel för att fullborda vår våg:

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

Om du markerar föregående artikel du kommer att se att jag bara upprepar det jag redan gjorde där.

Jag följde båda artiklarna men gradientkonfigurationerna är inte desamma.

Det beror på att vi kan nå samma resultat med olika gradientkonfigurationer. Du kommer att märka en liten skillnad i justeringen om du jämför båda konfigurationerna, men tricket är detsamma. Detta kan vara förvirrande om du inte är bekant med gradienter, men oroa dig inte. Med lite övning vänjer du dig vid dem och du kommer själv att upptäcka att olika syntax kan leda till samma resultat.

Här är hela koden för vår första våg:

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

Låt oss nu ta den här koden och justera den där vi introducerar en variabel som gör denna helt återanvändbar för att skapa vilken våg vi vill ha. Som vi såg i föregående avsnitt är huvudtricket att flytta cirklarna så att de inte längre är i linje, så låt oss uppdatera positionen för var och en. Vi flyttar den första upp och den andra ner.

Vår kod kommer att se ut så här:

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

Jag har introducerat en ny --p variabel som används för att definiera centrumpositionen för varje cirkel. Den första gradienten använder 50% calc(-1*var(--p)), så dess mitt flyttas uppåt medan den andra används calc(var(--size) + var(--p)) för att flytta ner den.

En demo säger mer än tusen ord:

Cirklarna är varken inriktade eller vidrör varandra. Vi placerade dem långt ifrån varandra utan att ändra deras radier, så vi tappade vår våg. Men vi kan fixa saker genom att använda samma matematik som vi använde tidigare för att beräkna den nya radien. Kom ihåg det R = sqrt(P² + S²)/2. I vårat fall, --size är lika med S/2; samma för --p som också är lika med P/2 eftersom vi flyttar båda cirklarna. Så avståndet mellan deras mittpunkter är det dubbla värdet av --p för detta:

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

Det ger oss ett resultat av 55.9px.

Vår våg är tillbaka! Låt oss koppla in den ekvationen i vår 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;
}

Detta är giltig CSS-kod. sqrt() är en del av specifikationen, men när jag skriver detta finns det inget webbläsarstöd för det. Det betyder att vi behöver lite JavaScript eller Sass för att beräkna det värdet tills vi blir bredare sqrt() stöd.

Det här är jävligt coolt: allt som krävs är två gradienter för att få en cool våg som du kan applicera på vilket element som helst med hjälp av mask fast egendom. Inga fler försök och misstag – allt du behöver är att uppdatera två variabler och du är igång!

Vända vågen

Tänk om vi vill att vågorna går åt andra hållet, där vi fyller i "himlen" istället för "vattnet". Tro det eller ej, allt vi behöver göra är att uppdatera två värden:

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

Allt jag gjorde där var att lägga till en offset lika med 100%, markerad ovan. Här är resultatet:

Vi kan överväga en mer användarvänlig syntax med nyckelordsvärden för att göra det ännu enklare:

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

Vi använder left och bottom nyckelord för att specificera sidorna och förskjutningen. Som standard har webbläsaren som standard left och top – det är därför vi använder 100% för att flytta elementet till botten. I verkligheten flyttar vi det från top by 100%, så det är egentligen samma sak som att säga bottom. Mycket lättare att läsa än matematik!

Med denna uppdaterade syntax är allt vi behöver göra att byta bottom för top — eller vice versa — för att ändra vågens riktning.

Och om du vill få både topp- och bottenvågor, kombinerar vi alla gradienter i en enda deklaration:

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

Om du kollar koden ser du att jag förutom att kombinera alla gradienter också har minskat deras höjd från 100% till 51% så att de båda täcker hälften av elementet. Ja, 51%. Vi behöver den där lilla extra procenten för en liten överlappning som undviker luckor.

Hur är det med vänster och höger sida?

Det är din läxa! Ta det vi gjorde med över- och undersidan och försök uppdatera värdena för att få höger och vänster värden. Oroa dig inte, det är enkelt och det enda du behöver göra är att byta värden.

Om du har problem kan du alltid använda online-generatorn för att kontrollera koden och visualisera resultatet.

Vågiga linjer

Tidigare gjorde vi vår första våg med en röd linje och fyllde sedan den nedre delen av elementet. Vad sägs om den där vågiga linjen? Det är en våg också! Ännu bättre är om vi kan kontrollera dess tjocklek med en variabel så att vi kan återanvända den. Vi gör det!

Vi ska inte börja om från början utan snarare ta den tidigare koden och uppdatera den. Det första du ska göra är att uppdatera färgstoppen för gradienterna. Båda gradienterna börjar från en transparent färg till en ogenomskinlig, eller vice versa. För att simulera en linje eller kant, måste vi börja från transparent, gå till ogenomskinlig och sedan tillbaka till transparent igen:

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

Jag tror att du redan gissat att --b variabel är vad vi använder för att kontrollera linjetjockleken. Låt oss tillämpa detta på våra gradienter:

Ja, resultatet är långt ifrån en vågig linje. Men när vi tittar noga kan vi se att en gradient skapar bottenkrökningen korrekt. Så allt vi behöver göra är att korrigera den andra gradienten. Istället för att hålla en hel cirkel, låt oss göra en partiell gradient som den andra.

Fortfarande långt, men vi har båda krökningarna vi behöver! Om du kontrollerar koden ser du att vi har två identiska gradienter. Den enda skillnaden är deras placering:

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

Nu måste vi justera storleken och positionen för den slutliga formen. Vi behöver inte längre gradienten vara i full höjd, så vi kan byta ut 100% med detta:

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

Det finns ingen matematisk logik bakom detta värde. Den behöver bara vara tillräckligt stor för krökningen. Vi kommer att se dess effekt på mönstret om en liten stund. Under tiden, låt oss också uppdatera positionen för att vertikalt centrera gradienterna:

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

Fortfarande inte riktigt där:

Den ena gradienten måste röra sig en bit ner och den andra en bit upp. Båda måste röra sig med hälften av sin längd.

Vi är nästan där! Vi behöver en liten fix för att radien ska ha en perfekt överlappning. Båda linjerna måste förskjutas med halva kanten (--b) tjocklek:

Vi har det! En perfekt våglinje som vi enkelt kan justera genom att kontrollera några variabler:

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

Jag vet att logiken tar lite att förstå. Det är bra och som sagt, att skapa en vågig form i CSS är inte lätt, för att inte tala om den knepiga matematiken bakom. Det är därför online generator är en livräddare — du kan enkelt få den slutliga koden även om du inte helt förstår logiken bakom den.

Vågiga mönster

Vi kan göra ett mönster från den vågiga linje vi just skapat!

Åh nej, koden för mönstret blir ännu svårare att förstå!

Inte alls! Vi har redan koden. Allt vi behöver göra är att ta bort repeat-x från vad vi redan har, och tada. 🎉

Ett fint vågmönster. Kommer du ihåg ekvationen jag sa att vi skulle återkomma till?

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

Jo, det är detta som styr avståndet mellan linjerna i mönstret. Vi kan göra en variabel av det, men det finns inget behov av mer komplexitet. Jag använder inte ens en variabel för det i generatorn. Jag kanske ändrar det senare.

Här är samma mönster som går i en annan riktning:

Jag förser dig med koden i den demon, men jag skulle vilja att du dissekerade den och förstår vilka ändringar jag gjorde för att få det att hända.

Förenkla koden

I alla tidigare demos definierar vi alltid --size och --p oberoende av. Men kommer du ihåg hur jag nämnde tidigare att onlinegeneratorn utvärderar P lika med m*SDär m styr vågens krökning? Genom att definiera en fast multiplikator kan vi arbeta med en viss våg och koden kan bli enklare. Detta är vad vi kommer att behöva i de flesta fall: en specifik vågform och en variabel för att kontrollera dess storlek.

Låt oss uppdatera vår kod och introducera m variabel:

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

Som du kan se behöver vi inte längre --p variabel. Jag ersatte den med var(--m)*var(--size), och optimerade en del av matematiken därefter. Nu, om vi vill arbeta med en viss vågform, kan vi utelämna --m variabel och ersätt den med ett fast värde. Låt oss försöka .8 till exempel.

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

Ser du hur koden är enklare nu? Bara en variabel att styra din våg, plus att du inte längre behöver lita på sqrt() som inte har något webbläsarstöd!

Du kan tillämpa samma logik på alla demos vi såg även för de vågiga linjerna och mönstret. Jag började med en detaljerad matematisk förklaring och gav den generiska koden, men du kanske behöver enklare kod i ett riktigt användningsfall. Det här är vad jag gör hela tiden. Jag använder sällan den generiska koden, men jag överväger alltid en förenklad version, särskilt att jag i de flesta fall använder några kända värden som inte behöver lagras som variabler. (Spoiler varning: Jag kommer att dela med mig av några exempel i slutet!)

Begränsningar för detta tillvägagångssätt

Matematiskt borde koden vi skapade ge oss perfekta vågiga former och mönster, men i verkligheten kommer vi att möta några konstiga resultat. Så, ja, denna metod har sina begränsningar. Till exempel kan onlinegeneratorn ge dåliga resultat, särskilt med vågiga linjer. En del av problemet beror på en speciell kombination av värden där resultatet blir förvrängt, som att använda ett stort värde för kanttjockleken jämfört med storleken:

Hur man skapar vågiga former och mönster i CSS PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.
Hur man skapar vågiga former och mönster i CSS

För de andra fallen är det problemet relaterat till någon avrundning som kommer att resultera i felinriktning och luckor mellan vågorna:

Hur man skapar vågiga former och mönster i CSS PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.
Hur man skapar vågiga former och mönster i CSS

Som sagt, jag tror fortfarande att metoden vi täckte fortfarande är bra eftersom den producerar jämna vågor i de flesta fall, och vi kan enkelt undvika de dåliga resultaten genom att spela med olika värden tills vi får det perfekt.

Inslagning upp

Jag hoppas att du efter den här artikeln inte längre kommer att fumla med trial and error för att bygga en vågig form eller mönster. För övrigt till onlinegeneratorn, du har alla matematiska hemligheter bakom att skapa vilken typ av våg du vill!

Artikeln slutar här men nu har du ett kraftfullt verktyg för att skapa snygga mönster som använder vågiga former. Här är inspiration för att komma igång...

Hur är det med dig? Använd min onlinegenerator (eller skriv koden manuellt om du redan har lärt dig all matematik utantill) och visa mig dina skapelser! Låt oss ha en bra samling i kommentarsfältet.

Tidsstämpel:

Mer från CSS-tricks