Worklet animacji Houdiniego

Zwiększ atrakcyjność animacji w swojej aplikacji internetowej

TL;DR: Worklet animacji umożliwia pisanie imperatywnych animacji, które uruchamiają się z fabryczną liczbą klatek, co zapewnia dodatkową płynność, Sprawia, że animacje są bardziej odporne na zatkanie w głównym wątku i można je kliknąć. do przewijania, a nie do czasu. Worklet animacji jest w Chrome Canary (za „Eksperymentalne funkcje platformy internetowej” flaga) i planujemy wersję próbną Chrome 71. Możesz zacząć go używać jako stopniowe ulepszenie dzisiaj.

Inny interfejs API Animation?

W rzeczywistości nie, to tylko kontynuacja tego, co już mieliśmy, i mamy ku temu dobry powód. Zacznijmy od początku. Jeśli chcesz animować dowolny element DOM w internecie masz dzisiaj do wyboru dwie opcje: przejścia CSS, proste przejścia A do B, animacje CSS potencjalnie cykliczne, bardziej złożone animacje czasowe oraz interfejs Web Animations API. (WAAPI) w przypadku niemal dowolnie złożonych animacji. Tablica pomocy WAAPI wygląda dość ponuro, idzie ku górze. Do tego czasu będzie jeszcze polyfill.

Cechą wspólną wszystkich tych metod jest to, że są bezstanowe na czas. Niektóre efekty, które testują programiści, nie są ani ograniczonych czasowo czy bezstanowych. Na przykład słynny pasek paralaksy to: jak sama nazwa wskazuje, będą przewijane. Zaimplementowanie w internecie skutecznego mechanizmu paralaksy na potrzeby przewijania jest obecnie zaskakująco trudne.

A co z bezstannością? Weźmy na przykład pasek adresu Chrome na Androida, przykład. Jeśli przewiniesz w dół, zniknie on z widoku. Ale gdy przewiniesz palcem w górę, urządzenie powraca, nawet jeśli będziesz w połowie drogi. w dół tej strony. Animacja zależy nie tylko od pozycji przewijania, ale też od poprzedniego kierunku przewijania. Jest stanowa.

Innym problemem jest styl pasków przewijania. Takie tytuły nie są nudne ani stylizowane co najmniej za mało stylu. Co zrobić, jeśli na pasku przewijania chcę wyświetlać kot nyan? Niezależnie od wybranej metody, utworzenie niestandardowego paska przewijania nie wydajny ani prosty.

Trzeba jednak pamiętać, że wszystko to jest niezręczne i trudne do uniemożliwienia. jego efektywne wdrażanie. Większość z nich bazuje na zdarzeniach lub requestAnimationFrame, który może utrzymywać szybkość 60 kl./s, nawet na ekranie może działać z prędkością 90 kl./s, 120 kl./s lub większą i wykorzystywać bezcennego budżetu na ramkę wątku głównego.

Worklet animacji rozszerza możliwości internetowego stosu animacji, o takie efekty. Zanim przejdziemy do szczegółów, który zawiera najbardziej aktualne informacje o animacjach.

Podstawowe informacje o animacjach i osi czasu

WAAPI i Worklet animacji szeroko wykorzystują oś czasu, korygować animacje i efekty w wybrany przez siebie sposób. Ta sekcja jest szybkie przypomnienie lub wprowadzenie do osi czasu i sposobu ich współdziałania z animacjami.

Każdy dokument zawiera document.timeline. Wartość zaczyna się od 0, gdy dokument jest i odlicza milisekundy od momentu utworzenia dokumentu. Wszystkie z tej listy Animacje w dokumencie będą działać względem tej osi czasu.

Aby zyskać więcej konkretów, przyjrzyjmy się temu fragmentowi kodu WAAPI.

const animation = new Animation(
  new KeyframeEffect(
    document.querySelector('#a'),
    [
      {
        transform: 'translateX(0)',
      },
      {
        transform: 'translateX(500px)',
      },
      {
        transform: 'translateY(500px)',
      },
    ],
    {
      delay: 3000,
      duration: 2000,
      iterations: 3,
    }
  ),
  document.timeline
);

animation.play();

Gdy wywołujemy funkcję animation.play(), animacja używa parametru currentTime osi czasu . Opóźnienie animacji wynosi 3000 ms, co oznacza, że animacja rozpocznie się (lub stanie się „aktywna”), gdy oś czasu osiągnie wartość „startTime”

  • 3000. After that time, the animation engine will animate the given element from the first keyframe (translateX(0)), through all intermediate keyframes (translateX(500px)) all the way to the last keyframe (translateY(500px)) in exactly 2000ms, as prescribed by thedurationoptions. Since we have a duration of 2000ms, we will reach the middle keyframe when the timeline'scurrentTimeisstartTime + 3000 + 1000and the last keyframe atstartTime + 3000 + 2000`. Najważniejsze jest, elementy sterujące osią czasu w animacji.

