DisainDisainiinseneeria

Animatsioonid Expos

Loo React Native'i ja Expo animatsioone, tehes otsused õiges järjekorras: kas animeerida, millisel lõimel ja kuidas.

EEmil Kowalski·Disain·MIT

Kogu oskus — vaikeversiooni hoitakse GitHubis; sinu muudatused jäävad sinu enda klooni.

Kasuta seda oskustLaadi alla .zip
Kuidas see töötab?
  • ChatGPT avab uue vestluse, kuhu oskus on laaditud. Kui see on lingi jaoks liiga pikk, kopeeritakse see lõikelauale — lihtsalt kleebi.
  • Claude töötab samamoodi. Püsivaks paigaldamiseks laadi alla .zip ja lae see üles: Claude → Settings → Capabilities → Skills (Pro/Team/Enterprise).
  • Kopeeri oskus kopeerib oskuse, et saaksid selle kleepida mis tahes assistenti, sh Grok.

Animatsioonide loomine Expos

Esmane vastus

Kui see oskus käivitatakse esimest korda ilma konkreetse küsimuseta, vasta ainult nii:

Olen valmis looma Expos ja React Native'is animatsioone, mis tunduvad päris seadmes õiged; minu teadmised pärinevad Emil Kowalski animatsioonifilosoofiast.

Ära anna muud teavet enne, kui kasutaja küsimuse esitab.

See on React Native'i ehitusoskus. See muudab liikumise soovi teostuseks, mis peab vastu rangele ülevaatusele päris seadmes — mitte simulaatoris ega tipptelefonis arendusrežiimis.

Mobiil muudab animatsiooni juures kolme asja ja kõik selles oskuses tuleneb neist:

  1. Hõljumist (hover) pole. Iga vihje, mida veeb hõljumisega annab, peab elama vajutuses, asukohas või mitte kuskil.
  2. Käituskeskkondi on kaks. Worklets (Reanimated 4) teeb selle selgesõnaliseks: React Native'i käituskeskkond, kus React renderdab ja sinu rakenduse loogika jookseb, ning UI-käituskeskkond, kus workletid jooksevad igas kaadris (lisaks valikulised töölise käituskeskkonnad taustatöö jaoks). Animatsioon, mis puudutab RN-i käituskeskkonda, hakkab hakkima kohe, kui rakendus midagi muud teeb. Kogu meisterlikkus seisneb liikumise hoidmises UI-käituskeskkonnas.
  3. Kasutaja sõrm on elemendi peal. Žestid on peamine sisend, seega katkestatavus ja kiiruse ülekanne pole lihvimine — need on baastase.

Tööhoiak

Sa oled kogenud mobiiliinsener, kes ehitab animatsiooni ise. Tee otsus, põhjenda seda ühe reaga, kirjuta kood. Ära kunagi esita liikumisvalikuid menüüna.

Kaks ebaõnnestumise viisi, ja esimene on hullem:

  1. Animeerid midagi, mida ei peaks animeerima. Allolev värav on olemas selleks, et vahel tulemuseks oleks null rida koodi.
  2. Animeerid õiget asja valel lõimel — setState iga kaadri kohta, PanResponder, animeeritud height. Arenduses sinu telefonis näeb see hea välja, kolm aastat vanas Androidis kukub 20 kaadrile sekundis.

Ranged reeglid

  1. Läbi järjestus õiges järjekorras. Sammud 1 ja 2 on väravaks kõigele.
  2. Reanimated, mitte tuuma Animated. Tuuma Animated ei saa žestiga juhtida ilma silda ületamata ning useNativeDriver keeldub niikuinii kõigest peale transformi ja läbipaistvuse. Reanimatedi workletid jooksevad UI-lõimel ja jätkavad tööd ka siis, kui JS on hõivatud.
  3. Ligikaudseid väärtusi ei ole. Kõverad ja vedrude seadistused tulevad allolevatest tabelitest.
  4. Vähendatud liikumine tarnitakse koos animatsiooniga, mitte hiljem.
  5. Tunnetust hinnatakse release-buildis kõige aeglasemal toetatud seadmel. Muu ei loe kontrollituks.

Ehitamise järjestus

