Noe av det som gjorde oss forvirret da vi bygget det nye nettstedet vårt, var de morphede bølgene som du kan se forskjellige steder på sidene. Vi hadde mange ideer om hvordan vi kunne implementere dem på riktig måte uten store anstrengelser. De fleste løsningene var imidlertid trege, og vi måtte bygge noe fra bunnen av som ville være raskere enn allerede eksisterende biblioteker.
Løsningsforslag
Vi startet med et vanlig SVG-objekt som ble animert med forskjellige biblioteker. Siden vi ønsket å ha tre objekter på én side, var ikke resultatet så tilfredsstillende. Alle animasjonene var rett og slett trege - alle banene til et enkelt SVG-objekt måtte oppdateres på veldig kort tid, noe som gjorde hele siden treg som en snegle. Vi måtte forkaste løsningen med ren SVG satt inn i et dokument. Dermed sto vi igjen med to andre løsninger å velge mellom.
Den video
elementet var det andre alternativet. Vi startet med to problemer:
- gjennomsiktig bakgrunn, som ikke kan brukes med de mest populære videoformatene som .mp4 eller .webm,
- responsivitet, noe som var et reelt problem fordi videoer ikke er skalerbare som sådan.
Vi bestemte oss for å holde denne løsningen i bakhånd - "hvis vi ikke finner noe annet, velger vi denne".
Det siste alternativet var å bruke lerret
med WebGL
gjengivelse. Det var et uvanlig alternativ fordi vi måtte designe alle renderingmekanikkene selv. Det var fordi de morfiske bølgene vi hadde, var spesialtilpassede - det tvang oss til å designe en spesialtilpasset løsning. Og det var det alternativet vi ønsket å følge og virkelig fokusere på.
Arkitektur for løsningen
La oss begynne fra bunnen av. Hva var materialet vi måtte bruke for å bygge disse bølgene? Tanken var at alle bølgene var en SVG-fil i størrelse 1×1 og spesifikke baner plassert rundt dette området. Animasjonen av denne SVG-filen ble bygget av noen former for tilstander i denne filen. Så alle animasjonene ble representert som et sett med filer som inneholdt stadiene av å flytte en form.
Ta en nærmere titt på hva tilstandene er - alle banene er bare en slags matrise med spesifikke verdier plassert i en bestemt rekkefølge. Hvis du endrer disse verdiene i bestemte posisjoner i denne matrisen, endres formen i de spesifikke delene. 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]
Vi kan anta at formen vi ønsker å gjengi, består av en matrise med fem elementer som endrer seg med den lineære lettelsen i bestemte tidsperioder. Når animasjonen er ferdig med det siste trinnet, starter den tilbake med det første for å gi oss inntrykk av en uendelig animasjon.
Men ... vent - hva er egentlig matrisen som presenteres ovenfor? Som jeg nevnte, er det en bane som er ansvarlig for å vise en bestemt form. All magien er inkludert i d
egenskapen til SVGs bane. Denne egenskapen inneholder et sett med "kommandoer" for å tegne en figur, og hver kommando har en rekke argumenter. Matrisen som er nevnt ovenfor, består av alle verdiene (argumentene) som er knyttet til disse kommandoene.
Den eneste forskjellen mellom disse "tilstandsfilene" var verdiene til spesifikke kommandoer, ettersom rekkefølgen på kommandoene var den samme. Så alt det magiske handlet om å hente inn alle verdiene og animere dem.
Veiviseren som heter Physics
I avsnittet ovenfor nevnte jeg at det eneste magiske med å animere et objekt er å skape overganger mellom alle stadiene i en form. Spørsmålet er hvordan man gjør det med lerret?
Funksjonen som alle som jobbet med lerret
bør vite er requestAnimationFrame. Hvis du ser dette for første gang, mener jeg oppriktig at du bør begynne med å lese dette. Det vi er interessert i med denne funksjonen er argumentet - DOMHighResTimeStamp
. Det ser veldig skremmende ut, men i praksis er det ikke så vanskelig å forstå. Vi kan si at det er et tidsstempel for tiden som har gått fra den første gjengivelsen.
Ok, men hva kan vi gjøre med dette? Ettersom requestAnimationFrame
funksjonen skal kalles rekursivt, kan vi få tilgang til et tidsdelta mellom anropene. Og her går vi med vitenskapen! ⚛️ (ok, kanskje ikke rakettvitenskap ... ennå)
Fysikken lærer oss at deltaet av en avstand er lik deltaet av tid multiplisert med hastighet. I vårt tilfelle er hastigheten konstant fordi vi ønsker å nå endepunktet i løpet av en bestemt tidsperiode. Så la oss se på hvordan vi kan representere det med tilstandene ovenfor:
La oss si at vi ønsker å skifte mellom disse tilstandene i løpet av tusen millisekunder, så hastighetsverdiene blir som følger:
delta: [ -1, 25, -45, -35, 20]
hastighet: [-1/1000, 25/1000, -45/1000, -35/1000, 20/1000]
Hastigheten ovenfor forteller oss: For hvert millisekund øker vi verdien med -1/1000. Og her er punktet der vi kan gå tilbake til vår requestAnimationFrame
og tidsdelta. Verdien av en bestemt posisjon vi ønsker å øke med, er tidsdelta multiplisert med hastigheten til posisjonen. En ting til for å oppnå dette uten problemer er å begrense verdien slik at den ikke overskrider tilstanden den skal til.
Når overgangen er over, ringer vi en ny og så videre. Og det ser ikke ut til å være så vanskelig å implementere, men en av hovedreglene i programvareutvikling er å ikke bruke tid på ting som allerede er implementert. Så - vi valgte en lite bibliotek som gjør det mulig for oss å skape disse overgangene på en enkel måte.
Slik skapte vi en animert bølge som ser ut som et levende vesen.
Noen ord om kloning av former
Som du kan se, er The Codest-merkebølgene ikke en enkelt animert figur. De består av mange figurer med samme form, men med forskjellig størrelse og posisjon. I dette trinnet vil vi ta en rask titt på hvordan du dupliserer på en slik måte.
Lerretskonteksten gjør det mulig for oss å skala tegneområde (under panseret - vi kan si at den multipliserer alle dimensjonene som sendes inn i drawable-metoder med "k", der "k" er en skaleringsfaktor, som standard er satt til "1"), gjøre lerretet oversatter det som å endre ankerpunktet for et tegneområde. Og vi kan også hoppe mellom disse tilstandene med disse metodene: spare og gjenopprette.
Med disse metodene kan vi lagre tilstanden "null modifikasjoner" og deretter gjengi et bestemt antall bølger i loopen med riktig skalert og oversatt lerret. Rett etter dette kan vi gå tilbake til den lagrede tilstanden. Det er alt om kloning av figurer. Mye enklere enn å klone sauer, ikke sant?
Kirsebær på toppen
Jeg nevnte at vi forkastet en av de potensielle løsningene på grunn av ytelsen. Alternativet med lerret er ganske raskt, men ingen har sagt at det ikke kan optimaliseres enda mer. La oss begynne med det faktum at vi egentlig ikke bryr oss om å flytte figurer når de befinner seg utenfor nettleserens visningsvindu.
Det finnes et annet nettleser-API som programmerere elsket - IntersectionObserver. Det lar oss følge spesifikke elementer på siden og håndtere hendelser som påkalles når disse elementene vises eller forsvinner fra visningsvinduet. Akkurat nå - vi har en ganske enkel situasjon - la oss opprette synlighetstilstanden, endre den på grunn av IntersectionObserver-hendelsesbehandlere og bare slå gjengivelsessystemet på / av for bestemte former. Og ... bang, ytelsen har blitt mye bedre! Vi gjengir bare det som er synlig i visningsvinduet.
Sammendrag
Det er ofte vanskelig å velge en måte å implementere ting på, spesielt når de tilgjengelige alternativene ser ut til å ha de samme fordelene og ulempene. Nøkkelen til å ta et riktig valg er å analysere hver enkelt av dem og velge bort dem vi anser som mindre fordelaktige. Ikke alt er like opplagt - noen løsninger krever mer tid enn andre, men de kan være enklere å optimalisere eller mer tilpasningsdyktige.
Selv om det dukker opp nye JS-biblioteker nesten hvert minutt, er det ting de ikke kan løse. Derfor bør alle frontend-ingeniører (og ikke bare dem) kjenne til nettleser-API-er, holde seg oppdatert på tekniske nyheter og noen ganger bare tenke "hvordan ville min implementering av dette biblioteket sett ut hvis jeg måtte gjøre dette?". Med mer kunnskap om nettlesere kan vi bygge virkelig fancy ting, ta gode beslutninger om verktøyene vi bruker, og bli tryggere på våre kode.
Les mer om dette:
– Ruby 3.0. Ruby og mindre kjente metoder for personvernkontroll
– Hold kjeft og ta pengene dine #1: Skjulte kostnader og reell smidighet i produktutviklingsprosessen
– CTO-utfordringer - oppskalering og vekst av programvareprodukter