1 Del CSS Rainbow Lutning Oändlighet

0
32

Jag först fick idén att CSS något av den typ som när jag såg denna gradient infinity-logo av Infographic Paradise:

Den ursprungliga lutning oändlighet.

Efter fyra timmar och tjugo minuter, varav över fyra timmar spenderades på tweaking positionering, kanter och höjdpunkter… jag hade äntligen resultatet nedan:

Min version av regnbågen lutning oändlighet.

Lutningen inte ser ut som i den ursprungliga bilden, som jag valde att skapa the rainbow logiskt istället för att använda Dev Verktyg picker eller något liknande, men andra än att jag tror att jag fick ganska nära—låt oss se hur jag gjorde det!

Markup

Som du har säkert redan gissat av namnet, HTML är bara en del:

<div class=’∞’></div>

Styling

Beslut om tillvägagångssätt

Den första tanken som kan komma att tänka när man ser ovanstående skulle vara med koniska gradienter som gränsen bilder. Tyvärr, border-image och border-radius inte spelar bra tillsammans, vilket framgår av den interaktiva demo nedan:

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

När vi sätter en gräns-bild, border-radius blir bara ignoreras, så använder de två tillsammans är tyvärr inte ett alternativ.

Så närmar vi tar här hjälp av kon-gradient() bakgrund och sedan få bort en del i mitten med hjälp av en mask. Låt oss se hur det fungerar!

Skapa de två ∞ halverar

Vi först besluta om en yttre diameter.

$att göra: 12.5 em;

Vi skapar de två halvorna av infinity symbol med hjälp av ::före och ::after pseudo-element i vår .∞ elementet. För att dessa två pseudo-element bredvid varandra, vi använder en flex layout på sina föräldrar (infinity-element .∞). Var och en av dessa har både bredd och höjd är lika med det yttre diameter $att göra. Vi har också runt dem med en border-radius på 50% och vi ger dem en dummy bakgrunden så att vi kan se dem.

.∞ {
display: flex;

&innan, &efter {
bredd: $att göra; height: $att göra;
border-radius: 50%;
bakgrund: #000;
innehåll: “;
}
}

Vi har också placerat .∞ elementet i mitten av sin förälder (kroppen i detta fall) både vertikalt och horisontellt med hjälp flexbox strategi.

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Hur kon-gradient() fungerar

För att skapa kon-gradient() bakgrund för de två måsten, måste vi först förstå hur den koniska-gradient () – funktionen fungerar.

Om inne i kon-gradient() funktion, vi har en lista med stopp utan erotiska positioner, då det första tas till vara på 0% (eller 0deg, samma sak), den sista är tagen för att vara på 100% (eller 360deg), medan alla de som återstår fördelas jämnt i [0% till 100%] – intervallet.

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Om vi har bara 2 tunnelbanestationer, det är enkelt. Den första är på 0%, den andra (och sista) till 100% och det finns inga andra stannar i mellan.

Om vi har 3 stationer, den första är på 0%, den sista (tredje) på 100%, medan den andra är död i mitten av [0% till 100%] intervall på 50%.

Om vi har 4 stationer, den första är på 0%, den sista (fjärde) på 100%, medan den andra och tredje dela [0% till 100%] intervall i 3 lika stora intervall, som är placerad vid 33.(3)% och 66.(6) procent.

Om vi har 5 stationer, den första är på 0%, den sista (femte) på 100%, medan andra, tredje och fjärde dela [0% till 100%] intervall i 4 lika stora intervall att vara placerad på 25%, 50% respektive 75%.

Om vi har 6 hållplatser, den första är på 0%, den sista (sjätte) på 100%, medan andra, tredje, fjärde och femte dela [0% till 100%] intervall i 5 lika stora intervall att vara placerad på 20%, 40%, 60% och 80%.

