CSS teknikker for materiell ringvirkning

En guide til forskjellige teknikker for ringvirkningen ved hjelp av CSS og JavaScript

Jeg måtte nylig implementere ringvirkningen fra materialdesign til en webapplikasjon. Og så skjønte jeg at jeg ikke hadde noen anelse om hvordan det ble implementert. Dette førte meg inn på en reise for å studere eksisterende implementeringer, og til og med komme frem med en helt ny teknikk som kan være nyttig for deg.

Hva er denne ringvirkningen?

Vent, du vet ikke ringvirkningen fra Googles materialdesign? Har du bodd på en hule i hvor mange år?

Ringvirkningen brukes når du trykker på en knapp. Det fungerer på samme måte for mus- eller berøringsinteraksjoner.

Posisjonen du klikker eller berører på knappen kalles kontaktpunktet. Derfra sendes en krusning som beveger seg utover, og mister opaciteten etter hvert som den blir større til den fyller hele knappen. Da forsvinner den helt.

Dynamikken i denne ringvirkningen ligner på krusningene du får når du berører en væskeoverflate, eller når du slipper en stein i en innsjø.

Krusningene du finner på nettet

Etter å ha forsket litt, kunne jeg finne to hovedteknikker som brukes til å implementere ringvirkningen på webapplikasjoner.

Bruker :: etter pseudo-element

Ved hjelp av denne teknikken er :: etter pseudo-elementet i knappen stylet som en halvtransparent sirkel, og animert for å vokse og visne. Beholderknappen må ha overløp: skjult slik at sirkelen aldri flyter utenom knappens overflate og plassering: relativt for å gjøre det enkelt å plassere sirkelen i knappen. Du kan lese flere detaljer om denne teknikken på denne artikkelen av Ionuț Colceriu.

Noe av det gode med denne teknikken er at det er en ren CSS-løsning på ringvirkningen. Ringvirkningen starter imidlertid alltid fra midten av knappen, i stedet for kontaktpunktet. Det er ikke den mest naturlige tilbakemeldingen.

Det kan forbedres ved å bruke JavaScript for å lagre kontaktpunktet, og bruke det til å plassere krusningen. Det er akkurat det material.io har gjort for komponenten deres på nettet. Den bruker CSS-variabler for å lagre kontaktpunktet, og :: etter pseudo-elementet bruker disse variablene for posisjonering.

Bruke barnelementer

I hovedsak bruker denne teknikken den samme strategien som før. Men i stedet for et pseudo-element, legger det til et spennelement inne i knappen, som deretter kan plasseres gjennom JavaScript. Denne teknikken er beskrevet på denne artikkelen av Jhey Tompkins.

Den enkleste implementeringen skaper et spenn for hvert klikk på knappen, og bruker museposisjonen på klikkhendelsen til å endre posisjonen til spennet. En CSS-animasjon får spennet til å vokse og falme til det blir helt gjennomsiktig. Vi kan velge å fjerne spennet fra DOM når animasjonen er ferdig, eller bare la det ligge der under teppet - ingen vil virkelig merke at et gjennomsiktig spenn henger rundt.

Jeg fant en annen variant av dette, der barnelementet er en svg i stedet for et spenn, og svg er animert gjennom JavaScript. Denne variasjonen er forklart av Dennis Gaebel, men i hovedsak ser den ut til å være den samme, og kanskje tillate å bruke komplekse SVG-former og effekter.

Et problem med sende innganger

Begge teknikkene beskrevet ovenfor virker flotte. Men det er dette som skjer da jeg prøvde å bruke dem på inngangselementer med type = send:

Hvorfor jobber de ikke?

Inngangselementet er et erstattet element. Kort sagt, det betyr at det er veldig lite du kan gjøre med disse elementene, med hensyn til DOM og CSS. Konkret kan de ikke ha barnelementer, og ingen pseudo-elementer heller. Nå er det klart hvorfor disse teknikkene mislykkes.

Så hvis du bruker Material Design, er det bedre å holde seg unna input [type = sende] og holde seg til knappelementer. Eller bare fortsett å lese.

Legger til krusninger for å sende innspill

