Xler8

Un fantastico effetto al passaggio del mouse per il tuo avatar

Hai presente quel tipo di effetto in cui la testa di qualcuno fa capolino attraverso un cerchio o un buco? La famosa animazione di Porky Pig in cui saluta mentre salta fuori da una serie di anelli rossi è l'esempio perfetto, e Kilian Valkhof in realtà l'ha ricreato qui su CSS-Tricks qualche tempo fa.

Ho un'idea simile ma affrontata in modo diverso e con una spolverata di animazione. Penso che sia abbastanza pratico e crei un effetto al passaggio del mouse pulito che puoi usare su qualcosa come il tuo avatar.

Guarda quello? Realizzeremo un'animazione in scala in cui l'avatar sembra uscire dal cerchio in cui si trova. Fantastico, vero? Non guardare il codice e costruiamo insieme questa animazione passo dopo passo.

L'HTML: solo un elemento

Se non hai controllato il codice della demo e ti stai chiedendo quanti divs questo ci vorrà, quindi fermati qui, perché il nostro markup non è altro che un singolo elemento immagine:

<img src="" alt="">

Sì, un singolo elemento! La parte impegnativa di questo esercizio consiste nell'utilizzare la minor quantità di codice possibile. Se sei stato seguendomi per un po' dovresti esserci abituato. Mi sforzo di trovare soluzioni CSS che possano essere ottenute con il codice più piccolo e gestibile possibile.

Ho scritto una serie di articoli qui su CSS-Tricks dove esploro diversi effetti al passaggio del mouse utilizzando lo stesso markup HTML contenente un singolo elemento. Vado nei dettagli su sfumature, mascherature, ritagli, contorni e persino tecniche di layout. Consiglio vivamente di verificarli perché riutilizzerò molti dei trucchi in questo post.

Un file immagine quadrato con uno sfondo trasparente funzionerà meglio per quello che stiamo facendo. Ecco quello che sto usando se vuoi iniziare con quello.

Progettato da cang

Spero di vedere molti esempi di questo possibile utilizzando immagini reali, quindi per favore condividi il tuo risultato finale nei commenti quando hai finito in modo che possiamo creare una raccolta!

Prima di passare ai CSS, analizziamo prima l'effetto. L'immagine diventa più grande al passaggio del mouse, quindi la useremo sicuramente transform: scale() lì dentro. C'è un cerchio dietro l'avatar e un gradiente radiale dovrebbe bastare. Infine, abbiamo bisogno di un modo per creare un bordo nella parte inferiore del cerchio che crei l'aspetto dell'avatar dietro il cerchio.

Andiamo a lavorare!

L'effetto scala

Iniziamo aggiungendo la trasformazione:

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

Niente di complicato ancora, giusto? Andiamo avanti.

Il cerchio

Abbiamo detto che lo sfondo sarebbe stato un gradiente radiale. È perfetto perché possiamo creare interruzioni rigide tra i colori di un gradiente radiale, che fanno sembrare che stiamo disegnando un cerchio con linee continue.

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

Nota la variabile CSS, --b, sto usando lì. Rappresenta lo spessore del "bordo" che in realtà viene utilizzato solo per definire le interruzioni di colore per la parte rossa del gradiente radiale.

Il prossimo passo è giocare con la dimensione del gradiente al passaggio del mouse. Il cerchio deve mantenere le sue dimensioni man mano che l'immagine cresce. Poiché stiamo applicando a scale() trasformazione, in realtà ne abbiamo bisogno diminuire la dimensione del cerchio perché altrimenti si ingrandisce con l'avatar. Quindi, mentre l'immagine si ingrandisce, abbiamo bisogno del gradiente per rimpicciolire.

Iniziamo definendo una variabile CSS, --f, che definisce il "fattore di scala", e usalo per impostare la dimensione del cerchio. sto usando 1 come valore predefinito, poiché in quella è la scala iniziale per l'immagine e il cerchio da cui trasformiamo.

Ecco una demo per illustrare il trucco. Passa il mouse per vedere cosa sta succedendo dietro le quinte:

Ho aggiunto un terzo colore al radial-gradient per identificare meglio l'area del gradiente al passaggio del mouse:

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