Gdy animacja dotrze do ostatniej klatki kluczowej, przeskoczy z powrotem do pierwszej klatki kluczowej i rozpoczynać kolejną iterację animacji. Ten proces powtarza łącznie 3 razy od ustawienia iterations: 3. Gdybyśmy chcieli, by animacja była nigdy nie przestaliby wpisywać znaków iterations: Number.POSITIVE_INFINITY. Oto result kodu powyżej.

WAAPI ma niesamowicie zaawansowane możliwości i jest w nim znacznie więcej funkcji, np. wygładzanie, odsunięcia początkowe, wagi klatek kluczowych i zachowanie wypełnienia, które spowodowałoby tego artykułu. Jeśli chcesz dowiedzieć się więcej, przeczytaj ten artykuł na temat animacji CSS w trikach CSS.

Pisanie Workleta animacji

Skoro znamy już koncepcję osi czasu, możemy zająć się Worklet animacji i sposób jego pomieszania z osiami czasu Animacja Worklet API nie tylko opiera się na WAAPI, ale jest – w kontekście elastycznej sieci – podstawowym elementem niższego poziomu, wyjaśnia, jak działa WAAPI. Jeśli chodzi o składnię, są one bardzo podobne pod względem składni:

Worklet animacji protokół WAAPI
new WorkletAnimation(
  'passthrough',
  new KeyframeEffect(
    document.querySelector('#a'),
    [
      {
        transform: 'translateX(0)'
      },
      {
        transform: 'translateX(500px)'
      }
    ],
    {
      duration: 2000,
      iterations: Number.POSITIVE_INFINITY
    }
  ),
  document.timeline
).play();
      
        new Animation(

        new KeyframeEffect(
        document.querySelector('#a'),
        [
        {
        transform: 'translateX(0)'
        },
        {
        transform: 'translateX(500px)'
        }
        ],
        {
        duration: 2000,
        iterations: Number.POSITIVE_INFINITY
        }
        ),
        document.timeline
        ).play();
        

Różnica dotyczy pierwszego parametru, który jest nazwą workletu. który jest podstawą tej animacji.

Wykrywanie cech

Chrome jest pierwszą przeglądarką, w której ta funkcja jest dostępna, więc musisz zadbać o to, kod nie oczekuje, że element AnimationWorklet będzie dostępny. Przed wczytaniem Worklet, należy wykryć, czy przeglądarka użytkownika obsługuje AnimationWorklet z prostą weryfikacją:

if ('animationWorklet' in CSS) {
  // AnimationWorklet is supported!
}

Wczytuję Worklet

Worklety to nowa koncepcja wprowadzonej przez grupę zadaniową Houdini. Dzięki niej łatwiej tworzyć i skalować nowe interfejsy API. Omówimy szczegóły dotyczące Workletów trochę później, ale dla uproszczenia mogą być one takie proste wątki (takie jak instancje robocze).

Musimy się upewnić, że wczytaliśmy Worklet o nazwie „passthrough” przed zadeklarowaniem animacji:

// index.html
await CSS.animationWorklet.addModule('passthrough-aw.js');
// ... WorkletAnimation initialization from above ...

// passthrough-aw.js
registerAnimator(
  'passthrough',
  class {
    animate(currentTime, effect) {
      effect.localTime = currentTime;
    }
  }
);

Co się tutaj dzieje? Rejestruujemy zajęcia jako animator za pomocą Wywołanie registerAnimator() obiektu AnimationWorklet, które nosi nazwę „passthrough”. Ta sama nazwa, która została użyta w konstruktorze WorkletAnimation() powyżej. Gdy funkcja rejestracja została zakończona, obietnica zwrócona przez firmę addModule() zostanie rozwiązana i możemy zacząć tworzyć animacje na jego podstawie.

Metoda animate() naszej instancji będzie wywoływana dla każdej klatki przeglądarka chce wyrenderować, przekazując currentTime osi czasu animacji oraz efekt, który jest obecnie przetwarzany. Mamy tylko jedną efektu, KeyframeEffect i używamy currentTime do ustawienia efektu localTime, dlatego ten animator jest nazywany „przejazdem”. Z tym kodem do Worklet, WAAPI i AnimationWorklet powyżej działają dokładnie jak widać w tabeli demo.

Godzina

Parametr currentTime metody animate() to currentTime parametru do konstruktora WorkletAnimation(). W poprzedniej na przykład przerobiliśmy ten czas. Ponieważ jednak jest to w kodzie JavaScript i możemy zniekształcić czas 💫