På nettapplikasjonen jeg jobbet med, hadde vi allerede mange innsenderknapper. Å endre dem alle til å bli et annet element ville kreve mye arbeid, og en høy risiko for å bryte stilark og JavaScript-logikk. Så jeg måtte finne ut hvordan jeg legger krusninger til de eksisterende innsendingsknappene.

Bruk en innpakningsbeholder

Jeg skjønte raskt at jeg kunne pakke innsenderknappen inne i et inline-block-element, og bruke inline-block-elementet som krusningsflaten. Her er en rask demonstrasjon:

Selv om jeg liker denne løsningen på grunn av enkelhet, krevde den meg fortsatt å endre markeringen for mange steder. Og jeg visste at det ville være en skjør løsning - nye utviklere ville komme inn i prosjektet, og lage sendeknapper uten å pakke dem ordentlig inn i en krusende overflate. Så jeg fortsatte å søke etter andre løsninger som ikke krevde å endre DOM.

Radiale gradienter

Radial-gradient syntaks gjør at jeg kan kontrollere både sentrum og størrelsen på gradienten. Selvfølgelig tillater det meg også å kontrollere fargen på gradienten, inkludert semi-transparente farger. Og det renner aldri over elementet det er brukt på. Så det virker som om den allerede gjør alt jeg trenger!

Ikke så raskt ... det er en ting som mangler: bakgrunnsbildeegenskapen er ikke animerbar. Jeg kunne ikke få gradienten til å vokse og visne til gjennomsiktig ved hjelp av CSS-animasjoner. Jeg klarte å få den til å vokse ved å animere egenskapen til bakgrunnsstørrelse, men det var alt jeg kunne gjøre.

Jeg prøvde noen få andre ting, for eksempel å ha en falmende sirkel som et animert bilde (ved å bruke apng-formatet), og brukte det som bakgrunnsbilde. Men så kunne jeg ikke kontrollere når bildesløyfen startet og sluttet.

Endelig en løsning med JavaScript

Hva du ikke kan gjøre i CSS, kan du gjøre det i JavaScript. Etter å ha brukt mer tid enn jeg er villig til å innrømme å prøve å få denne effekten til å bruke CSS-animasjoner, ga jeg bare opp og bestemte meg for å skrive animasjonen i JavaScript.

Jeg startet med den radiale gradientløsningen ovenfor, og brukte windows.requestAnimationFrame for å lage en flytende animasjon av den radiale gradienten, vokse og falme. Her er min endelige løsning:

Konklusjon

Så det er mulig å ha ringvirkninger på sendeknapper, bare ikke med CSS alene.

Jeg kunne ikke finne denne teknikken dokumentert noe sted på nettet, så jeg kaller den min egen. Leonardos krusningsteknikk krever ikke endringer i DOM, og fungerer for ethvert element fordi den ikke er avhengig av pseudo-elementer eller barnelementer. Det er imidlertid ikke en feilfri løsning.

For det første er det ytelse. Ved å animere gradienten med JavaScript, mister du mye nettleseroptimaliseringer. Men fordi den eneste egenskapen som endres er bakgrunnsbildet, vil jeg mistenke at nettlesere ikke trenger å flyte om igjen, og bare ville kreve å bruke stilene på nytt og male elementet på nytt. I praksis er det akkurat det som skjer, og ytelsen er veldig bra. Unntaket fra utsagnet er Firefox Mobile, som av en eller annen grunn ikke følger med animasjonen. (rediger: animasjonen er jevn i moderne Firefox Mobile-versjoner)

For det andre bruker teknikken bakgrunnsbildeegenskapen til knappen. Hvis designet krever at knappene dine skal ha et bilde på bakgrunnen, vil ringvirkningen overstyre det. Hvis du virkelig trenger det bildet på designet ditt, kan JavaScript endres for å tegne den radielle gradienten på toppen av det eksisterende bakgrunnsbildet.

For det tredje ser det ikke ut til at dette fungerer i Internet Explorer. Imidlertid ser jeg ikke noen grunn til at det ikke skal fungere med IE10 og over. Kanskje skyldes det at IE bruker en annen syntaks for radial gradient. Men, hvem bryr seg om IE i dag? (rediger: denne metoden fungerer uten problemer på Internet Explorer 11)