En af de ting, der gjorde os forvirrede, da vi byggede vores nye hjemmeside, var morfede bølger, som man kan se forskellige steder på siderne. Vi havde mange ideer til, hvordan vi kunne implementere dem på den rigtige måde uden den store indsats. Men de fleste af løsningerne var langsomme, og vi var nødt til at bygge noget op fra bunden, som ville være hurtigere end allerede eksisterende biblioteker.
Forslag til løsninger
Vi startede med et almindeligt SVG-objekt, som blev animeret med forskellige biblioteker. Da vi gerne ville have tre objekter på én side, var resultatet ikke så tilfredsstillende. Alle animationerne var bare langsomme - alle baner i et enkelt SVG-objekt skulle opdateres på meget kort tid, hvilket gjorde hele siden langsom som en snegl. Vi var nødt til at afvise løsningen med ren SVG indsat i et dokument. Det efterlod os med to andre løsninger at vælge imellem.
Den video
element var den anden mulighed. Vi startede med to problemer:
- gennemsigtig baggrund, som ikke kan anvendes med de mest populære videoformater som .mp4 eller .webm,
- responsivitet, hvilket var et reelt problem, fordi videoer ikke er skalerbare som sådan.
Vi besluttede at holde denne løsning i baghånden - "hvis vi ikke finder noget andet, vælger vi denne".
Den sidste mulighed var at bruge Lærred
med WebGL
gengivelse. Det var sådan en usædvanlig mulighed, fordi vi var nødt til at designe alle gengivelsesmekanikkerne selv. Det skyldes, at de morfiske bølger, vi havde, var brugerdefinerede - det tvang os til at designe en brugerdefineret løsning, og det var den mulighed, vi gerne ville følge og virkelig fokusere på.
Løsningens arkitektur
Lad os starte helt fra bunden. Hvad var det for et materiale, vi skulle bruge til at bygge disse bølger? Ideen var, at alle bølgerne var en SVG-fil i størrelsen 1×1 og specifikke stier placeret rundt om dette område. Animationen af denne SVG blev opbygget af nogle former for tilstande i denne fil. Så alle animationerne blev repræsenteret som et sæt filer, der indeholdt stadierne for at flytte en form.
Se nærmere på, hvad tilstandene er - alle stierne er bare en slags matrix med specifikke værdier placeret i en bestemt rækkefølge. Hvis man ændrer disse værdier i bestemte positioner inden for denne matrix, ændres formen i dens specifikke dele. Vi kan forenkle dette med følgende eksempel:
tilstand 1: [1, 50, 25, 40, 100].
tilstand 2: [0, 75, -20, 5, 120]
tilstand 3: [5, 0, -100, 80, 90]
Så vi kan antage, at den form, vi ønsker at gengive, består af et array med 5 elementer, der ændrer sig med den lineære lempelse i bestemte tidsperioder. Når animationen er færdig med det sidste trin, starter den igen med det første for at give os indtryk af en uendelig animation.
Men... vent - hvad er egentlig det array, der præsenteres ovenfor? Som jeg nævnte, er det en sti, der er ansvarlig for at vise en bestemt form. Al magien er inkluderet i d
egenskab af SVG's sti. Denne egenskab indeholder et sæt "kommandoer" til at tegne en form, og hver kommando har en slags argumenter. Ovennævnte array består af alle de værdier (argumenter), der er knyttet til disse kommandoer.
Den eneste forskel mellem disse "tilstandsfiler" var værdier for specifikke kommandoer, da rækkefølgen af kommandoer var den samme. Så al magien bestod i at hente alle værdierne og animere dem.
Guiden hedder Fysik
I afsnittet ovenfor nævnte jeg, at det eneste magiske ved at animere et objekt er at skabe overgange mellem alle faser af en form. Spørgsmålet er, hvordan man gør det med canvas?
Den funktion, som alle, der arbejdede med Lærred
bør vide er anmodningAnimationFrame. Hvis du ser dette for første gang, synes jeg oprigtigt, at du skal starte med at læse dette. Så det, vi er interesserede i med denne funktion, er argumentet - DOMHighResTimeStamp
. Det ser virkelig skræmmende ud, men i praksis er det ikke så svært at forstå. Vi kan sige, at det er et tidsstempel for den tid, der er gået fra den første gengivelse.
Ok, men hvad kan vi gøre med det? Som den anmodningAnimationFrame
funktion skal kaldes rekursivt, kan vi få adgang til et tidsdelta mellem dens kald. Og så er vi i gang med videnskaben! ⚛️ (ok, måske ikke raketvidenskab ... endnu)
Fysikken lærer os, at deltaet for en afstand er lig med deltaet for tiden ganget med hastigheden. I vores tilfælde er hastigheden konstant, fordi vi ønsker at nå slutpunktet inden for et bestemt tidsrum. Så lad os se på, hvordan vi kan repræsentere det med ovenstående tilstande:
Lad os sige, at vi ønsker at skifte mellem disse tilstande på tusind millisekunder, så hastighedsværdierne vil være følgende:
delta: [ -1, 25, -45, -35, 20].
hastighed: [-1/1000, 25/1000, -45/1000, -35/1000, 20/1000].
Hastigheden ovenfor fortæller os: For hvert millisekund øger vi værdien med -1/1000. Og her er det punkt, hvor vi kan gå tilbage til vores anmodningAnimationFrame
og tidsdelta. Værdien af en bestemt position, som vi ønsker at øge med, er tidsdelta ganget med positionens hastighed. Endnu en ting, man kan opnå uden problemer, er at begrænse værdien, så den ikke overstiger den tilstand, den er på vej til.
Når overgangen slutter, kalder vi på en ny og så videre. Og det ser ikke ud til at være så svært at implementere, men en af hovedreglerne i softwareudvikling er ikke at bruge tid på ting, der allerede er implementeret. Så vi valgte en lille bibliotek der giver os mulighed for at skabe disse overgange på en ubesværet måde.
Sådan skabte vi en animeret bølge, der ligner et levende væsen.
Et par ord om kloning af figurer
Som du kan se, er Codest-mærkets bølger ikke en enkelt animeret figur. De består af mange figurer med samme form, men forskellig størrelse og position. I dette trin ser vi hurtigt på, hvordan man duplikerer på en sådan måde.
Så lærredskonteksten giver os mulighed for at skaleret tegneområde (under motorhjelmen - vi kan sige, at den multiplicerer alle dimensioner, der sendes til drawable-metoder, med "k", hvor "k" er en skaleringsfaktor, der som standard er sat til "1"), gør lærredet oversatDet er som at ændre ankerpunktet for et tegneområde. Og vi kan også springe mellem disse tilstande med disse metoder: gemme og genoprette.
Disse metoder giver os mulighed for at gemme tilstanden "nul modifikationer" og derefter gengive et bestemt antal bølger i loopet med korrekt skaleret og oversat lærred. Lige efter dette kan vi gå tilbage til den gemte tilstand. Det er alt om figurkloning. Det er meget nemmere end at klone får, ikke sandt?
Kirsebær på toppen
Jeg nævnte, at vi afviste en af de potentielle løsninger på grund af ydeevnen. Løsningen med canvas er ret hurtig, men ingen sagde, at den ikke kunne optimeres endnu mere. Lad os starte med det faktum, at vi egentlig er ligeglade med at overføre figurer, når de er uden for browserens visningsport.
Der er et andet browser-API, som programmørerne elskede. KrydsObserver. Det giver os mulighed for at følge specifikke elementer på siden og håndtere hændelser, der fremkaldes, når disse elementer vises eller forsvinder fra visningsvinduet. Lige nu har vi en ret nem situation - lad os skabe en synlighedstilstand, ændre den ved hjælp af IntersectionObserver event handlers og simpelthen slå renderingssystemet til og fra for bestemte figurer. Og ... bum, ydelsen er blevet meget bedre! Vi gengiver kun de ting, der er synlige i viewporten.
Sammenfatning
At vælge en måde at implementere ting på er ofte et svært valg, især når de tilgængelige muligheder ser ud til at have de samme fordele og ulemper. Nøglen til at træffe et korrekt valg er at analysere hver enkelt af dem og udelukke dem, vi ser som mindre fordelagtige. Ikke alt er klart - nogle løsninger kræver mere tid end de andre, men de er måske nemmere at optimere eller tilpasse.
Selvom der kommer nye JS-biblioteker næsten hvert minut, er der ting, de ikke kan løse. Og derfor bør alle frontend-ingeniører (og ikke kun dem) kende browser-API'er, holde sig ajour med tekniske nyheder og nogle gange bare tænke "hvordan ville min implementering af dette bibliotek se ud, hvis jeg skulle gøre dette?". Med mere viden om browsere kan vi bygge virkelig smarte ting, træffe gode beslutninger om de værktøjer, vi bruger, og blive mere sikre på vores Kode.
Læs mere om det:
– Ruby 3.0. Ruby og mindre kendte metoder til kontrol af privatlivets fred
– Hold kæft og tag dine penge #1: Skjulte omkostninger og ægte agilitet i produktudviklingsprocessen
– CTO-udfordringer - opskalering og vækst af softwareprodukter