Ora dobbiamo posizionare il nostro sfondo al centro del cerchio e assicurarci che occupi tutta l'altezza. Mi piace dichiarare tutto direttamente sul background proprietà abbreviata, quindi possiamo aggiungere il nostro posizionamento in background e assicurarci che non si ripeta aggiungendo quei valori subito dopo il radial-gradient():

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

Lo sfondo è posto al centro (50%), ha una larghezza pari a calc(100%/var(--f)), e ha un'altezza pari a 100%.

Niente scala quando --f è uguale a 1 - ancora una volta, la nostra scala iniziale. Nel frattempo, il gradiente occupa l'intera larghezza del contenitore. Quando aumentiamo --f, la dimensione dell'elemento aumenta, grazie a scale() transform — e la dimensione del gradiente diminuisce.

Ecco cosa otteniamo quando applichiamo tutto questo alla nostra demo:

Ci stiamo avvicinando! Abbiamo l'effetto overflow in alto, ma abbiamo ancora bisogno di nascondere la parte inferiore dell'immagine, in modo che sembri uscire dal cerchio piuttosto che sedersi di fronte ad esso. Questa è la parte difficile di tutta questa faccenda ed è quello che faremo dopo.

Il bordo inferiore

Per prima cosa ho provato ad affrontare questo problema con il border-bottom proprietà, ma non sono riuscito a trovare un modo per abbinare la dimensione del bordo alla dimensione del cerchio. Ecco il meglio che ho potuto ottenere e puoi immediatamente vedere che è sbagliato:

La vera soluzione è usare il outline proprietà. SÌ, outlinenon, border. in un precedente articolo, mostro come outline è potente e ci consente di creare fantastici effetti al passaggio del mouse. Combinata con outline-offset, abbiamo esattamente ciò di cui abbiamo bisogno per il nostro effetto.

L'idea è di impostare un file outline sull'immagine e regolarne l'offset per creare il bordo inferiore. L'offset dipenderà dal fattore di scala allo stesso modo della dimensione del gradiente.

Ora abbiamo il nostro "confine" inferiore (in realtà an outline) combinato con il "bordo" creato dal gradiente per creare un cerchio completo. Abbiamo ancora bisogno di nascondere parti del file outline (dall'alto e dai lati), a cui arriveremo tra un attimo.

Ecco il nostro codice finora, incluse un paio di variabili CSS in più che puoi usare per configurare la dimensione dell'immagine (--s) e il colore del “bordo” (--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 */
}

Poiché abbiamo bisogno di un bordo inferiore circolare, abbiamo aggiunto a border-radius sul lato inferiore, consentendo il outline per adattarsi alla curvatura del gradiente.

Il calcolo utilizzato su outline-offset è molto più semplice di quanto sembri. Per impostazione predefinita, outline è disegnato al di fuori della scatola dell'elemento. E nel nostro caso, ne abbiamo bisogno sovrapposizione l'elemento. Più precisamente, ci serve per seguire il cerchio creato dal gradiente.

Diagramma della transizione dello sfondo.

Quando ridimensioniamo l'elemento, vediamo lo spazio tra il cerchio e il bordo. Non dimentichiamo che l'idea è di mantenere il cerchio alla stessa dimensione dopo l'esecuzione della trasformazione della scala, che ci lascia con lo spazio che useremo per definire l'offset del contorno come illustrato nella figura sopra.

Non dimentichiamo che il secondo elemento è ridimensionato, quindi anche il nostro risultato è ridimensionato... il che significa che dobbiamo dividere il risultato per f per ottenere il valore di offset reale:

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

Aggiungiamo un segno negativo poiché abbiamo bisogno che il contorno vada dall'esterno verso l'interno:

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

Ecco una rapida demo che mostra come il contorno segue il gradiente:

Potresti già vederlo, ma abbiamo ancora bisogno che il contorno inferiore si sovrapponga al cerchio piuttosto che lasciarlo sanguinare attraverso di esso. Possiamo farlo rimuovendo la dimensione del bordo dall'offset:

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

Ora dobbiamo trovare come rimuovere la parte superiore dal contorno. In altre parole, vogliamo solo la parte inferiore dell'immagine outline.

Innanzitutto, aggiungiamo spazio in alto con padding per evitare la sovrapposizione in alto:

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 */
}

