Zephyrnet-logo

Een fancy zweefeffect voor je avatar

Datum:

Ken je dat soort effect waarbij iemands hoofd door een cirkel of gat steekt? De beroemde Porky Pig-animatie waarin hij gedag zwaait terwijl hij uit een reeks rode ringen springt, is het perfecte voorbeeld, en Kilian Valkhof heeft dat een tijdje geleden hier op CSS-Tricks opnieuw gemaakt.

Ik heb een soortgelijk idee, maar op een andere manier aangepakt en met een vleugje animatie. Ik denk dat het behoorlijk praktisch is en zorgt voor een leuk zweefeffect dat je kunt gebruiken op zoiets als je eigen avatar.

Zie dat? We gaan een schalingsanimatie maken waarbij de avatar recht uit de cirkel lijkt te springen waarin hij zich bevindt. Cool, toch? Kijk niet naar de code en laten we samen stap voor stap deze animatie bouwen.

De HTML: slechts één element

Als je de code van de demo niet hebt gecontroleerd en je je afvraagt ​​hoeveel divAls dit duurt, stop dan daar, want onze opmaak is niets anders dan een enkel afbeeldingselement:

<img src="" alt="">

Ja, een enkel element! Het uitdagende deel van deze oefening is het gebruik van de kleinst mogelijke hoeveelheid code. Als je bent geweest mij volgen voor een tijdje zou je hieraan gewend moeten zijn. Ik doe mijn best om CSS-oplossingen te vinden die kunnen worden bereikt met de kleinst mogelijke, meest onderhoudbare code.

Ik schreef een reeks artikelen hier op CSS-Tricks waar ik verschillende zweefeffecten verken met dezelfde HTML-opmaak die een enkel element bevat. Ik ga in detail in op verlopen, maskeren, knippen, contouren en zelfs lay-outtechnieken. Ik raad ten zeerste aan om die uit te proberen, want ik zal veel van de trucs in dit bericht opnieuw gebruiken.

Een afbeeldingsbestand dat vierkant is met een transparante achtergrond werkt het beste voor wat we doen. Hier is degene die ik gebruik als je daarmee wilt beginnen.

Designed by cang

Ik hoop hier zoveel mogelijk voorbeelden van te zien met behulp van echte afbeeldingen - dus deel alsjeblieft je eindresultaat in de reacties als je klaar bent, zodat we een verzameling kunnen bouwen!

Laten we, voordat we in CSS springen, eerst het effect ontleden. Het beeld wordt groter bij zweven, dus we zullen het zeker gebruiken transform: scale() daarin. Er is een cirkel achter de avatar en een radiaal verloop zou voldoende moeten zijn. Ten slotte hebben we een manier nodig om een ​​rand aan de onderkant van de cirkel te maken die ervoor zorgt dat de avatar achter de cirkel verschijnt.

Laten we aan de slag gaan!

Het schaaleffect

Laten we beginnen met het toevoegen van de transformatie:

img { width: 280px; aspect-ratio: 1; cursor: pointer; transition: .5s;
}
img:hover { transform: scale(1.35);
}

Nog niets ingewikkelds, toch? Laten we verder gaan.

De cirkel

We zeiden dat de achtergrond een radiaal verloop zou zijn. Dat is perfect omdat we harde stops kunnen maken tussen de kleuren van een radiaal verloop, waardoor het lijkt alsof we een cirkel tekenen met ononderbroken lijnen.

img { --b: 5px; /* border width */ width: 280px; aspect-ratio: 1; background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), #C02942 calc(100% - var(--b)) 99%, #0000 ); cursor: pointer; transition: .5s;
}
img:hover { transform: scale(1.35);
}

Let op de CSS-variabele, --b, ik gebruik daar. Het vertegenwoordigt de dikte van de "rand" die eigenlijk alleen maar wordt gebruikt om de harde kleurstops voor het rode deel van de radiale gradiënt te definiëren.