I allmänhet, om vi har n slutar, den första är på 0%, den sista på 100%, medan de i mellan split [0% till 100%] – intervallet i n-1 eqial intervall som spänner över 100%/(n-1) vardera. Om vi ger den slutar 0-baserade index, då var och en av dem är placerad i*100%/(n-1).

För det första, jag är 0, vilket ger oss 0*100%/(n-1) = 0%.

För den sista (n-th), jag är n-1, vilket ger oss (n-1)*100%/(n-1) = 100%.

Här har vi valt att använda 9 stannar vilket innebär att vi delar upp [0% till 100%] intervall i 8 lika stora intervall.

Okej, men hur får vi stopp listan?

Hsl() stannar

Jo, för enkelhetens skull väljer vi att skapa det som en lista av HSL värden. Vi håller mättnad och ljushet fast och vi varierar nyans. Nyans är en vinkel värde som går från 0 till 360, som vi kan se här:

Visuell framställning av färg skala från 0 till 360 (mättnad och ljushet hålls konstant).

Med detta i åtanke, kan vi konstruera en lista av hsl() slutar med fast mättnad och ljushet och olika nyans om vi vet det börjar nyans $hue-start, det nyans utbud $hue-sortimentet (detta är slutet nyans minus börja nyans) och antal stopp $num-stopp.

Låt oss säga att vi håller mättnad och ljushet fast på 85% och 57%, respektive (godtyckliga värden som förmodligen kan justeras för bättre resultat) och, till exempel, kan vi gå från en början nyans på 240 till ett slut nyans av 300 och 4 hållplatser.

För att generera denna listan stopp, vi använder en få-stopp() funktion som tar dessa tre saker som argument:

@funktionen get-stannar($hue-start, $hue-sortimentet, $num-stopp) {}

Vi skapar listan stopp $lista som ursprungligen är tom (och där vi ska återkomma i slutet efter att vi fylla den). Vi beräknar också den spännvidd av en av de jämna mellanrum våra stopp split full början till slutet av intervall i (sek enhet).

@funktionen get-stannar($hue-start, $hue-sortimentet, $num-stopp) {
$listan: ();
$enheten: $hue-sortiment/($num-stannar – 1);

/* uppdatera listan stopp $lista */

@return $lista
}

För att fylla våra $lista, vi loopa igenom den stannar, beräkna den aktuella nyans, använda den aktuella nyans för att skapa hsl() värde på att stanna och sedan lägga till den i listan av hållplatser:

@$jag från 0 till $num-stannar {
$hue-valuta: sek hue-start + $*$enhet.
$lista: $lista, hsl($hue-curr, 85%, 57%);
}

Vi kan nu använda stoppa lista denna funktion returnerar för någon form av lutning, som det kan ses från användningsexempel för denna funktion visas i den interaktiva demo nedan (navigering fungerar både genom att använda föregående/nästa-knappar på sidorna, liksom piltangenterna och page down/ page Up tangenterna):

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Notera hur, när vårt sortiment passerar ena änden av [0, 360] – intervallet, den fortsätter från den andra änden. Till exempel, när börjar nyans är 30 och utbudet är -210 (fjärde exempel), kan vi bara gå ner till 0, så fortsätter vi sedan går ner från 360.

Koniska gradienter för våra två halvor

Okej, men hur gör vi bestämma $hue-start och $hue-sortimentet för våra enskilda fallet?

I den ursprungliga bilden, vi drar en linje mellan de centrala punkterna av de två halvorna av loop, och med utgångspunkt från denna linje, gå medurs i båda fallen kan vi se om vi utgår från och där hamnar vi i [0, 360] nyans intervall och vad andra nyanser vi passerar igenom.

Vi börjar från den linje som förbinder centrala punkter i två halvor, och vi går runt dem i medurs riktning.

För att förenkla saker och ting, vi anser att vi passerar genom hela [0, 360] nyans skala som går längs våra infinity symbol. Detta innebär att intervallet för varje hälften är 180 (hälften av 360) i absolut värde.

