1 Element CSS Regenboog Verloop Infinity

0
11

Ik kreeg het eerste idee tot en CSS iets van de soort toen ik dit zag verloop infinity logo door Infographic Paradijs:

De oorspronkelijke verloop oneindigheid.

Na vier uur en twintig minuten, waarvan meer dan vier uren werden besteed aan het tweaken van positionering, randen en hoogtepunten… had ik eindelijk het resultaat hieronder:

Mijn versie van de regenboog verloop oneindigheid.

Het verloop lijkt niet op in de originele illustratie, als ik koos voor het genereren van de regenboog logisch in plaats van het gebruik van de Dev Tools picker of iets dergelijks, maar anders dan dat, ik denk dat ik aardig in de buurt, laat zien hoe ik dat deed!

Markup

Dat heb je waarschijnlijk al kan raden uit de titel, de HTML is slechts één element:

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

Styling

De beslissing over de aanpak

Het eerste idee dat het zou kunnen opkomen bij het zien van het bovenstaande worden met behulp van kegelvormig verlopen als grens beelden. Helaas, border-image en border-radius niet goed samen te spelen, zoals wordt geïllustreerd door de interactieve demo hieronder:

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Wanneer we een grens-beeld, border-radius wordt gewoon genegeerd, zodat met behulp van de twee samen is helaas geen optie.

Dus de benadering hier is het gebruik van conic-gradient() achtergronden en vervolgens het wegwerken van de deel in het midden met behulp van een masker. Laten we eens kijken hoe dat werkt!

Het maken van de twee ∞ helften

We hebben eerst beslissen op een buitendiameter.

$doen: 12.5 em;

We maken de twee helften van het infinity symbool met de ::before en ::after pseudo-elementen van ons .∞ element. Om deze twee pseudo-elementen naast elkaar, we maken gebruik van een flex-lay-out op hun ouders (de oneindigheid element .∞). Elk van deze heeft zowel de breedte en de hoogte gelijk aan de buitendiameter $doen. We hebben ook ronde hen met een border-radius van 50% en geven we ze een dummy achtergrond, zodat we ze kunnen zien.

.∞ {
display: flex;

en:before en:after {
breedte: $doen; height: $doen;
border-radius: 50%;
background: #000;
inhoud: “;
}
}

We hebben ook geplaatst .∞ element in het midden van de ouder (het lichaam in dit geval) zowel verticaal en horizontaal door het gebruik van de flexbox aanpak.

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Hoe conic-gradient() werkt

Om de conische-gradient() achtergronden voor de twee haves, moeten we eerst begrijpen hoe de kegelsnede-gradient() functie werkt.

Als u binnen de kegelsnede-gradient() functie hebben we een lijst van de haltes, zonder expliciete positie, vervolgens de eerste is genomen te worden op 0% (of 0deg, dezelfde zaak), de laatste is op 100% (of 360deg), terwijl al die links zijn verdeeld in de [0%, 100%] interval.

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Als we slechts 2 haltes, het is simpel. De eerste is 0%, de tweede (en laatste) op 100% en er zijn geen andere tussenstops.

Als we 3 stops, de eerste is 0%, de laatste (derde) op 100%, terwijl het tweede is dat van de dood in het midden van de [0%, 100%] interval, op 50%.

Als we de 4-stops, de eerste is op 0%, de laatste (vierde) op 100%, terwijl de tweede en derde split [0%, 100%] interval in 3 gelijke intervallen, wordt geplaatst op 33.(3)% en 66.(6)% respectievelijk.

Als we 5 stops, de eerste is 0%, de laatste (vijfde) op 100%, terwijl de tweede, derde en vierde splitsing van de [0%, 100%] interval in 4 gelijke intervallen worden geplaatst op 25%, 50% en 75% respectievelijk.

Als we 6 haltes met de metro, de eerste is 0%, de laatste (zesde) op 100%, terwijl de tweede, derde, vierde en vijfde splitsing van de [0%, 100%] interval in 5 gelijke intervallen wordt geplaatst op 20%, 40%, 60% en 80% respectievelijk.

