Tecniche CSS per l'effetto increspatura del materiale

Una guida alle diverse tecniche per l'effetto a catena usando CSS e JavaScript

Di recente ho dovuto implementare l'effetto a catena dal design del materiale in un'applicazione web. E poi ho capito che non avevo idea di come fosse implementato. Questo mi ha portato in un viaggio per studiare le implementazioni esistenti e persino inventare una tecnica nuova di zecca che potrebbe esserti utile.

Cos'è questo effetto a catena?

Aspetta, non conosci l'effetto a catena del Material Design di Google? Vivi in ​​una grotta da quanti anni?

L'effetto a catena viene utilizzato quando si preme un pulsante. Funziona allo stesso modo per le interazioni del mouse o del tocco.

La posizione su cui si fa clic o si tocca il pulsante è denominata punto di contatto. Da lì, un'increspatura viene inviata spostandosi verso l'esterno, perdendo l'opacità man mano che si ingrandisce fino a riempire l'intero pulsante. Quindi scompare completamente.

Le dinamiche di questo effetto a catena sono simili alle increspature che si ottengono quando si tocca una superficie liquida o quando si rilascia una roccia in un lago.

Le increspature che troverai sul web

Dopo aver fatto qualche ricerca, sono riuscito a trovare due tecniche principali utilizzate per implementare l'effetto a catena sulle applicazioni web.

Utilizzo dello pseudo-elemento :: after

Usando questa tecnica, lo pseudo-elemento :: after del pulsante è disegnato come un cerchio semitrasparente e animato per crescere e sfumare. Il pulsante contenitore deve avere un overflow: nascosto in modo che il cerchio non trabocchi mai al di fuori della superficie del pulsante e posizione: relativo per facilitare il posizionamento del cerchio all'interno del pulsante. Puoi leggere maggiori dettagli di questa tecnica su questo articolo di Ionuț Colceriu.

Una delle grandi cose di questa tecnica è che si tratta di una soluzione CSS pura per l'effetto a catena. Tuttavia, l'effetto a catena inizia sempre dal centro del pulsante, anziché dal punto di contatto. Questo non è il feedback più naturale.

Potrebbe essere migliorato utilizzando JavaScript per memorizzare il punto di contatto e utilizzarlo per posizionare l'ondulazione. Questo è esattamente ciò che material.io ha fatto per il suo componente di ripple web. Utilizza le variabili CSS per memorizzare il punto di contatto e lo pseudo-elemento :: after utilizza queste variabili per il posizionamento.

Utilizzando elementi figlio

In sostanza, questa tecnica utilizza la stessa strategia di prima. Invece di uno pseudo-elemento, aggiunge un elemento span all'interno del pulsante, che può quindi essere posizionato tramite JavaScript. Questa tecnica è descritta in questo articolo da Jhey Tompkins.

L'implementazione più semplice crea un intervallo per ogni clic sul pulsante e utilizza la posizione del mouse sull'evento clic per modificare la posizione dell'intervallo. Un'animazione CSS fa crescere e svanire l'intervallo fino a diventare completamente trasparente. Possiamo scegliere di rimuovere l'intervallo dal DOM una volta terminata l'animazione, oppure lasciarlo lì sotto il tappeto: nessuno noterà davvero un intervallo trasparente in giro.

Ho trovato un'altra variante di questo, in cui l'elemento figlio è uno svg anziché uno span e lo svg è animato tramite JavaScript. Questa variazione è spiegata da Dennis Gaebel, ma in sostanza sembra essere la stessa, e forse permette di usare forme ed effetti SVG complessi.

Un problema con gli input di invio

Entrambe le tecniche sopra descritte sembrano grandi. Ma questo è ciò che accade quando ho provato ad applicarli su elementi di input con type = submit:

Perché non funzionano?

L'elemento di input è un elemento sostituito. In breve, ciò significa che c'è molto poco che puoi fare con quegli elementi, rispetto a DOM e CSS. In particolare, non possono avere elementi figlio e nemmeno pseudo-elementi. Ora è chiaro perché queste tecniche falliscono.

Quindi, se stai utilizzando Material Design, è meglio stare lontano dagli input [type = submit] e attenersi agli elementi dei pulsanti. O continua a leggere.

Aggiunta di increspature per inviare input