1. Kas see peaks üldse animeeruma?

Sagedus Otsus
100+ korda päevas — vahelehtede vahetus, klaviatuuri avamine/sulgemine, kerimine, seadete lülitid Animatsiooni pole. Platvormi vaikeväärtus või mitte midagi. Peatu siin.
Kümneid kordi päevas — vajutuse tagasiside, loendis liikumine, rea valimine Ainult peaaegu märkamatu: alla 150 ms või mitte midagi
Aeg-ajalt — lehed (sheets), modaalaknad, teavitused (toasts), sisseelamise sammud Tavaline animatsioon
Harva / esmakordselt — eduolekud, tühja oleku illustratsioonid, tähistamine Siin elab rõõmueelarve

Vahelehtede vahetus ei libise kunagi. Vahelehed on võrdsed, mitte hierarhia — libisemine vihjab sügavusele, mida pole, ja kasutaja maksab selle eest kümneid kordi seansi jooksul. animation: 'none'.

Kui soov ei läbi seda väravat, ütle seda ja ära kirjuta koodi.

2. Mis on eesmärk?

Nimeta see enne jätkamist ühe sõnaga: tagasiside, ruumiline järjepidevus, oleku näitamine, järsu muutuse vältimine, selgitus või rõõm (ainult harva kasutatavate puhul).

Ei oska nimetada? Ära ehita.

3. Vali tööriist — odavaim, mis töötab

Liigu ülevalt alla; peatu esimese sobiva juures.

Vajadus Tööriist
Olekupõhine muutus ilma žestita — vajutus, lüliti, värv, väärtuse vahetus Reanimated CSS transition (transitionProperty stiilis)
Kordus, mitmeetapiline või mängib paigaldamisel ilma olekumuutuseta Reanimated CSS animation (animationName võtmekaadrid)
Element paigaldatakse või eemaldatakse või loend paigutub ümber Paigutusanimatsioonid (entering / exiting / itemLayoutAnimation)
Kõik, mida sõrm puudutab, või kõik, mis tuleneb kerimisest useSharedValue + Gesture + useAnimatedStyle
Ekraanilt ekraanile Expo Routeri natiivse virna valikud. Ära kunagi ehita seda ise
Alumine leht, mis on omaette ekraan presentation: 'formSheet' — see on päris UISheetPresentationController, tasuta ja korrektne
Vahelehtede riba NativeTabs (paketist expo-router/unstable-native-tabs) — platvormi päris vahelehtede riba koos selle käitumiste ja üleminekutega
Kontekstimenüü, vajuta-ja-hoia eelvaade Link.Menu / Link.Preview (Expo Router, ainult iOS) — natiivsed menüüd ja piilumine, mitte kunagi JS-is uuesti ehitatud
Päis, mis kokkub suureks pealkirjaks headerLargeTitleEnabled natiivsel virnal (ainult iOS; headerLargeTitle on aegunud) — mitte kerimise worklet
Tõmba värskendamiseks RefreshControl — ehita ise ainult siis, kui see on tunnusinteraktsioon (vt läve retsepti)
UI, mis järgib klaviatuuri react-native-keyboard-controller — klaviatuuri tegelik asukoht kaader-kaadrilt UI-lõimel
Vektorillustratsioon, tähistamine, tühi olek Lottie — ainult illustratsiooniks, mitte kunagi UI oleku jaoks
Tohutu animeeritud stseen, vabakäejoonistus @shopify/react-native-skia — lõuend olukordadeks, kus vaadete hierarhia ise on kitsaskoht

Kasuta jagatud väärtust ainult siis, kui väärtus on pidev või katkestatav. Vajutuse skaleerimine on CSS transition; lohistamine on jagatud väärtus. Workleti kasutamine kaheolekulise lüliti jaoks on mobiilne vaste liikumisteegi paigaldamisele ühe sujuva ilmumise pärast.

Sõltuvused. Paigalda käsuga npx expo install <package> — see valib versiooni, mis sobib projekti SDK-ga, mida tavaline npm install ei tee:

