1 września 2022 7 min. czytania

Dostępne animacje w React

Czyli jak nie kręcić swoimi użytkownikami (jak winylem). Niektóre animacje mogą powodować problemy u użytkoników. Zadbamy o nich i sprawimy, że nieistotne animacje będą opcjonalne.

Zaktualizowano: 1 września 2022
Połowa winyla na białym tle
Zdjęcie autorstwa Miguel Á. Padriñán

Lubię minimalizm. Lubię minimalizm w moim pokoju, na moim ekranie i - jak widzisz - na moim blogu. Ale nawet dla mnie, niektóre wodotryski są fajne. Myślę, że przemyślana animacja może poprawić wrażenia z użytkowania. Możesz znaleźć subtelne przykłady na mojej stronie. Niektórzy projektanci albo programiści mogą się ze mną nie zgodzić. Efektywność może być dla nich najwyższym priorytetem. Ale czasami to nie jest nawet kwestia preferencji.

16 grudnia 1997 roku, epizod Pokemonów wyświetlony w telewizji w Tokio spowodował migreny, zniekształcenia wizualne, mdłości i drgawki u ponad 500 dzieci. Nie potrzebujesz jednak jaskrawego serialu albo gry wideo, aby wywołać te reakcje. Migające światło, wzorce z linii, kraty, szachownice i inne konfiguracje mogą sprowokować drgawki wywołane światłem [1].

Istnieje cała klasyfikacja różnych zaburzeń przedsionkowych. To ten system odpowiedzialny za naszą orientację przestrzenną. Jego praca, w przypadku niektórych ludzi, może zostać zaburzona przez zewnętrzne wyzwalacze. Wizualnie wywołane zawroty głowy mogą być sprowokowane przez złożone, zniekształcone lub ruszające się wizualne bodźce o dużej powierzchni. Ludzie mogą odczuwać iluzję poruszania się bez właściwego poruszania się [2].

Zatem, migający ekran lub ruch (czyli w zasadzie animacja) może wywołać poważne, fizyczne reakcje u niektórych użytkowników. Niektóre z nich brzmią jak reakcje na karuzelę dla wielu ludzi - w tym dla mnie. Dlatego nie zabieraj swojego użytkownika na mimowolną wycieczkę do parku rozrywki. Nie kręć nim jak winylem.*

*W angielskiej wersji nawiązałem w ten sposób do znanego utworu z lat 80, "You Spin Me Round (Like a Record)".

Opcje redukcji ruchu

Twórcy systemów operacyjnych zatroszczyli się o tych ludzi. Każdy duży, mobilny i komputerowy system operacyjny (macOS, Linux, Windows 7+, iOS, Android) ma jakieś ustawienie do redukcji ruchu. Zaznaczenie tej opcji wyłącza systemowe animacje.

  • W Windows 10: Ustawienia > Ułatwienia dostępu > Wyświetlacz > Pokaż animacje w systemie Windows
  • W Ubuntu: Terminal > gsettings set org.gnome.desktop.interface enable-animations false
  • W macOS: Preferencje systemowe > Dostępność > Ekran > Redukuj ruch
  • W iOS: Ustawienia > Dostępność > Ruch > Redukuj ruch
  • W Android: Ustawienia > Dostępność > Usuń animacje

Ta opcja jest także udostępniona użytkownikom sieci. Z wykorzystaniem prefers-reduced-motion CSS media query, możesz wykryć czy użytkownik preferuje animacje. Działa to podobnie do prefers-color-scheme media query. Ale to nie zadziała, jeżeli wsparcie przeglądarek jest kiepskie. Sprawdźmy:

Wykres ze strony caniuse.com prezentujący wsparcie przeglądarek dla funkcji prefers-reduced-motion (94,41%)

Wsparcie jest dobre, dlatego spróbuję uczynić moją stronę bardziej dostępną!

Wykorzystanie

Moja strona jest minimalistyczna, dlatego jest tu niewiele animacji. Ale jest jeden, dobry przykład - efekt maszyny do pisania na mojej stronie głównej. Stworzyłem komponent maszyny do pisania, który przyjmuje wyrazy i symuluje zapisywanie ich.

