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 2022Lubię 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:
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}67.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'23export const usePrefersReducedMotion = () => {4 const [hasReducedMotionPreference, setHasReducedMotionPreference] =5 useState(false)67 useEffect(() => {8 const mediaQueryList = window.matchMedia('(prefers-reduced-motion: reduce)')9 const initialPreference = mediaQueryList.matches10 setHasReducedMotionPreference(initialPreference)1112 const listener = (event) => {13 setHasReducedMotionPreference(event.matches)14 }15 mediaQueryList.addEventListener('change', listener)16 return () => {17 mediaQueryList.removeEventListener('change', listener)18 }19 }, [])2021 return hasReducedMotionPreference22}
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 import34const Landing = () => {5 const { t } = useTranslation('components/landing')6 const hasReducedMotionPreference = usePrefersReducedMotion()78 return (9 <Hero>10 <Tile as="header">11 <H1 aria-label={t('aria')}>12 {hasReducedMotionPreference ? (13 t('typewriter.create')14 ) : (15 <Typewriter16 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}2930export 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ą.