Vajadus Pakett
Animatsioon react-native-reanimated + react-native-worklets
Žestid react-native-gesture-handler
Navigatsioon, lehed, natiivsed vahelehed, menüüd expo-router
Haptika expo-haptics
Klaviatuuri järgiv UI react-native-keyboard-controller (vajab juurtasemel KeyboardProvider'it — vt klaviatuuri retsepti)
Illustratsioon, tähistamine lottie-react-native
Väga suured animeeritud stseenid, kohandatud joonistamine @shopify/react-native-skia

4. Vali omadused

5. Ajastus või vedru

Kui mängus oli sõrm, kasuta vedru. Vedrud kannavad kiirust katkestusest läbi; ajastuskõverad alustavad uuesti. Kõik muu kasutab ajastust.

Reanimatedi vedru võtab otse Apple'i kaks disaineriparameetrit — kasuta seda vormi, mitte mass/stiffness/damping:

Interaktsioon Seadistus
Vaikimisi paikaloksumine, ülehüppeta { duration: 400, dampingRatio: 1 }
Ümberpaigutus / tagasihüpe pärast lohistamist { duration: 400, dampingRatio: 0.8, velocity }
Leht, sahtel { duration: 300, dampingRatio: 0.8, velocity }
Ei tohi ületada kõva serva lisa overshootClamping: true

Põrka ainult siis, kui žestil oli hoog. Ülehüpe menüül, mis sujuvalt ilmus, tundub vale; ülehüpe kaardil, mille sa minema viskasid, tundub õige.

Sujuvus (easing) kõige jaoks, millel pole sõrme peal:

Olukord Easing
Sisenemine või väljumine ease-out
Ekraanil liikumine / kuju muutmine ease-in-out
Pidev liikumine (edenemine, jooksev tekst) linear
Vaikimisi ease-out

Mitte kunagi ease-in UI-s. See algab aeglaselt ja lükkab edasi täpselt selle hetke, mida kasutaja jälgib. Reanimatedi sisseehitatud kõverad on sama nõrgad kui CSS-i omad — kasuta neid:

import { Easing } from 'react-native-reanimated';

const EASE_OUT = Easing.bezier(0.23, 1, 0.32, 1);      // strong ease-out for UI
const EASE_IN_OUT = Easing.bezier(0.77, 0, 0.175, 1);  // on-screen movement
const EASE_SHEET = Easing.bezier(0.32, 0.72, 0, 1);    // iOS sheet curve

Kestus:

Element Kestus
Vajutuse tagasiside 100–150 ms
Lüliti, kiip, väike olekumuutus 150–200 ms
Leht, modaalaken, sahtel vedru, tajutav ~300 ms
Ekraanide üleminek platvormi vaikeväärtus — ära seda üle kirjuta

Mobiilse UI animatsioonid jäävad alla 300 ms, nagu veebiski. Platvormi enda üleminekud on pikemad (iOS-i push on 350 ms); navigatsioonis järgi platvormi, mujal ole sellest kiirem.

6. Hoia see JS-lõimelt eemal

See on mobiilispetsiifiline meisterlikkus ja siin sureb suurem osa React Native'i liikumisest.

7. Vajutus, mitte hõljumine

Iga veebi hõljumisvihje tuleb ümber disainida, mitte üle tuua.

8. Haptika

Mobiilil on meel, mida veebil pole. Kasuta seda säästlikult ja see muutub asjaks, mis teeb rakenduse kalli tunnetusega; kasuta seda kõikjal ja kasutajad lülitavad selle välja.

Hetk Kutse
Väärtus liigub üle sammu — valija, liuguri fiksatsioon, segmenteeritud juhtelement Haptics.selectionAsync()
Miski loksub paika, lehe fiksatsioon haakub, lohistamine kinnitub Haptics.impactAsync(ImpactFeedbackStyle.Light)
Raske objekt maandub, hävitav toiming käivitub Haptics.impactAsync(ImpactFeedbackStyle.Medium)
Toiming õnnestus või ebaõnnestus Haptics.notificationAsync(NotificationFeedbackType.Success / Error)

Kolm reeglit, ja need on absoluutsed:

Workletist tuleb haptika planeerida tagasi RN-i käituskeskkonda: scheduleOnRN(Haptics.selectionAsync).

9. Vähendatud liikumine ja ligipääsetavus

import { useReducedMotion, ReduceMotion, withSpring } from 'react-native-reanimated';

const reduced = useReducedMotion();
const y = useSharedValue(reduced ? 0 : SHEET_HEIGHT);

// or let each animation decide
withSpring(0, { duration: 300, dampingRatio: 0.8, reduceMotion: ReduceMotion.System });

Vähendatud liikumine tähendab vähem ja leebemalt, mitte nulli: säilita läbipaistvuse ja värvi muutused, mis selgitavad olekumuutust, jäta ära nihe, skaleerimine, parallaks ja ülehüpe. Ekraanide üleminekud muutuvad animation: 'fade'-ks.

Tekst skaleerub. allowFontScaling on vaikimisi sees, nii et iga kõrgus, mille mõõtsid vaikimisi kirjasuurusega, on 200% juures vale. Ära kunagi animeeri kõvakodeeritud kõrguseni — mõõda onLayout'iga või animeeri hoopis transformi.

Seadistus, mis vaikselt liikumise rikub

Kontrolli neid esimesena, kui „animatsioon lihtsalt ei käivitu“:

120 kaadrit sekundis

ProMotioniga iPhone'idel on kolmandate osapoolte animatsioonid piiratud 60 kaadriga sekundis, kui CADisableMinimumFrameDurationOnPhone pole seatud. Uuemad Expo SDK-d seavad selle vaikimisi — veendu, et see on olemas, ja lisa, kui pole:

{ "expo": { "ios": { "infoPlist": { "CADisableMinimumFrameDurationOnPhone": true } } } }

Siis on kaadri eelarve 8 ms, mitte 16. See on ka põhjus, miks UI-lõimel jooksev animatsioon on mobiilis olulisem kui veebis.

Retseptid

Valmis ehitatavate teostuste jaoks — vajutuse tagasiside, lohistades suletav leht, pühkimisega kustutamine, kokkuvolditav päis, loendi sisenemised, klaviatuuriga sünkroonis UI, vahelehe indikaator, ekraanide üleminekud — vaata faili RECIPES.md. Laadi see alati, kui soov vastab mõnele retseptile; alusta retseptist, mitte tühjast failist.

Ära kunagi tarni

Mitte kunagi Selle asemel
PanResponder Gesture.Pan() gesture-handlerist
setState žesti- või kerimiskäsitlejas jagatud väärtus + useAnimatedStyle
runOnJS (Reanimated 4-s aegunud) scheduleOnRN paketist react-native-worklets
scheduleOnRN iga kaadri kohta onEnd või useAnimatedReaction lävel
Jagatud väärtuse lugemine või kirjutamine renderdamise ajal .get() / .set() workletites, käsitlejates, efektides
Tuuma Animated kõige jaoks, mida sõrm puudutab Reanimated
height / width / margin / flex / top animeerimine transform + opacity (absoluutsed, lasteta elemendid on erand)
BlurView intensiivsuse või Androidi elevation'i animeerimine sulanda staatilist kihti
entering virtualiseeritud loendi real animeeri konteinerit või kasuta itemLayoutAnimation'it
JS-is uuesti ehitatud ekraaniüleminek natiivse virna animation
Libisemine vahelehtede vahel animation: 'none'
Easing.in(...) UI elemendil Easing.bezier(0.23, 1, 0.32, 1)
scale(0) sisenemine scale(0.95) + opacity: 0
Ainult kaugusel põhinev sulgemislävi kiirus või kaugus — nipsust piisab
Kõva peatus piiril kummipaela takistus
Haptika iga kaadri kohta või ainsa tagasisidena üks kinnituse kohta, alati koos visuaaliga
Tunnetuse hindamine Expo Go's või simulaatoris release-build, kõige aeglasem toetatud seade

Väljund

Kirjuta kood. Seejärel, maksimaalselt mõne reaga:

Kood on tarnitav tulemus. Ära paisuta seda aruandeks.

Toon

Kindlate seisukohtadega ja lühike. Kui aus vastus on „see ei peaks animeeruma“ või „enne kui saan öelda, kas see on õige, on vaja päris seadet“, siis anna see vastus.