Reanimated — silliq animatsiyalar

~20 daq · React Native · Animatsiya va UX

Muammodan boshlaylik

O'rnatilgan Animated ning cheklovlari tez seziladi:

  • useNativeDriver faqat opacity va transform — kenglik, rang, balandlik animatsiyasi sekin
  • Animatsiya davomida mantiq (masalan, "qiymat 0.5 dan oshsa — tebranish") yozib bo'lmaydi
  • Imo-ishoralar bilan (barmoq orqasidan ergashish) ishlash noqulay
  • Kod — ko'p useRef, interpolate, .start()

React Native Reanimated — professional ilovalarda standart. Animatsiyalar to'liq UI thread da ishlaydi, har qanday stil xususiyati bilan, va kod ancha qisqa.


O'rnatish

Expo shablonida allaqachon bor. Bo'lmasa:

npx expo install react-native-reanimated react-native-worklets

Expo Go da ishlaydi.


Asosiy tushunchalar

import Animated, { useSharedValue, useAnimatedStyle, withTiming, withSpring } from 'react-native-reanimated';

export function Quti() {
  const kenglik = useSharedValue(100);

  const stil = useAnimatedStyle(() => ({
    width: kenglik.value,
  }));

  return (
    <>
      <Animated.View style={[{ height: 100, backgroundColor: '#0284c7', borderRadius: 12 }, stil]} />
      <Tugma matn="Kattalashtir" onPress={() => { kenglik.value = withSpring(kenglik.value + 50); }} />
    </>
  );
}
Element Vazifasi
useSharedValue(x) Animatsiya qiymati — JS va UI thread o'rtasida umumiy
.value O'qish va yozish
useAnimatedStyle(() => ({...})) Qiymatdan stil hisoblovchi funksiya — UI thread da ishlaydi
withTiming, withSpring Qiymatni animatsiya bilan o'zgartirish
Animated.View react-native-reanimated dan

kenglik.value = withSpring(150) — "150 ga prujina bilan bor". .start() yo'q, useNativeDriver yo'q, va width ham silliq.

[!WARNING] .value ni render paytida o'qimang (<Text>{kenglik.value}</Text>) — qiymat yangilanmaydi va ogohlantirish chiqadi. .value — faqat useAnimatedStyle ichida va hodisa funksiyalarida.


Animatsiya funksiyalari

import { withTiming, withSpring, withDelay, withSequence, withRepeat, Easing } from 'react-native-reanimated';

x.value = withTiming(1, { duration: 300, easing: Easing.out(Easing.cubic) });
x.value = withSpring(1, { damping: 12, stiffness: 150 });
x.value = withDelay(200, withTiming(1));
x.value = withSequence(withTiming(1.2, { duration: 100 }), withSpring(1));   // "puls"
x.value = withRepeat(withTiming(360, { duration: 1000 }), -1);               // cheksiz

Tugash hodisasi:

x.value = withTiming(0, { duration: 200 }, (tugadi) => {
  if (tugadi) runOnJS(yopish)();      // JS funksiyasini UI thread dan chaqirish
});

Misol: "Yoqtirish" yuragi

import Animated, { useSharedValue, useAnimatedStyle, withSequence, withSpring, withTiming } from 'react-native-reanimated';
import * as Haptics from 'expo-haptics';

const AnimatedIonicons = Animated.createAnimatedComponent(Ionicons);

export function Yurak({ yoqdi, almashtir }: { yoqdi: boolean; almashtir: () => void }) {
  const masshtab = useSharedValue(1);

  const stil = useAnimatedStyle(() => ({ transform: [{ scale: masshtab.value }] }));

  function bos() {
    masshtab.value = withSequence(withTiming(1.3, { duration: 120 }), withSpring(1, { damping: 6 }));
    Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
    almashtir();
  }

  return (
    <Pressable onPress={bos} hitSlop={10}>
      <AnimatedIonicons name={yoqdi ? 'heart' : 'heart-outline'} size={28} color={yoqdi ? '#ef4444' : '#64748b'} style={stil} />
    </Pressable>
  );
}

Kattalashib, prujina bilan qaytadi va tebranadi — Instagram dagi kabi.

createAnimatedComponent — istalgan komponentni animatsiyalanadigan qilish.


Layout animatsiyalari — eng kuchli imkoniyat

Element paydo bo'lishi, yo'qolishi va joyi o'zgarishini bitta xususiyat bilan:

import Animated, { FadeInDown, FadeOut, LinearTransition } from 'react-native-reanimated';