De volgende stap is om te spelen met de verloopgrootte bij zweven. De cirkel moet zijn grootte behouden naarmate de afbeelding groeit. Omdat we a scale() transformatie, dat moeten we echt doen verlagen de grootte van de cirkel omdat deze anders meeschaalt met de avatar. Dus terwijl de afbeelding omhoog schaalt, hebben we het verloop nodig om omlaag te schalen.

Laten we beginnen met het definiëren van een CSS-variabele, --f, die de "schaalfactor" definieert, en gebruik deze om de grootte van de cirkel in te stellen. ik gebruik 1 als de standaardwaarde, want dat is de beginschaal voor de afbeelding en de cirkel waaruit we transformeren.

Hier is een demo om de truc te illustreren. Hover om te zien wat er achter de schermen gebeurt:

Ik heb een derde kleur toegevoegd aan de radial-gradient om het gebied van het verloop tijdens het zweven beter te identificeren:

radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), #C02942 calc(100% - var(--b)) 99%, lightblue
);

Nu moeten we onze achtergrond in het midden van de cirkel plaatsen en ervoor zorgen dat deze de volledige hoogte inneemt. Ik geef graag alles direct op de background steno-eigenschap, zodat we onze achtergrondpositionering kunnen toevoegen en ervoor kunnen zorgen dat deze niet wordt herhaald door die waarden direct na de radial-gradient():

background: radial-gradient() 50% / calc(100% / var(--f)) 100% no-repeat;

De achtergrond wordt in het midden geplaatst (50%), heeft een breedte gelijk aan calc(100%/var(--f)), en heeft een hoogte gelijk aan 100%.

Niets schaalt wanneer --f is gelijk aan 1 - nogmaals, onze eerste schaal. Ondertussen beslaat het verloop de volledige breedte van de container. Wanneer we verhogen --f, groeit de grootte van het element - dankzij de scale() transformeren — en de grootte van het verloop neemt af.

Dit is wat we krijgen als we dit allemaal toepassen op onze demo:

We komen dichterbij! We hebben het overloopeffect bovenaan, maar we moeten nog steeds het onderste deel van de afbeelding verbergen, zodat het lijkt alsof het uit de cirkel springt in plaats van ervoor te zitten. Dat is het lastige van dit hele gebeuren en dat is wat we nu gaan doen.

De onderste rand

Ik heb dit eerst geprobeerd aan te pakken met de border-bottom eigenschap, maar ik kon geen manier vinden om de grootte van de rand af te stemmen op de grootte van de cirkel. Dit is het beste wat ik kon krijgen en je kunt meteen zien dat het fout is:

De eigenlijke oplossing is om de outline eigendom. Ja, outline, Niet border. in een vorig artikel, ik laat zien hoe outline is krachtig en stelt ons in staat om coole zweefeffecten te creëren. Gecombineerd met outline-offset, hebben we precies wat we nodig hebben voor ons effect.

Het idee is om een outline op de afbeelding en pas de verschuiving aan om de onderrand te maken. De offset hangt op dezelfde manier af van de schaalfactor als de grootte van het verloop.

Nu hebben we onze onderste "grens" (eigenlijk een outline) gecombineerd met de "rand" gecreëerd door het verloop om een ​​volledige cirkel te creëren. We moeten nog delen van de outline (van de bovenkant en de zijkanten), waar we zo op terug komen.

Hier is onze code tot nu toe, inclusief nog een paar CSS-variabelen die u kunt gebruiken om de afbeeldingsgrootte te configureren (--s) en de "rand" kleur (--c):

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ width: var(--s); aspect-ratio: 1; cursor: pointer; border-radius: 0 0 999px 999px; outline: var(--b) solid var(--c); outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b)); background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000 ) 50% / calc(100% / var(--f)) 100% no-repeat; transform: scale(var(--f)); transition: .5s;
}
img:hover { --f: 1.35; /* hover scale */
}

Omdat we een ronde onderrand nodig hebben, hebben we een border-radius aan de onderkant, waardoor de outline om overeen te komen met de kromming van het verloop.