In het algemeen, als we n stops, de eerste is 0%, de laatste op 100%, terwijl die tussen split [0%, 100%] interval in n-1 eqial tijdstippen verspreid over 100%/(n-1) elk. Als we geven de aanslagen gebaseerd op 0-indices, dan is ieder van hen is geplaatst op i*100%/(n-1).

Voor de eerste, i 0, 0*100%/(n-1) = 0%.

Voor de laatste (n-e), i is n-1, (n-1)*100%/(n-1) = 100%.

Hier kiezen we gebruik van 9 stil, dus we splitsen de [0%, 100%] interval in 8 gelijke intervallen.

Oke, maar hoe krijgen we het stop-lijst?

De hsl() stopt

Goed, voor de eenvoud, wij kiezen voor het genereren van het als een lijst van HSL-waarden. We houden de verzadiging en helderheid vaste en we verschillen van de kleur. De hue is een waarde voor de hoek die varieert van 0 tot 360, zoals we hier kunnen zien:

Visuele weergave van de hue schaal van 0 tot 360 (verzadiging en helderheid wordt constant gehouden).

Met dit in het achterhoofd kunnen we de bouw van een lijst van hsl() stopt met vaste verzadiging en helderheid en verschillende tint als we weten dat de start tint $hue-start, de tint bereik $hue-bereik (dit is het einde tint minus de start tint) en het aantal stops $num-stops.

Laten we zeggen dat we houden de verzadiging en de helderheid is vastgesteld op 85% en 57%, respectievelijk (willekeurige waarden die kunnen waarschijnlijk worden getweaked voor betere resultaten) en, bijvoorbeeld, kunnen we gaan uit van een start tint van 240 tot een einde tint van 300 en gebruik maken van 4 stops.

Om deze lijst van de haltes, we maken gebruik van een voor-stops () – functie neemt deze drie dingen als argumenten:

@functie-stops($hue-start, $hue-bereik, $num-stops) {}

We maken de lijst van de haltes $lijst die oorspronkelijk leeg (en dat zullen we terug op het einde nadat we vullen). We berekenen ook de tijdsspanne van één van de gelijke intervallen ons stopt splits de volledige begin tot eind interval in ($eenheid).

@functie-stops($hue-start, $hue-bereik, $num-stops) {
$lijst: ();
$eenheid: $hue-bereik/($num-stopt – 1);

/* het vullen van een lijst van haltes $lijst */

@return $lijst
}

In te vullen op onze $lijst, hebben wij een lus door het stopt, het berekenen van de huidige kleurtoon, het gebruik van de huidige tint voor het genereren van de hsl – ( – ) waarde om dat te stoppen en dan vervolgens toevoegen aan de lijst van haltes:

@voor $ik van 0 naar $num-stopt {
$hue-curr: $hue-start + $i*$eenheid;
$lijst: $lijst, hsl($hue-curr, 85%, 57%);
}

We kunnen nu gebruik maken van de stop-lijst retourneert deze functie voor elke vorm van verloop, als het kan worden gezien van de voorbeelden voor deze functie weergegeven in de interactieve demo hieronder (navigatie werkt zowel met behulp van de vorige/volgende knoppen aan de zijkanten als de pijltjestoetsen en de PgDn/ PgUp toetsen):

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Merk op hoe, wanneer ons assortiment passeert het ene uiteinde van de [0, 360] interval, het blijft van de andere kant. Bijvoorbeeld, wanneer de start-hue is 30 en het bereik is -210 (de vierde voorbeeld), kunnen we alleen maar naar beneden naar 0, dus dan blijven we op de afdaling van 360.

Conic verlopen voor onze twee helften

Oke, maar hoe bepalen we de $hue-start en de $hue-bereik voor onze specifieke geval?

In het oorspronkelijke beeld, we trekken een lijn tussen de centrale punten van de twee helften van de loop en, vanaf deze lijn, gaat de klok in beide gevallen zien we waar we beginnen en waar eindigen we in de [0, 360] hue-interval en wat andere tinten die we passeren.

We gaan uit van de verbindingslijn van de centrale punten van de twee helften en we gaan rond hen in de richting van de klok.

Om dingen te vereenvoudigen, wij beschouwen we passeren de hele [0, 360] hue schaal samen te gaan van onze infinity symbool. Dit betekent dat het bereik voor elk de helft is 180 (de helft van de 360) in absolute waarde.