Nell'applicazione Web su cui stavo lavorando, avevamo già molti pulsanti di invio. Cambiarli tutti in modo che diventino un elemento diverso richiederebbe molto lavoro e un alto rischio di rottura dei fogli di stile e della logica JavaScript. Quindi ho dovuto capire come aggiungere increspature ai pulsanti di invio esistenti.

Utilizzando un contenitore da imballaggio

Mi sono reso rapidamente conto che avrei potuto avvolgere il pulsante di invio all'interno di un elemento di blocco inline e utilizzare l'elemento di blocco inline come superficie di ondulazione. Ecco una breve demo:

Sebbene questa soluzione mi piaccia per la sua semplicità, mi ha comunque richiesto di modificare il markup in troppi punti. E sapevo che sarebbe stata una soluzione fragile: i nuovi sviluppatori sarebbero entrati nel progetto e avrebbero creato pulsanti di invio senza avvolgerli correttamente in una superficie ondulata. Quindi ho continuato a cercare altre soluzioni che non richiedessero la modifica del DOM.

Gradienti radiali

La sintassi del gradiente radiale mi permette di controllare sia il centro che la dimensione del gradiente. Naturalmente, mi permette anche di controllare il colore del gradiente, compresi i colori semitrasparenti. E non trabocca mai mai l'elemento a cui è applicato. Quindi sembra che faccia già tutto ciò di cui ho bisogno!

Non così in fretta ... manca una cosa: la proprietà dell'immagine di sfondo non è animabile. Non sono riuscito a far crescere e sfumare il gradiente in trasparenza usando le animazioni CSS. Sono riuscito a farlo crescere animando la proprietà delle dimensioni dello sfondo, ma era tutto ciò che potevo fare.

Ho provato alcune altre cose, come avere un cerchio in dissolvenza come immagine animata (usando il formato apng) e applicarlo come immagine di sfondo. Ma poi non sono riuscito a controllare quando il ciclo di immagini è iniziato e terminato.

Finalmente una soluzione con JavaScript

Quello che non puoi fare nei CSS, puoi farlo in JavaScript. Dopo aver trascorso più tempo di quello che sono disposto ad ammettere cercando di far funzionare questo effetto usando le animazioni CSS, ho appena rinunciato e ho deciso di scrivere l'animazione in JavaScript.

Ho iniziato con la soluzione del gradiente radiale sopra e ho usato window.requestAnimationFrame per creare un'animazione fluida del gradiente radiale, in crescita e in dissolvenza. Ecco la mia soluzione finale:

Conclusione

Quindi è possibile avere effetti a catena sui pulsanti di invio, non solo con i CSS.

Non sono riuscito a trovare questa tecnica documentata in nessuna parte del Web, quindi la chiamo mia. La tecnica di ripple di Leonardo non richiede modifiche al DOM e funziona per qualsiasi elemento perché non si basa su pseudo-elementi o elementi figlio. Tuttavia, non è una soluzione impeccabile.

Innanzitutto, ci sono prestazioni. Animando il gradiente con JavaScript, perdi molte ottimizzazioni del browser. Ma, poiché l'unica proprietà che viene modificata è l'immagine di sfondo, sospetterei che i browser non avrebbero bisogno di essere ridisegnati e richiederei semplicemente di riapplicare gli stili e ridipingere l'elemento. In pratica, è esattamente quello che succede e le prestazioni sono davvero buone. L'eccezione a tale affermazione è Firefox Mobile, che per qualche motivo non tiene il passo con l'animazione. (modifica: l'animazione è fluida nelle moderne versioni di Firefox Mobile)

In secondo luogo, la tecnica utilizza la proprietà immagine di sfondo del pulsante. Se il tuo progetto richiede che i tuoi pulsanti abbiano un'immagine applicata al suo sfondo, l'effetto a catena avrebbe la precedenza. Se hai davvero bisogno di quell'immagine sul tuo disegno, allora JavaScript potrebbe essere modificato per disegnare il gradiente radiale sopra l'immagine di sfondo esistente.

Terzo, questo non sembra funzionare in Internet Explorer. Tuttavia, non vedo alcun motivo per cui non dovrebbe funzionare con IE10 e versioni successive. Forse è perché IE utilizza una sintassi diversa per il gradiente radiale. Ma a chi importa di IE al giorno d'oggi? (modifica: questo metodo funziona senza problemi su Internet Explorer 11)