Jedną z rzeczy, które wprawiły nas w zakłopotanie, gdy budowaliśmy naszą nową stronę internetową, były morfowane fale, które można zobaczyć w różnych miejscach na stronach. Mieliśmy wiele pomysłów na to, jak zaimplementować je we właściwy sposób bez większego wysiłku. Jednak większość rozwiązań była powolna i musieliśmy zbudować od podstaw coś, co byłoby szybsze niż już istniejące biblioteki.
Propozycje rozwiązań
Zaczęliśmy od zwykłego obiektu SVG, który był animowany za pomocą różnych bibliotek. Ponieważ chcieliśmy mieć 3 obiekty na jednej stronie, rezultat nie był zbyt satysfakcjonujący. Wszystkie animacje były po prostu powolne - wszystkie ścieżki pojedynczego obiektu SVG musiały być aktualizowane w naprawdę krótkich okresach czasu, co sprawiało, że cała strona była powolna jak ślimak. Musieliśmy odrzucić rozwiązanie z czystym SVG wstawionym do dokumentu. Pozostały nam dwa inne rozwiązania do wyboru.
The wideo
element był drugą opcją. Zaczęliśmy od dwóch problemów:
- przezroczyste tło, którego nie można zastosować w przypadku najpopularniejszych formatów wideo, takich jak .mp4 lub .webm,
- responsywność, co było prawdziwym problemem, ponieważ filmy nie są skalowalne jako takie.
Postanowiliśmy zachować to rozwiązanie na później - "jeśli nie znajdziemy nic innego, wybierzemy to".
Ostatnią opcją było użycie płótno
z WebGL
renderowanie. Była to bardzo nietypowa opcja, ponieważ musieliśmy sami zaprojektować całą mechanikę renderowania. To dlatego, że fale morficzne, które mamy, były niestandardowe - to zmusiło nas do zaprojektowania niestandardowego rozwiązania. I to była opcja, którą chcieliśmy podążać i na której naprawdę się skupiliśmy.
Architektura rozwiązania
Zacznijmy od podstaw. Jakiego materiału musieliśmy użyć do zbudowania tych fal? Pomysł polegał na tym, że wszystkie fale były plikiem SVG o rozmiarze 1×1 i określonymi ścieżkami umieszczonymi wokół tego obszaru. Animacja tego SVG została zbudowana przez pewne formy stanów tego pliku. Tak więc wszystkie animacje były reprezentowane jako zestaw plików, które zawierały etapy przesuwania kształtu.
Spójrzmy głębiej na to, czym są stany - wszystkie ścieżki są po prostu rodzajem tablicy z określonymi wartościami umieszczonymi w określonej kolejności. Zmiana tych wartości w określonych pozycjach w tej tablicy zmienia kształt w jej określonych częściach. Możemy to uprościć za pomocą następującego przykładu:
stan 1: [1, 50, 25, 40, 100]
stan 2: [0, 75, -20, 5, 120]
stan 3: [5, 0, -100, 80, 90]
Możemy więc założyć, że kształt, który chcemy wyrenderować, składa się z tablicy z 5 elementami, które zmieniają się wraz z liniowym easingiem w określonych okresach czasu. Kiedy animacja zakończy ostatni etap, zaczyna się od pierwszego, aby dać nam wrażenie nieskończonej animacji.
Ale... zaraz - czym właściwie jest tablica przedstawiona powyżej? Jak już wspomniałem, jest to ścieżka, która odpowiada za wyświetlanie określonego kształtu. Cała magia zawarta jest w d
ścieżki SVG. Ta właściwość zawiera zestaw "poleceń" do narysowania kształtu, a każde polecenie ma pewien rodzaj argumentów. Wspomniana powyżej tablica składa się ze wszystkich wartości (argumentów) dołączonych do tych poleceń.
Jedyną różnicą między tymi "plikami stanu" były wartości określonych poleceń, ponieważ kolejność poleceń była taka sama. Cała magia polegała więc na pobraniu wszystkich wartości i animowaniu ich.
Kreator o nazwie Fizyka
W powyższym akapicie wspomniałem, że jedyną magią w animowaniu obiektu jest tworzenie przejść między wszystkimi etapami kształtu. Pytanie brzmi - jak to zrobić za pomocą canvas?
Funkcja, z którą współpracowali wszyscy płótno
powinien wiedzieć, że requestAnimationFrame. Jeśli widzisz to po raz pierwszy, szczerze wierzę, że powinieneś zacząć od przeczytania tego. Tak więc, rzeczą w tej funkcji, która nas interesuje, jest argument -. DOMHighResTimeStamp
. Wygląda to naprawdę przerażająco, ale w praktyce nie jest to takie trudne do zrozumienia. Można powiedzieć, że jest to znacznik czasu, który upłynął od pierwszego renderowania.
Ok, ale co możemy z tym zrobić? Ponieważ requestAnimationFrame
funkcja powinna być wywoływana rekurencyjnie, możemy uzyskać dostęp do delty czasu między jej wywołaniami. I tu zaczynamy z nauką! ⚛️ (ok, może nie rocket science... jeszcze)
Fizyka uczy nas, że delta odległości jest równa delcie czasu pomnożonej przez prędkość. W naszym przypadku prędkość jest stała, ponieważ chcemy dotrzeć do punktu końcowego w określonym czasie. Przyjrzyjmy się zatem, jak możemy to przedstawić za pomocą powyższych stanów:
Powiedzmy, że chcemy przechodzić między tymi stanami w ciągu tysiąca milisekund, więc wartości prędkości będą następujące:
delta: [ -1, 25, -45, -35, 20]
prędkość: [-1/1000, 25/1000, -45/1000, -35/1000, 20/1000]
Powyższa prędkość mówi nam: za każdą milisekundę zwiększmy wartość o -1/1000. I to jest punkt, w którym możemy wrócić do naszego requestAnimationFrame
i delta czasu. Wartość konkretnej pozycji, o którą chcemy zwiększyć, to delta czasu pomnożona przez prędkość jej pozycji. Jeszcze jedną rzeczą, którą można osiągnąć bez problemu, jest ograniczenie wartości tak, aby nie przekroczyć stanu, do którego zmierza.
Kiedy przejście się kończy, wywołujemy kolejne i tak dalej. I nie wydaje się to takie trudne do zaimplementowania, ale jedna z głównych zasad w rozwój oprogramowania nie jest poświęcanie czasu na rzeczy, które są już wdrożone. Więc - wybraliśmy mała biblioteka która pozwala nam tworzyć te przejścia w bezwysiłkowy sposób.
W ten sposób stworzyliśmy jedną animowaną falę, która wygląda jak żywe stworzenie.
Kilka słów o klonowaniu kształtów
Jak widać, fale marki The Codest nie są pojedynczą animowaną postacią. Składają się one z wielu figur o tym samym kształcie, ale różnym rozmiarze i położeniu. W tym kroku przyjrzymy się, jak powielić je w taki sposób.
Kontekst kanwy pozwala nam więc na obszar rysowania skali (pod maską - możemy powiedzieć, że mnoży wszystkie wymiary przekazywane do metod drawable przez "k", gdzie "k" jest współczynnikiem skalowania, domyślnie ustawionym na "1"), Przetłumacz płótnoJest to jak zmiana punktu zakotwiczenia obszaru rysowania. Możemy również przeskakiwać między tymi stanami za pomocą tych metod: oszczędzać i przywrócenie.
Metody te pozwalają nam zapisać stan "zero modyfikacji", a następnie wyrenderować określoną liczbę fal w pętli z odpowiednio przeskalowanym i przetłumaczonym płótnem. Zaraz po tym możemy wrócić do zapisanego stanu. To wszystko jeśli chodzi o klonowanie postaci. Dużo łatwiejsze niż klonowanie owiec, prawda?
Wiśnia na szczycie
Wspomniałem, że odrzuciliśmy jedno z potencjalnych rozwiązań ze względu na wydajność. Opcja z płótnem jest całkiem szybka, ale nikt nie powiedział, że nie można jej jeszcze bardziej zoptymalizować. Zacznijmy od tego, że tak naprawdę nie zależy nam na przechodzeniu kształtów, gdy znajdują się one poza rzutnią przeglądarki.
Jest jeszcze jedno API przeglądarki, które pokochali programiści - IntersectionObserver. Pozwala nam śledzić określone elementy strony i obsługiwać zdarzenia, które są wywoływane, gdy te elementy pojawiają się lub znikają z rzutni. W tej chwili mamy dość prostą sytuację - stwórzmy stan widoczności, zmieńmy go za pomocą obsługi zdarzeń IntersectionObserver i po prostu włączmy / wyłączmy system renderowania dla określonych kształtów. I ... bum, wydajność znacznie się poprawiła! Renderujemy tylko te rzeczy, które są widoczne na rzutni.
Podsumowanie
Wybór sposobu wdrożenia rzeczy jest często trudnym wyborem, zwłaszcza gdy dostępne opcje wydają się mieć podobne zalety i wady. Kluczem do dokonania właściwego wyboru jest przeanalizowanie każdej z nich i wykluczenie tych, które postrzegamy jako mniej korzystne. Nie wszystko jest oczywiste - niektóre rozwiązania wymagają więcej czasu niż inne, ale mogą być łatwiejsze do zoptymalizowania lub bardziej konfigurowalne.
Choć nowe biblioteki JS pojawiają się niemal co minutę, są rzeczy, których nie są one w stanie rozwiązać. Dlatego też każdy inżynier front-endu (i nie tylko) powinien znać API przeglądarek, być na bieżąco z nowinkami technicznymi, a czasem po prostu pomyśleć "jak wyglądałaby moja implementacja tej biblioteki, gdybym musiał to zrobić?". Mając większą wiedzę na temat przeglądarek, możemy budować naprawdę wymyślne rzeczy, podejmować dobre decyzje dotyczące narzędzi, których używamy, i być bardziej pewnymi siebie. kod.
Czytaj więcej:
– Ruby 3.0. Ruby i mniej znane metody kontroli prywatności
– Zamknij się i bierz swoje pieniądze #1: Ukryte koszty i prawdziwa elastyczność w procesie rozwoju produktu
– Wyzwania CTO - skalowanie i rozwój oprogramowania