Dokumentacja Mdn definiuje: “funkcja prefers-reduced-motion media query jest wykorzystywana do wykrywania, czy użytkownik zażądał od systemu minimalizacji zbędnych animacji”. Pomimo że wspomniana animacja elegancko przedstawia to co zwykle robię - piszę na klawiaturze - nie jest niezbędna. Stworzyłem ją głównie dla estetyki. Dlatego jest dobrym kandydatem do ograniczenia ruchu.

CSS media query

Ten fragment kodu jest odpowiedzialny za migający kursor. Co sekundę, przezroczystość kreski pionowej oscyluje pomiędzy 0 i 1.

CSS
1@keyframes blink {
2 50% {
3 opacity: 0;
4 }
5}
6
7.cursor:after {
8 content: '|';
9 animation: blink 1s step-start infinite;
10 color: var(--color-primary-base);
11}

Możemy wyłączyć tę animację korzystając z naszej funkcji media query.

CSS
1@media (prefers-reduced-motion: reduce) {
2 .cursor:after {
3 content: '';
4 animation-name: none;
5 }
6}

Jeżeli użytkownik preferuje ograniczenie ruchu, animacja nie będzie odtworzona i nie będzie znaku kreski pionowej.

React

Ale w tej animacji jest jeszcze druga część - pisanie. Ta część jest zaimplementowana w języku JavaScript. Najprawdopodobniej istnieje jakiś kombinatorski sposób na zaimplementowanie czegoś podobnego w czystym CSS. Nawet próbowałem kilka rozwiązań, ale żadne mnie nie satysfakcjonowało. Pomimo że CSS się rozwija, ma swoje ograniczenia. Istnieją też inne typy animacji, które po prostu nie mogą być zrobione w CSS.

W innej części mojej strony wykorzystuję window.matchMedia(), aby wykryć preferencje systemowe dotyczące koloru motywu. Używając tej metody, możemy uzyskać dostęp do wartości media queries z poziomu JS. Użyję jej, żeby sprawdzić preferencje ruchu.

JSX
1const hasReducedMotionPreference = window.matchMedia(
2 '(prefers-reduced-motion: reduce)'
3).matches // Wartość Bool - żależy od ustawień systemowych

Teraz mogę wykorzystać tę informację w niestandardowym haku.

JSX
1import { useState, useEffect } from 'react'
2
3export const usePrefersReducedMotion = () => {
4 const [hasReducedMotionPreference, setHasReducedMotionPreference] =
5 useState(false)
6
7 useEffect(() => {
8 const mediaQueryList = window.matchMedia('(prefers-reduced-motion: reduce)')
9 const initialPreference = mediaQueryList.matches
10 setHasReducedMotionPreference(initialPreference)
11
12 const listener = (event) => {
13 setHasReducedMotionPreference(event.matches)
14 }
15 mediaQueryList.addEventListener('change', listener)
16 return () => {
17 mediaQueryList.removeEventListener('change', listener)
18 }
19 }, [])
20
21 return hasReducedMotionPreference
22}

Domyślną wartością tego haka jest false - nie znamy jeszcze preferencji użytkownika. Możesz też ustawić ją jako true - domyślnie animacje będą wyłączone. W ten sposób funkcja jest bardziej dostępna, ale powodowała przemieszczanie się mojego layoutu, dlatego na razie zostawię wartość false. Następnie hak useEffect() sprawdza preferencje systemu operacyjnego. W zależności od ustawień, zmienna hasReducedMotionPreference jest odpowiednio zaktualizowana. Zmienna ta jest zwracana przez funkcję. Event listener sprawia, że zmienna jest aktualna z ustawieniami systemowymi. Możesz sprawdzić go w akcji. Zmień ustawienia ruchu na swoim systemie podczas obserwacji mojej strony głównej.

Możemy wykorzystywać nasz hak w każdym komponencie, który używa animacji. Wykorzystam go w moim komponencie landing, gdzie zlokalizowany jest efekt maszyny do pisania.

JSX
1import { usePrefersReducedMotion } from '../hooks/usePrefersReducedMotion'
2// Pozostałe deklaracje import
3
4const Landing = () => {
5 const { t } = useTranslation('components/landing')
6 const hasReducedMotionPreference = usePrefersReducedMotion()
7
8 return (
9 <Hero>
10 <Tile as="header">
11 <H1 aria-label={t('aria')}>
12 {hasReducedMotionPreference ? (
13 t('typewriter.create')
14 ) : (
15 <Typewriter
16 strings={[
17 t('typewriter.design'),
18 t('typewriter.code'),
19 t('typewriter.write'),
20 t('typewriter.create')
21 ]}
22 ></Typewriter>
23 )}
24 </H1>
25 </Tile>
26 </Hero>
27 )
28}
29
30export default Landing

