close
close

CSS Olympische ringen | CSS-trucs

bevat 16 elementen die fungeren als de lagen, die ik inpak tags. Die vijf ringen stoppen we in een bovenliggende container om alles bij elkaar te houden. We geven de bovenliggende container een .rings klasse en elke ring, creatief, een .ring klas.

Dit is een verkorte versie van de HTML die laat zien hoe dat in elkaar zit:


Merk op --i aangepaste eigenschap die ik heb laten vallen op de style kenmerk van elk element:




We gaan gebruiken --i om de positie, grootte en kleur van elke laag te berekenen. Daarom heb ik hun waarden ingesteld als gehele getallen in oplopende volgorde — dat zijn vermenigvuldigers voor het ordenen en stylen van elke laag afzonderlijk.

Professionele tip: U kunt voorkomen dat u de HTML voor elke laag handmatig schrijft als u werkt met een IDE die Emmet ondersteunt. Maar als dat niet zo is, geen zorgen, want CodePen doet dat wel! Voer het volgende in uw HTML-editor in en druk op Tab -toets op uw toetsenbord om het uit te breiden naar 16 lagen: i*16(style="--i: $;")

De (vanille) CSS

Laten we beginnen met de ouder .rings container krijgt nu gewoon een relatieve positie. Zonder relatieve positionering zouden de ringen uit de documentstroom worden verwijderd en ergens van de pagina afrollen wanneer er absolute positionering op wordt ingesteld.

.rings {
  position: relative;
}

.ring {
  position: absolute;
}

Laten we hetzelfde doen met de elementen, maar gebruik CSS-nesting om de code compact te houden. We gooien er border-radius terwijl we bezig zijn, knippen we de hoekige randen bij, zodat er perfecte cirkels ontstaan.

.rings {
  position: relative;
}

.ring {
  position: absolute;
  
  i {
    position: absolute;
    border-radius: 50%;
  }
}

Het laatste stukje basisstyling dat we toepassen voordat we verder gaan, is een aangepaste eigenschap voor de --ringColor. Dit maakt het kleuren van de ringen vrij eenvoudig, omdat we het eenmalig kunnen schrijven en het dan laag voor laag kunnen overschrijven. We verklaren --ringColor op de border eigenschap omdat we alleen kleuring aan de buitenste randen van elke laag willen in plaats van ze volledig in te vullen met background-color:

.rings {
  position: relative;
}

.ring {
  position: absolute;
  --ringColor: #0085c7;
  
  i {
    position: absolute;
    inset: -100px;
    border: 16px var(--ringColor) solid;
    border-radius: 50%;
  }
}

Heb je gemerkt dat ik er nog iets anders in heb gestopt? Dat klopt, de inset eigenschap is er ook en ingesteld op een negatieve waarde van 100pxDat ziet er misschien een beetje vreemd uit, dus laten we daar eerst over praten terwijl we verdergaan met het stylen van ons werk.

Negatieve inzet

Een negatieve waarde instellen op de inset eigenschap betekent dat de positie van de laag daalt buiten de .ring element. Dus we zouden het eerder als een “begin” kunnen zien. In ons geval is de .ring heeft geen grootte omdat er geen inhoud of CSS-eigenschappen zijn om het dimensies te geven. Dat betekent dat de laag inset (of liever gezegd “begin”) is 100px in elke richting, wat resulteert in een .ring dat is 200×200 pixels.

Een transparant vierkant met een blauwe rand, getekend op de grafieklijnen, met pijlen erin die de offsets van de ringlaag aangeven en hoe deze de grootte van het ringelement beïnvloeden.

Laten we eens kijken wat we tot nu toe hebben:

Positionering voor diepte

We gebruiken de lagen om de indruk van diepte te creëren. Dat doen we door elk van de 16 lagen langs de z-as te plaatsen, die elementen van voor naar achter stapelt. We plaatsen elke laag slechts 2px uit elkaar — dat is alle ruimte die we nodig hebben om een ​​lichte visuele scheiding tussen de lagen te creëren, waardoor we de diepte krijgen die we willen.

Herinner de --i aangepaste eigenschap die we in de HTML hebben gebruikt?