Trefwoorden tint waarden correspondentie voor de verzadiging en de helderheid is vastgesteld op 100% en 50% respectievelijk.

Op de linker helft, we gaan uit van iets dat eruit ziet alsof het in tussen een soort van cyaan (hue 180) en een soort van kalk (hue 120), zodat we aan de start van de tint aan het gemiddelde van de kleuren van deze twee (180 + 120)/2 = 150.

Het plan voor de linker helft.

We krijgen een soort van rood, die is 180 afstand vanaf het begin, dus bij 330, of we trekken of toe te voegen 180:

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

Dus… we gaan omhoog of naar beneden? Goed, we gaan door geel, die zijn rond de 60 op de tint schaal, dus dat gaat af van 150, niet op. Naar beneden betekent ons assortiment is negatief (-180).

Het plan voor de rechter helft.

Op de rechter helft, beginnen we ook van dezelfde tint tussen cyaan en kalk (150) en we eindigen op dezelfde soort van rode (330), maar deze keer worden we door de blues, die circa 240, wat betekent dat we gaan voor de start tint van 150, dus ons assortiment is positief in dit geval (180).

Zo ver als het aantal stops gaat, 9 zou moeten volstaan.

Nu werken onze code met behulp van de waarden voor de linker helft van de standaardwaarden in voor onze functie:

@functie-stops($hue-start: 150, $hue-bereik: -180, $num-stops: 9) {
/* hetzelfde als voorheen */
}

.∞ {
display: flex;

en:before en:after {
/* hetzelfde als voorheen */
achtergrond: conic-gradient(get-stops());
}

en:after {
achtergrond: conic-gradient(get-stops(150, 180));
}
}

En nu onze twee discs hebben een kegelvormige-gradient() achtergronden:

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Echter, we willen niet dat deze kegelsnede verlopen aan de start van de top.

Voor de eerste schijf, willen we het om te beginnen van rechts—dat is bij 90° vanaf de bovenkant in de richting van de klok (positieve) richting. Voor de tweede schijf, willen we het om te beginnen van links—die op 90° van de top in de andere (negatieve) richting, wat overeenkomt met 270° vanaf de bovenkant in de richting van de klok (omdat negatieve hoeken blijken niet te werken vanuit een of andere reden).

Hoekige verschuivingen in de top van onze twee helften.

Laten we wijzigen onze code om dit te bereiken:

.∞ {
display: flex;

en:before en:after {
/* hetzelfde als voorheen */
achtergrond: conic-gradient(van 90deg, krijgen-stops());
}

en:after {
achtergrond: conic-gradient(van 270deg, krijgen-stops(150, 180));
}
}

So far, So good!

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Van 🥧 te 🍩

De volgende stap is om te snijden gaten van onze twee helften. Dit doen We met een masker of, meer precies, met een radiaal verloop (). Dit snijdt Edge ondersteuning voor nu, maar aangezien het iets is wat in ontwikkeling, het gaat waarschijnlijk om een cross-browser oplossing op enig moment in de niet al te verre toekomst.

Vergeet niet dat CSS gradient maskers zijn alfa maskers standaard (en alleen Firefox momenteel kunt u dit via masker-modus), wat betekent dat alleen de alpha-kanaal zaken. Een overlaging van het masker over ons element maakt elke pixel van dit element het alpha kanaal van de overeenkomstige pixel van het masker. Als het masker pixel is volledig transparant (alpha-waarde is 0), dan zullen de overeenkomstige pixel van het element.

Zie de Pen door thebabydino (@thebabydino) op CodePen.

In orde maken van het masker, berekenen we de straal van de buitenste $ro (de helft van de buitendiameter $) en de binnenste straal $ri (een fractie van de straal van de buitenste $ro).

$ro: .5*$doen;
$ri: .52*$ro;
$m: radiaal verloop(transparant $ri, rood 0);

Dan stellen We het masker op onze twee helften:

.∞ {
/* hetzelfde als voorheen */

en:before en:after {
/* hetzelfde als voorheen */
masker: $m;
}
}

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Dit ziet er perfect uit in Firefox, maar aan de randen van de radiale verlopen met abrupte overgangen van de ene halte naar de andere lelijk kijken in Chrome en, bijgevolg, zo doen de binnenste randen van onze ringen.

