CSS-tekniker för materialrimpeleffekt

En guide till olika tekniker för krusningseffekten med CSS och JavaScript

Jag var nyligen tvungen att implementera krusningseffekten från materialdesign till en webbapplikation. Och då insåg jag att jag inte hade någon aning om hur det genomfördes. Detta ledde mig in i en resa för att studera befintliga implementationer och till och med komma med en helt ny teknik som kan vara användbar för dig.

Vad är denna krusningseffekt?

Vänta, du vet inte krusningseffekten från Googles materialdesign? Har du bott i en grotta i många år?

Rippeleffekten används när du trycker på en knapp. Det fungerar på samma sätt för mus- eller beröringsinteraktioner.

Den position du klickar på eller pekar på knappen kallas kontaktpunkten. Därifrån skickas en krusning som rör sig utåt och förlorar opaciteten när den blir större tills den fyller hela knappen. Då försvinner den helt.

Dynamiken i denna krusningseffekt liknar krusningarna du får när du vidrör en vätskeyta eller när du släpper en sten i en sjö.

Ringarna du hittar på webben

Efter att ha gjort lite forskning kunde jag hitta två huvudtekniker som används för att implementera rippeleffekten på webbapplikationer.

Använda :: efter pseudo-element

Med hjälp av denna teknik är :: efter pseudo-elementet i knappen utformad som en halvtransparent cirkel och animeras för att växa och blekna. Containerknappen måste ha överflöde: dold så att cirkeln aldrig flyter utanför knappens yta och position: relativt för att göra det enkelt att placera cirkeln i knappen. Du kan läsa mer information om denna teknik i den här artikeln av Ionuț Colceriu.

En av de fantastiska sakerna med denna teknik är att det är en ren CSS-lösning på krusningseffekten. Rippeleffekten startar emellertid alltid från mitten av knappen istället för kontaktpunkten. Det är inte den mest naturliga feedbacken.

Det kan förbättras genom att använda JavaScript för att lagra kontaktpunkten och använda den för att placera krusningen. Det är exakt vad material.io har gjort för deras webb-rippelkomponent. Den använder CSS-variabler för att lagra kontaktpunkten, och :: efter pseudo-elementet använder dessa variabler för positionering.

Använda barnelement

I huvudsak använder denna teknik samma strategi som tidigare. Men istället för ett pseudo-element lägger det till ett spanelement inuti knappen som sedan kan placeras via JavaScript. Denna teknik beskrivs i denna artikel av Jhey Tompkins.

Den enklaste implementeringen skapar ett intervall för varje klick på knappen och använder muspositionen på klickhändelsen för att ändra positionen för spännvidden. En CSS-animation gör att spännvidden växer och bleknar tills den blir helt transparent. Vi kan välja att ta bort spännvidden från DOM när animationen är klar, eller bara lämna den där under mattan - ingen kommer verkligen att märka ett transparent spann som hänger runt.

Jag hittade en annan variant av detta, där barnelementet är en svg istället för ett spann, och svg animeras via JavaScript. Denna variation förklaras av Dennis Gaebel, men i huvudsak verkar den vara densamma, och kanske tillåter att använda komplexa SVG-former och effekter.

Ett problem med skicka ingångar

Båda teknikerna som beskrivs ovan verkar bra. Men det här är vad som händer när jag försökte tillämpa dem på inputelement med typ = skicka:

Varför fungerar de inte?

Inmatningselementet är ett ersatt element. Kort sagt, det betyder att det är väldigt lite du kan göra med dessa element, med avseende på DOM och CSS. Specifikt kan de inte ha barnelement och inga pseudoelement heller. Nu är det klart varför dessa tekniker misslyckas.

Så om du använder Materialdesign är det bättre att hålla sig borta från input [type = skicka] och hålla fast vid knappelement. Eller bara fortsätt att läsa.

Lägga till krusningar för att skicka inmatningar