{royxat.map((v, i) => (
  <Animated.View
    key={v.id}
    entering={FadeInDown.delay(i * 50).springify()}   // paydo bo'lish
    exiting={FadeOut.duration(200)}                     // yo'qolish
    layout={LinearTransition.springify()}              // qolganlar silliq siljiydi
  >
    <VazifaKarta vazifa={v} />
  </Animated.View>
))}
  • entering — mount bo'lganda: FadeIn, FadeInDown, SlideInRight, ZoomIn, BounceIn...
  • exiting — unmount bo'lganda: FadeOut, SlideOutLeft, ZoomOut...
  • layout — joyi o'zgarganda (boshqa element o'chirildi)
  • .delay(), .duration(), .springify() — sozlash

Animated bilan bu o'nlab qator va murakkab mantiq bo'lardi. Reanimated da — uchta xususiyat. FlatList uchun — Animated.FlatList va itemLayoutAnimation.

Modal / bildirishnoma

{korinsin && (
  <Animated.View entering={SlideInDown.springify().damping(15)} exiting={SlideOutDown} style={styles.toast}>
    <Text>Savatga qo'shildi</Text>
  </Animated.View>
)}

Shartli render + entering / exiting — animatsiyali paydo bo'lish va yo'qolish.


Scroll bilan

import Animated, { useAnimatedScrollHandler, useSharedValue, useAnimatedStyle, interpolate, Extrapolation } from 'react-native-reanimated';

const y = useSharedValue(0);
const handler = useAnimatedScrollHandler((e) => { y.value = e.contentOffset.y; });

const sarlavhaStil = useAnimatedStyle(() => ({
  opacity: interpolate(y.value, [0, 100], [0, 1], Extrapolation.CLAMP),
  transform: [{ translateY: interpolate(y.value, [0, 100], [-10, 0], Extrapolation.CLAMP) }],
}));

const rasmStil = useAnimatedStyle(() => ({
  transform: [{ scale: interpolate(y.value, [-200, 0], [1.5, 1], Extrapolation.CLAMP) }],   // pastga tortilsa kattalashadi
}));

<Animated.ScrollView onScroll={handler} scrollEventThrottle={16}>
  <Animated.Image source={...} style={[{ width: '100%', height: 300 }, rasmStil]} />
</Animated.ScrollView>

Mahsulot sahifasidagi "parallax" rasm va scroll qilganda paydo bo'ladigan sarlavha.


Worklet — UI thread dagi funksiya

useAnimatedStyle, useAnimatedScrollHandler ichidagi funksiyalar — worklet: ular JavaScript thread da emas, UI thread da bajariladi. Shuning uchun:

  • Ular ichida oddiy JS funksiyalarni (holat yangilash, router.push, fetch) to'g'ridan-to'g'ri chaqirib bo'lmaydi — runOnJS(funksiya)(argumentlar) orqali
  • O'z worklet ingiz: funksiya boshida 'worklet'; direktivasi
function chegarala(v: number, min: number, max: number) {
  'worklet';
  return Math.min(Math.max(v, min), max);
}

Animated yoki Reanimated?

Animated Reanimated
O'rnatish O'rnatilgan Kutubxona (Expo da tayyor)
Native animatsiya Faqat opacity, transform Har qanday xususiyat
Layout animatsiyalari LayoutAnimation (cheklangan) entering, exiting, layout
Imo-ishoralar Noqulay Gesture Handler bilan ideal (46-dars)
Kod Ko'proq Qisqaroq

Yangi loyihalarda — Reanimated. Animated ni bilish — eski kod va oddiy holatlar uchun.


Nima uchun shunday?

Nima uchun animatsiya UI thread da ishlashi bunchalik muhim?

React Native ilovasida ikki asosiy thread bor: JavaScript (sizning mantiqingiz, React) va UI (ekranni chizish). JS thread band bo'lishi oddiy holat: katta JSON ni qayta ishlash, ro'yxatni chizish, navigatsiya. Agar animatsiya JS da hisoblansa — shu paytlarda u "qotadi". Reanimated animatsiya mantiqini butunlay UI thread ga ko'chiradi: JS band bo'lsa ham, barmoq ostidagi element 60 (yoki 120) kadr/soniyada silliq harakatlanadi. Foydalanuvchi aynan shuni "sifatli ilova" deb his qiladi.


Xulosa

Eslab qoling:

  • useSharedValue + useAnimatedStyle + Animated.View
  • x.value = withTiming / withSpring / withSequence / withRepeat(...)
  • Har qanday stil xususiyati silliq — UI thread da
  • .value ni render da o'qimang
  • Layout animatsiyalari: entering, exiting, layout — eng kam kod, eng katta effekt
  • useAnimatedScrollHandler + interpolate — scroll effektlari
  • Worklet lar ichidan JS ga — runOnJS
  • Yangi loyihalarda — Reanimated

Keyingi dars

46-dars: Gesture Handler — surish, tortish, swipe-to-delete