function remap(minIn, maxIn, minOut, maxOut, v) {
  return ((v - minIn) / (maxIn - minIn)) * (maxOut - minOut) + minOut;
}
registerAnimator(
  'sin',
  class {
    animate(currentTime, effect) {
      effect.localTime = remap(
        -1,
        1,
        0,
        2000,
        Math.sin((currentTime * 2 * Math.PI) / 2000)
      );
    }
  }
);

Wykorzystujemy Math.sin() (currentTime) i przypisujemy tę wartość do zakres [0; 2000], czyli zakres czasu, dla którego zdefiniowany jest nasz efekt. Teraz wyglądają zupełnie inaczej – zmieniły klatki kluczowe lub opcje animacji. Kodem Workletu można dowolnego złożonego charakteru i umożliwia automatyczne definiowanie efektów, w jakiej kolejności i w jakim zakresie.

Opcje nad opcjami

Możesz ponownie użyć Worklet i zmienić jego numery. Z tego powodu Konstruktor WorkletAnimation umożliwia przekazanie obiektu options do Workletu:

registerAnimator(
  'factor',
  class {
    constructor(options = {}) {
      this.factor = options.factor || 1;
    }
    animate(currentTime, effect) {
      effect.localTime = currentTime * this.factor;
    }
  }
);

new WorkletAnimation(
  'factor',
  new KeyframeEffect(
    document.querySelector('#b'),
    [
      /* ... same keyframes as before ... */
    ],
    {
      duration: 2000,
      iterations: Number.POSITIVE_INFINITY,
    }
  ),
  document.timeline,
  {factor: 0.5}
).play();

W tym przykładzie Obie animacje są oparte na tym samym kodzie, ale z różnymi opcjami.

Podaj swój region.

Jak już wspomniałem, jednym z kluczowych problemów, które ma rozwiązać zadanie animacji, jest w postaci animacji stanowych. Worklety animacji mogą przechowywać stan. Jednak jedna z nich z głównych funkcji Workletów jest to, że można je przenosić do innych a nawet zniszczyć ją w celu zapisania zasobów, co mogłoby także zniszczyć stanu. Aby zapobiec utracie stanu, Worklet animacji oferuje punkt zaczepienia, który jest wywoływany przed zniszczeniem listy zadań, której można użyć do zwrócenia stanu. obiektu. Ten obiekt zostanie przekazany do konstruktora, gdy Worklet zostanie na nowo. Przy pierwszym tworzeniu ten parametr będzie miał wartość undefined.

registerAnimator(
  'randomspin',
  class {
    constructor(options = {}, state = {}) {
      this.direction = state.direction || (Math.random() > 0.5 ? 1 : -1);
    }
    animate(currentTime, effect) {
      // Some math to make sure that `localTime` is always > 0.
      effect.localTime = 2000 + this.direction * (currentTime % 2000);
    }
    destroy() {
      return {
        direction: this.direction,
      };
    }
  }
);

Za każdym razem, gdy odświeżysz tę demonstrację, otrzymasz 50/50 prawdopodobieństwo, w którym kierunku obróci się kwadrat. Gdyby przeglądarka została demontowana i przenieść go do innego wątku, Math.random() wywołuje proces tworzenia, co może spowodować nagłą zmianę kierunek. Aby tego uniknąć, zwracamy animacje losowo wybrany kierunek jako state (stan) i użyj go w konstruktorze, jeśli jest podany.

Zagłębianie się w kontynuum czasoprzestrzenne: ScrollTimeline

Jak widać w poprzedniej sekcji, AnimationWorklet pozwala automatycznie określić wpływ zmian w harmonogramie na wyniki, animację. Jednak do tej pory zawsze znajdowała się tu oś czasu document.timeline, czyli śledzi czas.

ScrollTimeline otwiera przed Tobą nowe możliwości i umożliwia tworzenie animacji z przewijaniem zamiast z czasem. Wykorzystujemy pierwszy „przekazywanie” Worklet dla tego demonstracja:

new WorkletAnimation(
  'passthrough',
  new KeyframeEffect(
    document.querySelector('#a'),
    [
      {
        transform: 'translateX(0)',
      },
      {
        transform: 'translateX(500px)',
      },
    ],
    {
      duration: 2000,
      fill: 'both',
    }
  ),
  new ScrollTimeline({
    scrollSource: document.querySelector('main'),
    orientation: 'vertical', // "horizontal" or "vertical".
    timeRange: 2000,
  })
).play();