På webbapplikationen jag arbetade med hade vi redan massor av skicka-knappar. Att ändra dem alla till att bli ett annat element kräver mycket arbete och en hög risk att bryta stilsidor och JavaScript-logik. Så jag var tvungen att ta reda på hur man lägger till ringar till de befintliga skicka-knapparna.

Använd en inpackningsbehållare

Jag insåg snabbt att jag kunde linda in skicka-knappen inuti ett inline-block-element och använda inline-block-elementet som krusningsytan. Här är en snabb demo:

Medan jag gillar den här lösningen för dess enkelhet krävde den fortfarande att jag ändrade markeringen på för många platser. Och jag visste att det skulle vara en ömtålig lösning - nya utvecklare skulle komma in i projektet och skapa skicka-knappar utan att ordentligt linda in dem i en krusig yta. Så jag fortsatte att leta efter andra lösningar som inte krävde att byta DOM.

Radiella gradienter

Den radiella gradientsyntaxen tillåter mig att styra både centrum och storleken på lutningen. Naturligtvis tillåter det mig också att kontrollera färgen på lutningen, inklusive halvgenomskinliga färger. Och det överträffar aldrig det element det appliceras på. Så det verkar som om det redan gör allt jag behöver!

Inte så snabbt ... det saknas en sak: bakgrundsbildegenskapen kan inte animeras. Jag kunde inte få lutningen att växa och blekna till transparent genom att använda CSS-animationer. Jag lyckades få den att växa genom att animera egenskapen bakgrundsstorlek, men det var allt jag kunde göra.

Jag försökte några andra saker, som att ha en blekningscirkel som en animerad bild (med apng-formatet) och använde den som en bakgrundsbild. Men då kunde jag inte kontrollera när bildslingan startade och slutade.

Slutligen en lösning med JavaScript

Vad du inte kan göra i CSS kan du göra det i JavaScript. Efter att ha ägnat mer tid än jag är villig att erkänna att jag försökte få denna effekt att fungera med CSS-animationer, gav jag bara upp och bestämde mig för att skriva animationen i JavaScript.

Jag började med den radiella gradientlösningen ovan och använde windows.requestAnimationFrame för att göra en flytande animering av den radiella gradienten, växer och bleknar. Här är min slutliga lösning:

Slutsats

Så det är möjligt att ha rippeleffekter på skicka knappar, bara inte med CSS ensam.

Jag kunde inte hitta den här tekniken dokumenterad någonstans på webben, så jag kallar den min egen. Leonardos rippelteknik kräver inte förändringar av DOM och fungerar för något element eftersom den inte litar på pseudo-element eller barnelement. Det är dock inte en felfri lösning.

Först, det är prestanda. Genom att animera lutningen med JavaScript, förlorar du många webbläsaroptimeringar. Men eftersom den enda egenskapen som ändras är bakgrundsbilden, skulle jag misstänka att webbläsare inte skulle behöva flytta om igen, och bara skulle kräva omformaterna på nytt och måla om elementet. I praktiken är det exakt vad som händer och prestandan är riktigt bra. Undantaget från detta uttalande är Firefox Mobile, som av någon anledning inte följer med animationen. (redigera: animering är smidig i moderna Firefox Mobile-versioner)

För det andra använder tekniken knappens bakgrundsbildegenskap. Om din design kräver att dina knappar ska ha en bild på sin bakgrund, kommer rippeleffekten att åsidosätta det. Om du verkligen behöver den bilden på din design, kan JavaScript ändras för att rita den radiella lutningen ovanpå den befintliga bakgrundsbilden.

För det tredje verkar detta inte fungera i Internet Explorer. Jag ser emellertid inte någon anledning till att den inte ska fungera med IE10 och senare. Kanske beror det på att IE använder en annan syntax för radiell gradient. Men vem bryr sig om IE idag? (redigera: den här metoden fungerar utan problem i Internet Explorer 11)