Een onscherpte animeren

Vervagen is een geweldige manier om de aandacht van een gebruiker te verleggen. Door sommige visuele elementen wazig te laten lijken terwijl andere elementen scherp blijven, wordt de focus van de gebruiker op natuurlijke wijze gestuurd. Gebruikers negeren de wazige inhoud en concentreren zich in plaats daarvan op de inhoud die ze kunnen lezen. Een voorbeeld hiervan is een lijst met pictogrammen die details over de afzonderlijke items weergeven als u er met de muis overheen beweegt. Gedurende die tijd kunnen de resterende keuzes wazig worden gemaakt, zodat de gebruiker wordt omgeleid naar de nieuw weergegeven informatie.

TL; DR

Het animeren van een onscherpte is niet echt een optie, omdat het erg langzaam is. Bereken in plaats daarvan vooraf een reeks steeds vager wordende versies en vervaag ertussen. Mijn collega Yi Gu heeft een bibliotheek geschreven om alles voor je te regelen! Bekijk onze demo .

Deze techniek kan echter behoorlijk schokkend zijn als deze zonder enige overgangsperiode wordt toegepast. Het animeren van een onscherpte (de overgang van onscherp naar onscherp) lijkt een redelijke keuze, maar als je dit ooit op internet hebt geprobeerd, heb je waarschijnlijk gemerkt dat de animaties allesbehalve vloeiend zijn, zoals deze demo laat zien als je dat niet hebt gedaan een krachtige machine. Kunnen wij het beter doen?

Het probleem

Opmaak is omgezet in texturen door de CPU. Texturen worden geüpload naar de GPU. De GPU tekent deze texturen naar de framebuffer met behulp van shaders. De vervaging vindt plaats in de schaduw.

Vanaf nu kunnen we het animeren van een onscherpte niet efficiënt laten werken. We kunnen echter een oplossing vinden die er goed genoeg uitziet, maar technisch gezien geen geanimeerde waas is. Laten we, om te beginnen, eerst begrijpen waarom de geanimeerde onscherpte langzaam is. Om elementen op internet te vervagen, zijn er twee technieken: de CSS- filter en SVG-filters. Dankzij de verbeterde ondersteuning en het gebruiksgemak worden doorgaans CSS-filters gebruikt. Als u Internet Explorer moet ondersteunen, heeft u helaas geen andere keuze dan SVG-filters te gebruiken, aangezien IE 10 en 11 deze wel ondersteunen, maar geen CSS-filters. Het goede nieuws is dat onze oplossing voor het animeren van onscherpte met beide technieken werkt. Laten we dus proberen het knelpunt te vinden door naar DevTools te kijken.

Als u "Paint Flashing" in DevTools inschakelt, ziet u helemaal geen flitsen. Het lijkt erop dat er geen herschilderingen plaatsvinden. En dat is technisch correct, aangezien "opnieuw schilderen" verwijst naar het feit dat de CPU de textuur van een gepromoveerd element opnieuw moet schilderen. Wanneer een element zowel wordt gepromoot als vervaagd, wordt de vervaging door de GPU toegepast met behulp van een arcering.

Zowel SVG-filters als CSS-filters gebruiken convolutiefilters om vervaging toe te passen. Convolutiefilters zijn vrij duur omdat voor elke uitgangspixel een aantal ingangspixels in aanmerking moet worden genomen. Hoe groter het beeld of hoe groter de vervagingsradius, hoe duurder het effect is.

En dat is waar het probleem ligt: ​​we voeren elk frame een vrij dure GPU-bewerking uit, waardoor ons framebudget van 16 ms wordt opgeblazen en daardoor ruim onder de 60 fps terechtkomt.

Door het konijnenhol

Wat kunnen we doen om dit soepel te laten verlopen? Wij kunnen goochelarij gebruiken! In plaats van de daadwerkelijke vervagingswaarde (de straal van de vervaging) te animeren, berekenen we vooraf een aantal vervaagde kopieën waarbij de vervagingswaarde exponentieel toeneemt, en vervagen we ertussen met behulp van opacity .

De cross-fade is een reeks overlappende dekkingsfade-ins en -fade-outs. Als we bijvoorbeeld vier vervagingsfasen hebben, vervagen we de eerste fase terwijl we tegelijkertijd de tweede fase vervagen. Zodra de tweede fase de dekking van 100% bereikt en de eerste 0% heeft bereikt, vervagen we de tweede fase terwijl we vervagen in de derde fase. Zodra dat is gebeurd, vervagen we uiteindelijk de derde fase en vervagen we in de vierde en laatste versie. In dit scenario zou elke fase ¼ van de totale gewenste duur in beslag nemen. Visueel lijkt dit erg op een echte, geanimeerde vervaging.

In onze experimenten leverde het exponentieel vergroten van de vervagingsradius per fase de beste visuele resultaten op. Voorbeeld: Als we vier vervagingsfasen hebben, passen we filter: blur(2^n) toe op elke fase, dwz fase 0: 1px, fase 1: 2px, fase 2: 4px en fase 3: 8px. Als we elk van deze wazige kopieën op hun eigen laag forceren (genaamd "promoten") met behulp van will-change: transform , zou het veranderen van de dekking van deze elementen supersnel moeten zijn. In theorie zou dit ons in staat stellen het dure werk van vervaging naar voren te halen. Het blijkt dat de logica gebrekkig is. Als je deze demo uitvoert, zul je zien dat de framerate nog steeds onder de 60 fps ligt en dat de onscherpte zelfs erger is dan voorheen.