Close-up van de binnenste rand van de rechter helft in Chrome.

De correctie zou hier niet om een abrupte overgang tussen de haltes, maar verspreid over een kleine afstand, laten we zeggen een halve pixel:

$m: radiaal verloop(transparant calc(#{$ri} – .5px), rood $ri);

Dat We nu verlost van de gekartelde randen in Chrome:

Close-up van de binnenste rand van de rechter helft in Chrome na het verspreiden van de overgang tussen de haltes meer dan de helft van een pixel.

De volgende stap is om te compenseren voor de twee helften zo dat ze daadwerkelijk een infinity symbool. De zichtbare ronde strips beide hebben dezelfde breedte, het verschil tussen de straal van de buitenste $ro en de binnenste straal $ri. Dit betekent dat we een verandering nodig in elke lateraal door de helft van dit verschil $ri – $ri.

.∞ {
/* hetzelfde als voorheen */

en:before en:after {
/* hetzelfde als voorheen */
margin: 0 (-.5*($ro – $ri));
}
}

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Kruisende helften

We komen dichter en dichter bij, maar we hebben nog steeds een heel groot probleem hier. We willen niet het recht een deel van de lus volledig over de linker. In plaats daarvan willen we de bovenste helft van het juiste onderdeel te zijn dan dat van het linker deel en in de onderste helft van het linker gedeelte te boven dat van het rechter gedeelte.

Dus hoe doen we dat?

We nemen een soortgelijke aanpak gepresenteerd in een ouder artikel: met behulp van 3D!

Om beter te begrijpen hoe dit werkt, overweeg dan de twee-kaart voorbeeld hieronder. Wanneer we roteren rond de x-as, ze zijn niet in het vlak van het scherm niet meer. Een positieve omwenteling brengt de onderzijde naar voren en duwt de top terug. Een negatieve rotatie brengt de bovenrand naar voren en duwt de bodem terug.

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Merk op dat de demo hierboven niet werkt in de Rand.

Dus als wij geven, de linker een positieve omwenteling en het recht van een negatieve rotatie, dan is de bovenste helft van de rechter verschijnt in de voorkant van de bovenste helft van het verlaten van het ene en de andere manier rond voor de onderste helften.

Addiing perspectief maakt het wat dichter bij onze ogen lijkt groter en wat verder weg lijkt kleiner en we gebruiken kleine hoeken. Zonder dat we het 3D-vlak kruising zonder de 3D-weergave.

Merk op dat onze beide helften moeten worden in hetzelfde 3D-context, iets dat wordt bereikt door het instellen van transformatie-stijl: behouden-met 3d op de .∞ element.

.∞ {
/* hetzelfde als voorheen */
transform-stijl: behouden-3d;

en:before en:after {
/* hetzelfde als voorheen */
transformeren: rotatex(1deg);
}

en:after {
/* hetzelfde als voorheen */
transformeren: rotatex(-1deg);
}
}

En nu zijn we er bijna, maar niet helemaal:

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Fine tuning

We hebben een beetje rode strook in het midden, omdat het verloop eindigt en de snijlijn niet helemaal overeen:

Close-up van een klein probleem op de kruising van de twee helften.

Een vrij lelijk, maar efficiënte oplossing is voor het toevoegen van een 1px voordat u de vertaling van de rotatie van het rechtse deel (de ::after pseudo-element):

.∞:na { transform: vertalen(1) rotatex(-1deg) }

Veel beter!

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Dit is nog steeds niet perfect maar. Sinds de binnenste randen van onze twee ringen zijn een beetje wazig, de overgang tussen hen en de scherpe buitenste ziet er een beetje vreemd, dus misschien kunnen we het beter doen er:

Close-up van de continuïteit probleem (scherpe randen vergadering wazig innerlijke).

Een snelle oplossing hier toevoegen van een radiaal verloop() hebben betrekking op elk van de twee helften. Dit deksel is transparant wit voor de meeste van de ontmaskerd deel van de twee helften en gaat aan de effen witte die zowel hun binnenste en buitenste randen zodanig dat we hebben leuke continuïteit:

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

.∞ {
/* hetzelfde als voorheen */

en:before en:after {
/* hetzelfde als voorheen */
achtergrond: $gc, conisch-gradient(van 90deg, krijgen-stops());
}

en:after {
/* hetzelfde als voorheen */
achtergrond: $gc, conisch-gradient(van 270deg, krijgen-stops(150, 180));
}
}

Het voordeel wordt duidelijker wanneer we het toevoegen van een donkere achtergrond op het lichaam:

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Nu ziet het er nog beter bij het inzoomen:

Niet meer scherp contrast tussen de binnenste en buitenste randen.

Het uiteindelijke resultaat

Tot slot voegen we wat prettifying raakt door lagen wat meer subtiele radiaal verloop hoogtepunten over de twee helften. Dit was het deel dat mij het meest, omdat het betrokken de minste hoeveelheid van de logica en de meest bedrag van trial and error. Op dit moment heb ik net gelaagde de originele afbeelding onder de .∞ element, gemaakt van de twee helften van de semi-transparant en begon met het toevoegen van gradiënten en tweaken totdat ze vrij veel overeen met de hoogtepunten. En u kunt zien wanneer ik ziek van, want dat is wanneer de positie waarden ruwer benaderingen met minder decimalen.

Een andere coole touch in schaduwen op de hele zaak met behulp van een filter op het lichaam. Helaas, dit breekt de 3D-kruising effect in Firefox, wat betekent dat we niet kunnen toevoegen.

@ondersteunt niet (-moz-transform: schaal(2)) {
filter: drop-shadow – (.25em .25em .25em #000)
drop-shadow – (.25em .25em .5em #000);
}

We hebben nu de definitieve statische resultaat!

Zie de Pen door thebabydino (@thebabydino) op CodePen.

Kruiden met animatie!

Toen ik voor het eerst een gedeelde deze demo, ik werd gevraagd over het animeren. Ik in eerste instantie dacht dat dit zou moeilijk worden, maar toen drong het tot me dat, dankzij Houdini, het hoeft niet te worden!

Zoals vermeld in mijn vorige artikel, we kunnen animeren in tussen de haltes, laten we zeggen, van rood tot blauw. In ons geval, de verzadiging en helderheid onderdelen van de hsl – () – waarden, gebruikt voor het genereren van de regenboog verloop constant blijven, al die veranderingen is de tint.

Voor elke stoppen, de tint gaat van zijn initiële waarde te zijn initiële waarde plus 360, dus het passeren van de hele hue schaal in het proces. Dit is gelijk aan het houden van de oorspronkelijke tint constante en wisselende een verschuiving. Deze offset –off (uit) is de aangepaste eigenschap wij animeren.

Helaas, betekent dit dat de steun beperkt te Knipperen browsers met de Experimentele Web-Platform functies vlag ingeschakeld.

De Experimentele Web-Platform functies vlag ingeschakeld in Chrome.

Nog steeds, laten we zien hoe we dit in de code!

Voor starters, passen we de voor-stops() functie die de huidige tint op elk moment is de oorspronkelijke tint van de huidige stop $hue-curr plus onze offset – –uit:

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

Vervolgens registreren wij deze aangepaste eigenschap:

CSS.registerProperty({
naam: ‘–uit’,
syntaxis: ‘<aantal>’,
initialValue: 0;
})

En tot slot, we animeren 360:

.∞ {
/* hetzelfde als voorheen */

en:before en:after {
/* hetzelfde als voorheen */
animatie: shift 2 lineaire oneindig;
}
}

@keyframes shift {{ –uit: 360 } }

Dit geeft ons een geanimeerd verloop oneindigheid!

Geanimeerde ∞ logo (live demo, Knipperen alleen met vlag ingeschakeld).

Dat is het! Ik hoop dat je hebt genoten van deze duik in wat er gedaan kan worden met CSS van deze dagen!

De Jetpack WordPress plugin draait op deze site, het voeden niet alleen de gerelateerde berichten hieronder, maar de beveiliging en de back-ups, Markdown ondersteuning, site search, reactieformulier, sociale netwerk-verbindingen, en meer!