Sökord till hue värden korrespondens till mättnad och ljushet fast vid 100% respektive 50%.

På den vänstra halvan, vi börjar från något som ser ut som det är i mellan någon form av cyan (hue 180) och någon form av kalk (hue 120), så tar vi börja nyans motsvara genomsnittet av nyanser av dessa två (180 + 120)/2 = 150.

Planen för den vänstra halvan.

Vi få till någon form av röda, som är 180 avstånd från start-värdet, så på 330, om vi dra ifrån eller lägga till 180:

(150 – 180 + 360)%360 = (150 + 180 + 360)%360 = 330

Så… gör vi gå upp eller ner? Tja, vi passerar genom den gula som är runt 60 på nyans skala, så som kommer ner från 150, inte upp. Går ner betyder att vårt utbud är negativ (-180).

Planen för den högra halvan.

På den högra halvan, vi börjar också från samma nyans mellan cyan och lime (150) och vi också slutet på samma typ av röd (330), men den här gången går vi igenom blues, som är cirka 240, vilket innebär att vi går upp från vår start nyans på 150, så vårt sortiment är positiva i detta fall (180).

Så långt som antalet stopp går, 9 bör räcka.

Nu uppdatera vår kod med hjälp av värdena för den vänstra halvan som standardprogram för vår funktion:

@funktionen get-stannar($hue-start: 150, $hue-sortimentet: -180, $num-slutar: 9) {
/* samma som innan */
}

.∞ {
display: flex;

&innan, &efter {
/* samma som innan */
bakgrund: kon-gradient(få-stopp());
}

&efter {
bakgrund: kon-gradient(get-stannar(150, 180));
}
}

Och nu våra två skivor har kon-gradient() bakgrund:

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Men vi vill inte ha dessa koniska gradienter för att börja från toppen.

För första skiva, vi vill att det ska börja från rätt—det är i 90° från toppen i medurs (positiv) riktning. För den andra skivan, vi vill att det ska börja från vänster—det är i 90° från toppen i den andra (negativ) riktning, vilket motsvarar 270° från toppen i medurs riktning (eftersom negativa vinklar tycks inte fungerar från någon anledning).

Kantiga förskjutningar från toppen för våra två halvor.

Låt oss modifiera vår kod för att uppnå detta:

.∞ {
display: flex;

&innan, &efter {
/* samma som innan */
bakgrund: kon-gradient(från 90deg, få-stopp());
}

&efter {
bakgrund: kon-gradient(från 270deg, få-stannar(150, 180));
}
}

Så långt, så bra!

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Från 🥧 att 🍩

Nästa steg är att skära hål av våra två halvor. Vi gör detta med en mask eller, mer precist, med en radial gradient (). Detta klipper ut Kanten stöd för nu, men eftersom det är något som är under utveckling, är det antagligen kommer att vara en cross-browser lösning någon gång i en inte alltför avlägsen framtid.

Kom ihåg att CSS lutning masker är alpha masker som standard (och bara Firefox för närvarande kan ändra detta via mask-läge), vilket innebär att endast alfa-kanal frågor. Blanda masken över våra element gör att varje pixel av detta element använda en alfa-kanal för motsvarande pixel i mask. Om masken pixel är helt transparent (alfa-värde är 0), sedan så kommer motsvarande pixel i elementet.

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

För att skapa masken, vi beräkna den yttre radie $ro (hälften av den yttre diameter $do) och inre radie $ri (en bråkdel av den yttre radie $ro).

$ro: .5*$att göra;
$ri: .52*$ro;
$m: radial gradient(transparent $ri, röda 0);

Vi satte sedan masken på våra två halvor:

.∞ {
/* samma som innan */

&innan, &efter {
/* samma som innan */
mask: $m;
}
}

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Denna ser perfekt ut i Firefox, men kanterna av radiella gradienter med abrupta övergångar från en hållplats till en annan ser ful i Krom och, följaktligen, så gör de inre kanterna av våra ringar.