Nogmaals, dit zijn vermenigvuldigers om ons te helpen translate elke laag langs de z-as. Laten we een nieuwe aangepaste eigenschap maken die de vergelijking definieert, zodat we deze op elke laag kunnen toepassen:

i {
  --translateZ: calc(var(--i) * 2px);
}

Waar passen we het op toe? We kunnen de CSS gebruiken transform eigenschap. Op deze manier kunnen we de lagen verticaal roteren (d.w.z. rotateY()) terwijl ze langs de z-as worden vertaald:

i {
  --translateZ: calc(var(--i) * 2px);

  transform: rotateY(-45deg) translateZ(var(--translateZ));
}

Kleur voor schaduw

Voor kleurschakering maken we de lagen donkerder volgens hun positie, zodat de lagen donkerder worden naarmate we van de voorkant van de z-as naar de achterkant gaan. Er zijn een paar manieren om dit te doen. Een daarvan is door een andere zwarte laag met afnemende dekking toe te voegen. Een andere is door het “lichtheids”-kanaal in een hsl() kleurfunctie waarbij de waarde aan de voorkant “lichter” is en naar achteren toe steeds donkerder. Een derde optie is spelen met de dekking van de laag, maar dat wordt rommelig.

Hoewel we die drie benaderingen hebben, denk ik dat de moderne CSS relatieve kleursyntaxis de beste manier is om te gaan. We hebben al een standaard gedefinieerd --ringColor aangepaste eigenschap. We kunnen het door de relatieve kleursyntaxis halen om het te manipuleren naar andere kleuren voor elke ring laag.

Ten eerste hebben we een nieuwe aangepaste eigenschap nodig die we kunnen gebruiken om een ​​”lichte” waarde te berekenen:

.ring {
  --ringColor: #0085c7;
  
  i {
    --light: calc(var(--i) / 16);

    border: 16px var(--ringColor) solid;
  }
}

We zullen de calc()-ulated resultaat in een andere aangepaste eigenschap die onze standaardwaarde zet --ringColor via de relatieve kleurensyntaxis waarbij de --light Met een aangepaste eigenschap kunt u de helderheid van de resulterende kleur aanpassen.

.ring {
  --ringColor: #0085c7;
  
  i {
    --light: calc(var(--i) / 16);
    --layerColor: rgb(from var(--ringColor) calc(r * var(--light)) calc(g * var(--light)) calc(b * var(--light)));

    border: 16px var(--ringColor) solid;
  }
}

Dat is nogal een vergelijking! Maar het ziet er alleen maar complex uit omdat de relatieve kleursyntaxis argumenten nodig heeft voor elk kanaal in de kleur (RGB) en we berekenen elk kanaal.

rgb(from origin-color channelR channelG channelB)

Voor zover het de berekeningen betreft, vermenigvuldigen we elk RGB-kanaal met de --light aangepaste eigenschap, wat een getal is tussen 0 En 1 divided door het aantal lagen, 16.

Tijd voor een nieuwe controle om te zien waar we staan:

Het creëren van de vorm

Om de vorm van een cirkelvormige ring te krijgen, stellen we de grootte van de laag (d.w.z. de dikte) in met de border eigendom. Dit is waar we trigonometrie in ons werk kunnen gaan gebruiken!

We willen dat de dikte van elke ring een waarde is tussen 0deg naar 180deg — aangezien we eigenlijk maar de helft van een cirkel maken — zullen we verdelen 180deg door het aantal lagen, 16wat neerkomt op 11.25deg. De … gebruiken sin() trigonometrische functie (die equivalent is aan de tegenovergestelde En hypotenusa zijden van een rechte hoek), krijgen we deze uitdrukking voor de laag --size:

--size: calc(sin(var(--i) * 11.25deg) * 16px);

Dus, wat dan ook --i staat in de HTML en fungeert als een vermenigvuldiger voor het berekenen van de laag border dikte. We hebben de grens van de laag als volgt gedeclareerd:

i {
  border: 16px var(--ringColor) solid;
)

Nu kunnen we de hard-gecodeerde 16px waarde met --size berekening:

i {
  --size: calc(sin(var(--i) * 11.25deg) * 16px);

  border: var(--size) var(--layerColor) solid;
)