Wykorzystanie go nie jest skomplikowane. Importuje niestandardowy hak i używam go, aby zwrócić preferencje dotyczące animacji. Następnie wykorzystuję operator trójskładnikowy. Jeżeli opcja redukcji ruchu jest włączona, statyczny wyraz jest renderowany. W przeciwnym wypadku efekt maszyny do pisania jest odtworzony.

Wyłączenie wszystkich animacji

“No dobra, ale to wygląda jak sporo pracy. Czy nie możemy wyłączyć wszystkich animacji jednym fragmentem kodu, w stylu resetu CSS?”. W zasadzie możemy. Przeglądając sieć, znalazłem taki fragment kodu:

CSS
1* {
2 /*CSS transitions*/
3 -o-transition-property: none !important;
4 -moz-transition-property: none !important;
5 -ms-transition-property: none !important;
6 -webkit-transition-property: none !important;
7 transition-property: none !important;
8 /*CSS transforms*/
9 -o-transform: none !important;
10 -moz-transform: none !important;
11 -ms-transform: none !important;
12 -webkit-transform: none !important;
13 transform: none !important;
14 /*CSS animations*/
15 -webkit-animation: none !important;
16 -moz-animation: none !important;
17 -o-animation: none !important;
18 -ms-animation: none !important;
19 animation: none !important;
20}

Ale myślę, że to nie jest dobry pomysł. Po pierwsze, zachęcam cię do zminimalizowania zapisu !important. Wykorzystując go zbyt często możesz zrobić bałagan ze specyficznością w języku CSS. Po drugie, nie wszystkie animacje są w języku CSS. Ten fragment kodu nie zadziała w przypadku animacji canvas, SVG i innych podobnych. No i redukcja ruchu nie oznacza braku animacji. Niektóre animacje mogą poprawić wrażenia z użytkowania. Czasami linia pomiędzy niezbędnymi i zbędnymi animacjami jest rozmyta, ale już samo zastanawianie się nad problemem jest dobrym startem. Pod koniec pisania, znalazłem ten artykuł - zawiera przykłady problematycznych animacji, więc może się przyda.

Drobiazgowe przygotowanie animacji i ich zredukowanych wersji może być męczące. Ale - jak zawsze - jest warte swojej ceny. Uczynienie twojej strony bardziej dostępną, może oszczędzić niektórym osobom kłopotu, zatem więcej osób może cieszyć się twoją zawartością.

Newsletter, który rozpala ciekawość💡

Subskrybuj mój newsletter, aby otrzymywać comiesięczną dawkę:

  • Nowości, przykładów, inspiracji ze świata front-end, programowania i designu
  • Teorii naukowych i sceptycyzmu
  • Moich ulubionych źródeł, idei, narzędzi i innych interesujących linków
Nie jestem nigeryjskim księciem, aby oferować ci okazje. Nie wysyłam spamu. Anuluj kiedy chcesz.

Pozostań ciekawy. Przeczytaj więcej

Zdjęcie zmiennych CSS w edytorze Visual Studio Code14 września 20227 min. czytania

Konwertowanie tokenów projektowych na zmienne CSS z Node.js

Konwertowanie tokenów projektowych jest procesem podatnym na błędy - przekonałem się o tym na własnej skórze. Dlatego stworzyłem prosty skrypt dla środowiska Node.js, który pomoże mi z tym zadaniem.

Czytaj wpis
Pięć metalowych kół zębatych na czarnym tle23 września 202210 min. czytania

Gatsby z Netlify CMS

W tym wpisie przyjrzymy się bliżej Netlify CMS. Jest to przykład CMSa nowego typu, który jest oparty o Git. Zintegrujemy go z przykładowym projektem Gatsby.

Czytaj wpis
Znak zapytania ułożony z kropek na żółtym tle23 lipca 20226 min. czytania

To jest natywny JavaScript? Prawda??

Moje pierwsze spotkanie z optional chaining i nullish coalescing operator.

Czytaj wpis