De berekening gebruikt op outline-offset is een stuk overzichtelijker dan het lijkt. Standaard, outline is getekend buiten van de doos van het element. En in ons geval hebben we het nodig overlappen het element. Meer precies, we hebben het nodig om de cirkel te volgen die door het verloop is gecreëerd.

Diagram van de achtergrondovergang.

Wanneer we het element schalen, zien we de ruimte tussen de cirkel en de rand. Laten we niet vergeten dat het de bedoeling is om de cirkel op dezelfde grootte te houden nadat de schaaltransformatie is uitgevoerd, waardoor we de ruimte overhouden die we zullen gebruiken om de offset van de omtrek te definiëren, zoals geïllustreerd in de bovenstaande afbeelding.

Laten we niet vergeten dat het tweede element geschaald is, dus ons resultaat is ook geschaald... wat betekent dat we het resultaat moeten delen door f om de echte offsetwaarde te krijgen:

Offset = ((f - 1) * S/2) / f = (1 - 1/f) * S/2

We voegen een negatief teken toe omdat we de omtrek nodig hebben om van buiten naar binnen te gaan:

Offset = (1/f - 1) * S/2

Hier is een korte demo die laat zien hoe de omtrek het verloop volgt:

Je ziet het misschien al, maar we hebben nog steeds de onderste omtrek nodig om de cirkel te overlappen in plaats van er doorheen te laten bloeden. We kunnen dat doen door de grootte van de rand van de offset te verwijderen:

outline-offset: calc((1 / var(--f) - 1) * var(--s) / 2) - var(--b));

Nu moeten we uitzoeken hoe we het bovenste gedeelte van de omtrek kunnen verwijderen. Met andere woorden, we willen alleen het onderste deel van de afbeelding outline.

Laten we eerst ruimte aan de bovenkant toevoegen met opvulling om overlapping aan de bovenkant te voorkomen:

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ width: var(--s); aspect-ratio: 1; padding-block-start: calc(var(--s)/5); /* etc. */
}
img:hover { --f: 1.35; /* hover scale */
}

Er zit geen specifieke logica in die topvulling. Het idee is om ervoor te zorgen dat de omtrek het hoofd van de avatar niet raakt. Ik heb de grootte van het element gebruikt om die ruimte te definiëren om altijd dezelfde verhouding te hebben.

Merk op dat ik de heb toegevoegd content-box waarde aan de background:

background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000 ) 50%/calc(100%/var(--f)) 100% no-repeat content-box;

We hebben dit nodig omdat we opvulling hebben toegevoegd en we alleen de achtergrond willen instellen op het inhoudsvak, dus we moeten de achtergrond expliciet vertellen om daar te stoppen.

CSS-masker toevoegen aan de mix

We hebben het laatste stuk bereikt! Het enige wat we hoeven te doen is wat stukjes te verbergen, en we zijn klaar. Hiervoor zullen we vertrouwen op de mask eigenschap en, natuurlijk, gradiënten.

Hier is een figuur om te illustreren wat we moeten verbergen of wat we moeten laten zien om nauwkeuriger te zijn

Laat zien hoe het masker van toepassing is op het onderste gedeelte van de cirkel.

Het linkerbeeld is wat we momenteel hebben en het rechterbeeld is wat we willen. Het groene gedeelte illustreert het masker dat we op de originele afbeelding moeten aanbrengen om het uiteindelijke resultaat te krijgen.

We kunnen twee delen van ons masker onderscheiden:

  • Een cirkelvormig deel onderaan dat dezelfde afmeting en kromming heeft als de radiale gradiënt die we gebruikten om de cirkel achter de avatar te maken
  • Een rechthoek aan de bovenkant die het gebied binnen de omtrek bedekt. Merk op hoe de omtrek zich buiten het groene gebied aan de bovenkant bevindt - dat is het belangrijkste onderdeel, omdat de omtrek zo kan worden afgesneden dat alleen het onderste deel zichtbaar is.

Dit is onze uiteindelijke CSS:

img { --s: 280px; /* image size */ --b: 5px; /* border thickness */ --c: #C02942; /* border color */ --f: 1; /* initial scale */ --_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; --_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b)); width: var(--s); aspect-ratio: 1; padding-top: calc(var(--s)/5); cursor: pointer; border-radius: 0 0 999px 999px; outline: var(--b) solid var(--c); outline-offset: var(--_o); background: radial-gradient( circle closest-side, #ECD078 calc(99% - var(--b)), var(--c) calc(100% - var(--b)) 99%, #0000) var(--_g); mask: linear-gradient(#000 0 0) no-repeat 50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%, radial-gradient( circle closest-side, #000 99%, #0000) var(--_g); transform: scale(var(--f)); transition: .5s;
}
img:hover { --f: 1.35; /* hover scale */
}

Laten we dat opsplitsen mask eigendom. Merk om te beginnen op dat een vergelijkbaar radial-gradient() van het background het pand zit erin. Ik heb een nieuwe variabele gemaakt, --_g, voor de gemeenschappelijke delen om de zaken minder rommelig te maken.

--_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; mask: radial-gradient( circle closest-side, #000 99%, #0000) var(--_g);

Vervolgens is er een linear-gradient() daar ook in:

--_g: 50% / calc(100% / var(--f)) 100% no-repeat content-box; mask: linear-gradient(#000 0 0) no-repeat 50% calc(-1 * var(--_o)) / calc(100% / var(--f) - 2 * var(--b)) 50%, radial-gradient( circle closest-side, #000 99%, #0000) var(--_g);

Hierdoor ontstaat het rechthoekige deel van het masker. De breedte is gelijk aan de breedte van de radiale gradiënt min tweemaal de randdikte:

calc(100% / var(--f) - 2 * var(--b))

De hoogte van de rechthoek is gelijk aan de helft, 50%, van de grootte van het element.

We hebben ook de lineaire gradiënt nodig die in het horizontale midden is geplaatst (50%) en offset vanaf de bovenkant met dezelfde waarde als de offset van de omtrek. Ik heb een andere CSS-variabele gemaakt, --_o, voor de offset die we eerder hebben gedefinieerd:

--_o: calc((1 / var(--f) - 1) * var(--s) / 2 - var(--b));

Een van de verwarrende dingen hier is dat we een negatief offset voor de omtrek (om deze van buiten naar binnen te verplaatsen) maar a positief offset voor het verloop (om van boven naar beneden te gaan). Dus als je je afvraagt ​​waarom we de offset vermenigvuldigen, --_odoor -1, nou, nu weet je het!

Hier is een demo om de verloopconfiguratie van het masker te illustreren:

Beweeg over het bovenstaande en zie hoe alles samen beweegt. Het middelste vak illustreert de maskerlaag die is samengesteld uit twee verlopen. Stel je het voor als het zichtbare deel van de linkerafbeelding en je krijgt het eindresultaat aan de rechterkant!

Afsluiten

Oef, we zijn klaar! En we eindigden niet alleen met een gelikte hover-animatie, maar we deden het allemaal met een enkele HTML <img> element. Precies dat en minder dan 20 regels CSS-bedrog!

Natuurlijk vertrouwden we op een paar kleine trucjes en wiskundige formules om zo'n complex effect te bereiken. Maar we wisten precies wat we moesten doen, omdat we van tevoren de stukken hadden geïdentificeerd die we nodig hadden.

Hadden we de CSS kunnen vereenvoudigen als we onszelf meer HTML hadden toegestaan? Absoluut. Maar we zijn hier om nieuwe CSS-trucs te leren! Dit was een goede oefening om CSS-gradiënten, maskering, de outline het gedrag van onroerend goed, transformaties en nog veel meer. Als je je op enig moment verloren voelde, ga dan zeker eens kijken mijn serie die dezelfde algemene concepten gebruikt. Soms helpt het om meer voorbeelden te zien en cases te gebruiken om een ​​punt naar huis te trekken.

Ik laat je achter met een laatste demo die foto's van populaire CSS-ontwikkelaars gebruikt. Vergeet niet om me een demo met je eigen afbeelding te laten zien, zodat ik deze aan de collectie kan toevoegen!

spot_img

Laatste intelligentie

spot_img