Närbild på den inre kanten av den högra halvan i Chrome.

Fix här skulle vara att inte få en abrupt övergång mellan hållplatser, men sprida ut det över ett litet avstånd, låt oss säga en halv pixel:

$m: radial gradient(transparent calc(#{$ri} – .5px), red $ri);

Vi har nu fått bort ojämna kanter i Chrome:

Närbild på den inre kanten av den högra halvan i Chrome efter att sprida ut övergången mellan stannar över en halv pixel.

Följande steg är att kompensera för de två halvorna så att de faktiskt utgör en infinity symbol. Den synliga cirkulär remsor båda har samma bredd, skillnaden mellan den yttre radie $ro och inre radie $ri. Detta innebär att vi måste flytta varje sidled genom att hälften av denna skillnad $ri – $ri.

.∞ {
/* samma som innan */

&innan, &efter {
/* samma som innan */
margin: 0 (-.5*($ro – $ri));
}
}

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Skär halvorna

Vi kommer allt närmare, men vi har fortfarande ett mycket stort problem här. Vi vill inte att den högra delen av slingan för att vara helt över den vänstra. I stället vill vi att den övre delen av den högra delen att vara över den vänstra delen och den nedre delen av den vänstra delen för att vara över den högra del.

Så hur uppnår vi det?

Vi tar ett liknande tillvägagångssätt som presenteras i en äldre artikel: använda 3D!

För att bättre förstå hur detta fungerar, anser två kort exempel nedan. När vi rotera dem runt deras axlar x, de är inte i planet på skärmen längre. En positiv rotation ger botten fram och skjuter upp ryggen. En negativ rotation ger upp framåt och skjuter ned tillbaka.

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Observera att demo ovan fungerar inte i Kanten.

Så om vi ger den till vänster en positiv rotation och den högra en negativ rotation, sedan den övre halvan av den till höger visas framför den övre halvan av den till vänster och tvärtom för den nedre halvan.

Addiing perspektiv gör vad som närmare våra ögon verkar större och vad som finns längre bort verkar mindre och vi använder alldeles mindre vinklar. Utan det, vi har 3D-plan korsningen utan 3D-utseende.

Observera att både våra halverar behovet av att vara i samma 3D-sammanhang, något som uppnås genom att omvandla stil: bevara-3d .∞ elementet.

.∞ {
/* samma som innan */
förvandla-style: bevara-3d.

&innan, &efter {
/* samma som innan */
förändra: rotatex(1deg);
}

&efter {
/* samma som innan */
förändra: rotatex(-1deg);
}
}

Och nu är vi nästan där, men inte riktigt:

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Finjustering

Vi har en liten röd remsa i mitten eftersom lutningen slutar och korsningen linje inte riktigt matcha:

Närbild på små problem i skärningspunkten mellan de två halvorna.

En ganska ful, men effektiv lösning är att lägga till en 1px översättning innan rotation på den högra delen (:: after pseudo-element):

.∞:efter { transform: översätta(1px) rotatex(-1deg) }

Mycket bättre!

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Det är fortfarande inte perfekt ändå. Eftersom de inre kanterna av våra två ringar är lite suddig, övergången mellan dem och knaprigt yttre ser lite udda, så kanske vi kan göra det bättre:

Närbild av kontinuitet fråga (skarpa ytterkanter möte suddiga inre).

En quick fix här skulle vara att lägga till en radial gradient() locket på vart och ett av de två halvorna. Det här locket är transparent vit för de flesta av de omaskerade del av två halvor och går till ett fast vitt längs både sin inre och yttre kanter så att vi har en bra kontinuitet:

$gc: radial gradient(#fff $ri, rgba(#fff, 0) calc(#{$ri} + 1px),
rgba(#fff, 0) calc(#{$ro} – 1px), #fff calc(#{$ro} – .5px));

.∞ {
/* samma som innan */

&innan, &efter {
/* samma som innan */
bakgrund: $gc -, koniska-gradient(från 90deg, få-stopp());
}

&efter {
/* samma som innan */
bakgrund: $gc -, koniska-gradient(från 270deg, få-stannar(150, 180));
}
}

Fördelen blir mer uppenbar när vi lägger till en mörk bakgrund till kroppen:

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Nu ser det ut bättre även när du zoomar in:

Ingen mer skarp kontrast mellan inre och yttre kanter.

Det slutliga resultatet

Slutligen lägger vi till några prettifying inslag av skiktning lite mer subtila radiell övertoning höjdpunkter under de två halvorna. Detta var det som tog mig mest eftersom det innebar att de minsta logik och den mest mycket trial and error. Vid denna punkt, jag bara skiktad den ursprungliga bilden nedanför .∞ element, gjorde två halvor semi-transparent och började lägga till toningar och pyssla med dem tills de ganska mycket matchas höjdpunkterna. Och du kan se när jag blev sjuk av det eftersom det är då den position värden blivit sämre approximationer med färre decimaler.

En annan cool touch skulle vara skuggor på det hela med hjälp av ett filter på kroppen. Tyvärr, detta bryter 3D-korsningen effekt i Firefox, vilket innebär att vi inte kan lägga till det, alltför.

@stöder inte (-moz-transform: skala(2)) {
filter: drop-shadow(.25em .25em .25em #000)
drop-shadow(.25em .25em .5em #000);
}

Vi har nu den slutliga statiska resultat!

Se Pennan genom att thebabydino (@thebabydino) på CodePen.

Krydda upp det med animation!

När jag först delade denna demo, fick jag frågade om animera den. Jag trodde först att detta skulle vara komplicerat, men då slog det mig att, tack vare Houdini, det behöver inte vara!

Som jag nämnde i min tidigare artikel, vi kan animera i mellan hållplatser, låt oss säga från en röd och en blå. I vårt fall, mättnad och ljushet komponenter i hsl () – värden som används för att generera rainbow lutning vistelse konstant, alla förändringar är hue.

För varje stopp, nyans går från sitt ursprungliga värde till dess ursprungliga värde plus 360, därmed passerar genom hela nyans skala i processen. Detta är ekvivalent med att hålla den ursprungliga färgtonen konstant och varierande längden. Denna offset-off är den anpassade egenskapen vi animera.

Tyvärr innebär detta stöd är begränsad till Blink webbläsare med Experimentell Plattform för Webben har flaggan aktiverad.

Experimentell Plattform för Webben funktioner flagga som aktiveras i Chrome.

Fortfarande, låt oss se hur vi lägger allt i koden!

Till att börja med, vi ändrar få-stopp () – funktionen så att den nuvarande nyans som helst är den ursprungliga nyans av den aktuella stop $hue-curr plus våra offset-off:

$lista: $lista, hsl(calc(#{$hue-curr} + var (- off, 0)), 85%, 57%);

Nästa, vi registrera detta egna fastigheten:

CSS.registerProperty({
namn: ‘–off”,
syntax: ‘<nummer>’,
initialValue: 0;
})

Och slutligen, vi animera det till 360:

.∞ {
/* samma som innan */

&innan, &efter {
/* samma som innan */
animation: shift 2 linjär oändlig;
}
}

@keyframes skift {{ –off: 360 } }

Detta ger oss vår animerade lutning oändlighet!

Animerade ∞ logotyp (demo, Blinkar bara med flaggan aktiverad).

Det är det! Jag hoppas att du har haft denna dyk in i vad som kan göras med CSS dessa dagar!

Jetpack WordPress plugin som körs på denna webbplats, driver inte bara relaterade inlägg nedan, men säkerhet och backup, Wiki-stöd, sök på sajten, kommentera form, sociala nätverk, och mycket mer!