Maar! Zoals u wellicht heeft opgemerkt, veranderen we de grootte van de laag niet wanneer we de grootte ervan wijzigen. border breedte. Als gevolg hiervan verschijnt het ronde profiel alleen aan de binnenkant van de laag. Het belangrijkste hierbij is dat u begrijpt dat het instellen van de --size met de inset eigenschap, wat betekent dat het de eigenschappen van het element niet beïnvloedt box-sizingHet resultaat is zeker een 3D-ring, maar de meeste schaduwen zijn verborgen.

⚠️ Automatisch afspelen van media

We kunnen de schaduw tevoorschijn halen door een nieuwe te berekenen inset voor elke laag. Dat is ongeveer wat ik deed in de 2020-versie, maar ik denk dat ik een makkelijkere manier heb gevonden: voeg een outline met hetzelfde border waarden om de boog aan de buitenkant van de ring te voltooien.

i {
  --size: calc(sin(var(--i) * 11.25deg) * 16px);

  border: var(--size) var(--layerColor) solid;
  outline: var(--size) var(--layerColor) solid;
}

We hebben nu een ring die er natuurlijker uitziet, nu we een outline:

De ringen animeren

Ik moest de ring in die laatste demo animeren om de schaduw van de ring voor en na te vergelijken. We gebruiken dezelfde animatie in de laatste demo, dus laten we eens kijken hoe ik dat heb gedaan voordat we de andere vier ringen aan de HTML toevoegen

Ik probeer niets bijzonders te doen; ik stel alleen de rotatie op de y-as in vanaf -45deg naar 45deg (de translateZ waarde blijft constant).

@keyframes ring {
  from { transform: rotateY(-45deg) translateZ(var(--translateZ, 0)); }
  to { transform: rotateY(45deg) translateZ(var(--translateZ, 0)); }
}

Wat betreft de animation eigendom, ik heb het een naam gegeven ring en een hard-gecodeerde (althans voor nu) duur van 3sdie oneindig doorloopt. De timingfunctie van de animatie instellen met ease-in-out En alternaterespectievelijk, geeft ons een vloeiende heen-en-weer beweging.

i {
  animation: ring 3s infinite ease-in-out alternate;
}

Zo werkt de animatie!

Meer ringen toevoegen

Nu kunnen we de resterende vier ringen toevoegen aan de HTML. Vergeet niet dat we in totaal vijf ringen hebben en dat elke ring 16 ringen bevat. lagen. Het zou er zo simpel uit kunnen zien:

There’s something elegant about the simplicity of this markup. And we could use the CSS nth-child() pseudo-selector om ze individueel te selecteren. Ik vind het fijner om iets declaratiever te zijn dan dat en ik ga elk .ring en een extra klasse die we kunnen gebruiken om expliciet een bepaalde ring te selecteren.

Our task now is to adjust each ring individually. Right now, everything looks like the first ring we made together. We’ll use the unique classes we just set in the HTML to give them their own color, position, and animation duration.

The good news? We’ve been using custom properties this entire time! All we have to do is update the values in each ring’s unique class.