Zamiast wartości document.timeline tworzymy nowy ScrollTimeline. Być może zgadniesz. ScrollTimeline nie używa czasu, ale Pozycja przewijania scrollSource do ustawienia currentTime w Worklet. Bycie przewinięto do końca na górę (lub w lewo) oznacza currentTime = 0, a przewinięcie do końca na sam dół (lub w prawo) powoduje ustawienie wartości currentTime na timeRange Jeśli przewiniesz pole w tej demo. kontrolować położenie czerwonego pola.

Jeśli utworzysz ScrollTimeline z elementem, który nie przewija się, funkcja currentTime na osi czasu będzie NaN. Zwłaszcza przy elastycznym projektowaniu przygotuj się na NaN swojego currentTime. Często sensowne jest domyślne ustawienie wartości 0.

Od dawna szukaliśmy możliwości łączenia animacji z pozycją przewijania, ale nigdy nie udało się jej osiągnąć na takim poziomie wierności (oprócz sposoby obejścia w kodzie CSS3D). Worklet animacji umożliwia stosowanie tych efektów zostały wdrożone w prosty sposób przy zachowaniu dużej wydajności. Na przykład: efekt przewijania z efektem paralaksy, demo pokazuje, Teraz wystarczy kilka wierszy, aby utworzyć animację przewijaną.

Dla zaawansowanych

Worklety

Worklety to konteksty JavaScriptu z osobnym zakresem i bardzo małym interfejsem API na różnych powierzchniach. Mała powierzchnia interfejsu API umożliwia bardziej agresywną optymalizację zwłaszcza na niedrogich urządzeniach. Dodatkowo Worklety nie są powiązane z w określonym cyklu zdarzeń, ale w razie potrzeby można przechodzić między wątkami. To jest szczególnie ważne w przypadku obiektu AnimationWorklet.

Nsynchronizacja kompozytora

Niektóre właściwości CSS szybko się animują, Nie. Niektóre właściwości wymagają do działania GPU, przeglądarka ponownie wyświetli cały dokument.

W Chrome (podobnie jak w wielu innych przeglądarkach) mamy proces nazywany kompozytorem, czyją to zadanie – i bardzo upraszczam – rozmieszczanie warstw tekstury, a następnie możliwie regularnie aktualizować ekran za pomocą GPU. najlepiej z szybkością, od której aktualizuje się ekran (zwykle 60 Hz). W zależności od tego, Właściwości CSS są animowane, przeglądarka może wymagać jedynie polecenia kompozytora wykonuje swoją pracę, natomiast inne właściwości muszą uruchamiać układ, który jest którą może wykonać tylko wątek główny. W zależności od tego, jakie usługi Jeśli plan ma być animowany, Worklet animacji zostanie powiązany z głównym elementem lub uruchomić w osobnym wątku zsynchronizowanym z kompozytorem.

Klepnięcie w nadgarstku

Zwykle istnieje tylko jeden proces kompozytora, który może być współużytkowany wiele kart, ponieważ GPU jest bardzo wymagającym zasobem. Jeśli kompozytor otrzyma w jakiś sposób są zablokowane, cała przeglądarka zawiesza się i nie reaguje na danych wejściowych użytkownika. Należy tego unikać za wszelką cenę. Co się więc dzieje, gdy Worklet nie może dostarczyć danych, których kompozytor potrzebuje w czasie do zatrzymania klatki wyrenderowano?

W takim przypadku Worklet może się „prześlizgać” (zgodnie ze specyfikacją). Jest odwrotna kompozytora, który może ponownie użyć danych ostatniej klatki do utrzymuj liczbę klatek na sekundę. Wizualnie wygląda to trochę twardo, ale duży różnica polega na tym, że przeglądarka wciąż reaguje na dane wejściowe użytkownika.

Podsumowanie

AnimationWorklet ma wiele aspektów i korzyści, jakie oferuje w internecie. Oczywistymi korzyściami jest większa kontrola nad animacjami i nowe sposoby animacje, które zapewniają nowy poziom jakości wizualnej w internecie. Interfejsy API jest również bardziej odporna na zacinanie, a jednocześnie dostęp do wszystkich nowych funkcji jednocześnie.

Worklet animacji jest w wersji Canary. Naszym celem jest uruchomienie wersji próbnej Chrome 71. Z niecierpliwością czekamy na Wasze nowe doświadczenia w internecie i usłyszenia o tym, co możemy poprawić. Dostępna jest też kod polyfill , który obejmuje ten sam interfejs API, ale nie obejmuje izolacji wydajności.

Pamiętaj, że przejścia CSS i animacje CSS są nadal prawidłowe i może być znacznie prostsza w przypadku podstawowych animacji. Jeśli jednak chcesz Możesz utworzyć aplikację AnimationWorklet.