Non c'è una logica particolare in quella imbottitura superiore. L'idea è di garantire che il contorno non tocchi la testa dell'avatar. Ho usato la dimensione dell'elemento per definire quello spazio per avere sempre la stessa proporzione.

Si noti che ho aggiunto il content-box valore al 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;

Ne abbiamo bisogno perché abbiamo aggiunto il padding e vogliamo solo che lo sfondo sia impostato sulla casella del contenuto, quindi dobbiamo dire esplicitamente allo sfondo di fermarsi lì.

Aggiunta maschera CSS al mix

Siamo arrivati ​​all'ultima parte! Tutto quello che dobbiamo fare è nascondere alcuni pezzi e abbiamo finito. Per questo ci affideremo al mask proprietà e, ovviamente, gradienti.

Ecco una figura per illustrare cosa dobbiamo nascondere o cosa dobbiamo mostrare per essere più precisi

Mostra come la maschera si applica alla parte inferiore del cerchio.

L'immagine a sinistra è ciò che abbiamo attualmente e quella a destra è ciò che vogliamo. La parte verde illustra la maschera che dobbiamo applicare all'immagine originale per ottenere il risultato finale.

Possiamo identificare due parti della nostra maschera:

  • Una parte circolare in basso che ha la stessa dimensione e curvatura del gradiente radiale che abbiamo usato per creare il cerchio dietro l'avatar
  • Un rettangolo nella parte superiore che copre l'area all'interno del contorno. Nota come il contorno è al di fuori dell'area verde in alto: questa è la parte più importante, in quanto consente di tagliare il contorno in modo che sia visibile solo la parte inferiore.

Ecco il nostro CSS finale:

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 */
}

Analizziamolo mask proprietà. Per cominciare, nota che un simile radial-gradient() dal background la proprietà è lì dentro. ho creato una nuova variabile, --_g, per le parti comuni per rendere le cose meno ingombranti.

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

Successivamente, c'è un linear-gradient() anche lì:

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

Questo crea la parte rettangolare della maschera. La sua larghezza è uguale alla larghezza del gradiente radiale meno il doppio dello spessore del bordo:

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

L'altezza del rettangolo è pari alla metà, 50%, della dimensione dell'elemento.

Abbiamo anche bisogno del gradiente lineare posto al centro orizzontale (50%) e offset dall'alto dello stesso valore dell'offset del contorno. Ho creato un'altra variabile CSS, --_o, per l'offset precedentemente definito:

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

Una delle cose confuse qui è che abbiamo bisogno di a negativo. offset per il contorno (per spostarlo dall'esterno verso l'interno) ma a positivo offset per il gradiente (per spostarsi dall'alto verso il basso). Quindi, se ti stai chiedendo perché moltiplichiamo l'offset, --_o, di -1, beh, ora lo sai!

Ecco una demo per illustrare la configurazione del gradiente della maschera:

Passa il mouse sopra e guarda come tutto si muove insieme. La casella centrale illustra il livello maschera composto da due gradienti. Immaginalo come la parte visibile dell'immagine a sinistra e ottieni il risultato finale sulla destra!

Concludendo

Uff, abbiamo finito! E non solo ci siamo ritrovati con una brillante animazione al passaggio del mouse, ma abbiamo fatto tutto con un singolo codice HTML <img> elemento. Solo questo e meno di 20 righe di trucco CSS!

Certo, ci siamo affidati ad alcuni piccoli trucchi e formule matematiche per raggiungere un effetto così complesso. Ma sapevamo esattamente cosa fare poiché abbiamo identificato in anticipo i pezzi di cui avevamo bisogno.

Avremmo potuto semplificare il CSS se ci fossimo concessi più HTML? Assolutamente. Ma siamo qui per imparare nuovi trucchi CSS! Questo è stato un buon esercizio per esplorare i gradienti CSS, il mascheramento, il outline comportamento della proprietà, trasformazioni e molto altro ancora. Se ti sei sentito perso in qualsiasi momento, allora dai un'occhiata la mia serie che utilizza gli stessi concetti generali. A volte aiuta vedere più esempi e casi d'uso per portare a casa un punto.

Vi lascio con un'ultima demo che utilizza foto di famosi sviluppatori CSS. Non dimenticare di mostrarmi una demo con la tua immagine in modo che io possa aggiungerla alla raccolta!

Parla con noi

Ciao! Come posso aiutarla?