.ring {
  &.ring__1 { --ringColor: #0081c8; --duration: 3.2s; --translate: -240px, -40px; }
  &.ring__2 { --ringColor: #fcb131; --duration: 2.6s; --translate: -120px, 40px; }
  &.ring__3 { --ringColor: #444444; --duration: 3.0s; --translate: 0, -40px; }
  &.ring__4 { --ringColor: #00a651; --duration: 3.4s; --translate: 120px, 40px; }
  &.ring__5 { --ringColor: #ee334e; --duration: 2.8s; --translate: 240px, -40px; }
}

Als je je afvraagt ​​waar die --ringColor waarden vandaan kwamen, baseerde ik ze op de gedocumenteerde kleuren van het Internationaal Olympisch Comité. Elke --duration is enigszins ten opzichte van elkaar verschoven om de beweging tussen de ringen te laten verlopen, en de ringen zijn --translate'D 120px uit elkaar en vervolgens verticaal verschoven door hun positie af te wisselen 40px En -40px.

Laten we de vertaalkwestie toepassen op de .ring elementen:

.ring {
  transform: translate(var(--translate));
}

Eerder hebben we de duur van de animatie vastgelegd op drie seconden:

i {
  animation: ring 3s infinite ease-in-out alternate;
}

Dit is het moment om dit te vervangen door een aangepaste eigenschap die de duur voor elke ring afzonderlijk berekent.

i {
  animation: ring var(--duration) -10s infinite ease-in-out alternate;
}

Whoa, whoa! Wat is de -10s waarde die je daar doet? Ook al is elke ringlaag ingesteld om te animeren voor een andere duur, de starthoek van de animaties is allemaal hetzelfde. Door een constante negatieve vertraging toe te voegen aan veranderende duur, zorg je ervoor dat de animatie van elke ring begint in een andere hoek.

Nu hebben we iets dat is bijna afgerond:

Nog wat laatste handjes

We zijn bij de laatste loodjes! De animatie ziet er al geweldig uit, maar ik wil er nog twee dingen aan toevoegen. De eerste is een kleine-10deg “kanteling” op de x-as van de ouder .rings container. Hierdoor lijkt het alsof we dingen vanuit een hoger perspectief bekijken.

.rings {
  rotate: x -10deg;
}

De tweede finishing touch heeft te maken met schaduwen. We kunnen de 3D-diepte van ons werk echt accentueren en het enige dat nodig is, is het selecteren van de .ring element's ::after pseudo-element en styling ervan leuk vinden een schaduw.

Eerst stellen we de breedte van de rand en de omtrek van de pseudo's in op een constante (24px) terwijl de kleur wordt ingesteld op een semi-transparant zwart (#0003). Dan zullen we translate zodat ze verder weg lijken te zijn. We zullen ook inset zodat ze op één lijn liggen met de werkelijke ringen. Eigenlijk verschuiven we de pseudo-elementen ten opzichte van het werkelijke element.

.ring {
  /* etc. */

  &::after {
    content: '';
    position: absolute;
    inset: -100px;
    border: 24px #0003 solid;
    outline: 24px #0003 solid;
    translate: 0 -100px -400px;
  }
}

De pseudo's zien er op dit moment niet erg schaduwrijk uit. Maar dat zullen ze wel worden als we blur() hen een beetje:

.ring {
  /* etc. */

  &::after {
    content: '';
    position: absolute;
    inset: -100px;
    border: 24px #0003 solid;
    outline: 24px #0003 solid;
    translate: 0 -100px -400px;
    filter: blur(12px);
  }
}

De schaduwen zijn ook behoorlijk boxy. Laten we ervoor zorgen dat ze rond zijn, net als de ringen:

.ring {
  /* etc. */

  &::after {
    content: '';
    position: absolute;
    inset: -100px;
    border: 24px #0003 solid;
    outline: 24px #0003 solid;
    translate: 0 -100px -400px;
    filter: blur(12px);
    border-radius: 50%;
  }
}

Oh, en we moeten dezelfde animatie op de pseudo instellen, zodat de schaduwen in harmonie met de ringen bewegen:

.ring {
  /* etc. */

  &::after {
    content: '';
    position: absolute;
    inset: -100px;
    border: 24px #0003 solid;
    outline: 24px #0003 solid;
    translate: 0 -100px -400px;
    filter: blur(12px);
    border-radius: 50%;
    animation: ring var(--duration) -10s infinite ease-in-out alternate;
  }
}

Laatste demo

Laten we even stilstaan ​​en ons voltooide werk bewonderen:

Uiteindelijk ben ik erg blij met de versie van de Olympische ringen van 2024. De versie van 2020 deed wat het moest doen en was waarschijnlijk de juiste aanpak voor die tijd. Maar met alle functies die we tegenwoordig in moderne CSS krijgen, had ik genoeg mogelijkheden om de code te verbeteren, zodat deze niet alleen efficiënter is, maar ook herbruikbaarder. Dit kan bijvoorbeeld in een ander project worden gebruikt en 'gethematiseerd' door simpelweg de --ringColor aangepaste eigenschap.

Uiteindelijk bewees deze oefening mij de kracht en flexibiliteit van moderne CSS. We namen een bestaand idee met complexiteit en creëerden het opnieuw met eenvoud en elegantie.

Copyright © 2023 Summer Blog. All Rights Reserved.