Ontwikkelaars   toont een spoor waar de GPU lange periodes van drukke tijd heeft.

Een snelle blik op DevTools laat zien dat de GPU het nog steeds extreem druk heeft en elk frame tot ~90 ms uitrekt. Maar waarom? We veranderen de vervagingswaarde niet meer, alleen de dekking, dus wat gebeurt er? Het probleem ligt wederom in de aard van het vervagingseffect: zoals eerder uitgelegd, als het element zowel wordt gepromoot als vervaagd, wordt het effect toegepast door de GPU. Dus ook al animeren we de vervagingswaarde niet meer, de textuur zelf is nog steeds onvervaagd en moet elk frame opnieuw vervaagd worden door de GPU. De reden dat de framesnelheid nog slechter is dan voorheen komt voort uit het feit dat de GPU, vergeleken met de naïeve implementatie, eigenlijk meer werk heeft dan voorheen, omdat er meestal twee texturen zichtbaar zijn die onafhankelijk van elkaar vervaagd moeten worden.

Wat we bedacht hebben is niet mooi, maar het maakt de animatie razendsnel. We gaan terug naar het niet promoten van het te vervagen element, maar in plaats daarvan naar een bovenliggende wrapper. Als een element zowel vervaagd als gepromoot wordt, wordt het effect toegepast door de GPU. Dit maakte onze demo traag. Als het element vervaagd is maar niet wordt gepromoot, wordt de vervaging in plaats daarvan gerasterd naar de dichtstbijzijnde bovenliggende structuur. In ons geval is dat het gepromote bovenliggende wrapperelement. De wazige afbeelding is nu de textuur van het bovenliggende element en kan voor alle toekomstige frames worden hergebruikt. Dit werkt alleen omdat we weten dat de vervaagde elementen niet geanimeerd zijn en dat het cachen ervan eigenlijk nuttig is. Hier is een demo die deze techniek implementeert. Ik vraag me af wat de Moto G4 van deze aanpak vindt? Spoiler alert: hij vindt het geweldig:

Ontwikkelaars   toont een spoor waar de GPU veel inactieve tijd heeft.

Nu hebben we veel hoofdruimte op de GPU en een zijdezachte 60 fps. Wij hebben het gedaan!

Productioniseren

In onze demo hebben we een DOM-structuur meerdere keren gedupliceerd, zodat kopieën van de inhoud met verschillende sterktes kunnen vervagen. Je vraagt ​​je misschien af ​​hoe dit zou werken in een productieomgeving, omdat dat onbedoelde bijwerkingen kan hebben met de CSS-stijlen van de auteur of zelfs met hun JavaScript. Je hebt gelijk. Betreed Shadow DOM!

Hoewel de meeste mensen Shadow DOM beschouwen als een manier om "interne" elementen aan hun aangepaste elementen te koppelen, is het ook een isolatie- en prestatieprimitief! JavaScript en CSS kunnen de Shadow DOM-grenzen niet doorbreken, waardoor we inhoud kunnen dupliceren zonder de stijlen of applicatielogica van de ontwikkelaar te verstoren. We hebben al een <div> -element voor elke kopie om op te rasteren en gebruiken deze <div> 's nu als schaduwhosts. We maken een ShadowRoot met behulp van attachShadow({mode: 'closed'}) en voegen een kopie van de inhoud toe aan de ShadowRoot in plaats van aan de <div> zelf. We moeten ervoor zorgen dat we ook alle stylesheets naar de ShadowRoot kopiëren om te garanderen dat onze kopieën op dezelfde manier zijn opgemaakt als het origineel.

Sommige browsers ondersteunen Shadow DOM v1 niet, en voor deze browsers vallen we terug op het dupliceren van de inhoud en hopen we er het beste van dat er niets kapot gaat. We zouden de Shadow DOM-polyfill kunnen gebruiken met ShadyCSS , maar we hebben dit niet in onze bibliotheek geïmplementeerd.

En daar ga je. Na onze reis door de weergavepijplijn van Chrome, hebben we ontdekt hoe we vervagingen efficiënt in verschillende browsers kunnen animeren!

Conclusie

Dit soort effect mag niet lichtvaardig worden gebruikt. Omdat we DOM-elementen kopiëren en op hun eigen laag forceren, kunnen we de grenzen van lagere apparaten verleggen. Het kopiëren van alle stylesheets naar elke ShadowRoot is ook een potentieel prestatierisico, dus u moet beslissen of u liever uw logica en stijlen aanpast zodat deze niet worden beïnvloed door kopieën in LightDOM of dat u onze ShadowDOM -techniek gebruikt. Maar soms kan onze techniek een waardevolle investering zijn. Bekijk de code in onze GitHub-repository en de demo en stuur me een bericht op Twitter als je vragen hebt!