Hüseyin DOLHüseyin DOL
Reanimated ve Akışkan 60FPS Animasyonlar
Mobile

Reanimated ve Akışkan 60FPS Animasyonlar

UI/UX standartlarını çok yukarılara çekmek için JS thread dışına çıkarılan ve mikro-animasyonları ku...

Hüseyin DOL
Hüseyin DOL
5 dk okuma

Bu makale Mobile alanındaki deneyimlerimi ve yazılım geliştirme metodolojimi aktarmaktadır.

Genel Bakış

UI/UX standartlarını çok yukarılara çekmek için JS thread dışına çıkarılan ve mikro-animasyonları kusursuz yöneten Reanimated 3 implementasyonlarımız.

Profesyonel mobil ürünleri rakiplerinden ayırt eden şey işlevi kadar sunduğu mikro-etkileşimler (Micro-animations) ve pürüzsüz geri bildirim dokularıdır. Ancak React Native JS ipliği animasyonlar için çok yavaştır.

JS Thread vs UI Thread: Neden Animated API Yetmez?

React Native'in yerleşik Animated API'si her frame'de JS thread ile UI thread arasında bridge üzerinden iletişim kurar. Bu köprü geçişi ~5ms gecikme yaratır ve karmaşık animasyonlarda frame drop kaçınılmazdır. Reanimated 3 ise animasyon mantığını tamamen UI thread'ine (native taraf) taşıyarak bu darboğazı ortadan kaldırır.

[Animated API]
JS Thread → Bridge → UI Thread (her frame'de köprü geçişi = jank)

[Reanimated 3]
Worklet → UI Thread (doğrudan native çalışma = 60FPS)

Shared Values ve Worklet'ler

Reanimated'in temel yapı taşı useSharedValue ve useAnimatedStyle hook'larıdır. Shared value'lar UI thread'de yaşar ve JS thread'i bloklamadan animasyonları yönetir.

import Animated, {
  useSharedValue,
  useAnimatedStyle,
  withSpring,
  withTiming,
} from 'react-native-reanimated'
 
function AnimatedCard({ onPress }: Props) {
  const scale = useSharedValue(1)
  const opacity = useSharedValue(1)
 
  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ scale: scale.value }],
    opacity: opacity.value,
  }))
 
  const handlePressIn = () => {
    scale.value = withSpring(0.95, { damping: 15, stiffness: 150 })
    opacity.value = withTiming(0.8, { duration: 100 })
  }
 
  const handlePressOut = () => {
    scale.value = withSpring(1, { damping: 15, stiffness: 150 })
    opacity.value = withTiming(1, { duration: 100 })
  }
 
  return (
    <Pressable
      onPressIn={handlePressIn}
      onPressOut={handlePressOut}
      onPress={onPress}
    >
      <Animated.View style={[styles.card, animatedStyle]}>
        {/* Card content */}
      </Animated.View>
    </Pressable>
  )
}

Gesture Handler Entegrasyonu

react-native-gesture-handler v2 ile Reanimated'i birleştirerek dokunma, sürükleme ve fırlatma hareketlerini 60FPS'de işliyoruz. Swipe-to-delete gibi etkileşimler artık native hissiyatla çalışıyor.

import { Gesture, GestureDetector } from 'react-native-gesture-handler'
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withSpring,
  runOnJS,
} from 'react-native-reanimated'
 
function SwipeableRow({ onDelete, children }: Props) {
  const translateX = useSharedValue(0)
 
  const panGesture = Gesture.Pan()
    .onUpdate(event => {
      translateX.value = Math.min(0, event.translationX)
    })
    .onEnd(() => {
      if (translateX.value < -120) {
        translateX.value = withSpring(-200)
        runOnJS(onDelete)()
      } else {
        translateX.value = withSpring(0)
      }
    })
 
  const rowStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: translateX.value }],
  }))
 
  return (
    <GestureDetector gesture={panGesture}>
      <Animated.View style={rowStyle}>{children}</Animated.View>
    </GestureDetector>
  )
}

Layout Animations: Entering ve Exiting

Reanimated 3'ün layout animation API'si ile liste elemanlarının eklenmesi ve silinmesi sırasında otomatik geçiş animasyonları uyguluyoruz. Tek satır kod ile profesyonel sonuçlar elde ediliyor.

import Animated, {
  FadeInDown,
  FadeOutLeft,
  LinearTransition,
} from 'react-native-reanimated'
 
function NotificationList({ items }: Props) {
  return (
    <Animated.FlatList
      data={items}
      itemLayoutAnimation={LinearTransition}
      renderItem={({ item, index }) => (
        <Animated.View
          entering={FadeInDown.delay(index * 50).springify()}
          exiting={FadeOutLeft.duration(300)}
        >
          <NotificationCard item={item} />
        </Animated.View>
      )}
    />
  )
}

Spring vs Timing: Doğru Easing Seçimi

Her animasyon türü için doğru easing fonksiyonu seçmek UX kalitesini belirler. Temel kuralımız: kullanıcı etkileşimi sonucu tetiklenen animasyonlarda spring, otomatik/sistem animasyonlarında timing kullanmak.

SenaryoAnimasyon TipiNeden?
Buton basmawithSpringDoğal geri sekme hissi
Sayfa geçişiwithTiming + Easing.bezierKontrollü ve öngörülebilir
Pull-to-refreshwithSpringElastik geri dönüş
Toast bildirimiwithTimingSabit sürede giriş/çıkış

Kullanıcının tıkladığı nesnenin yumuşakça esnemesi, sayfalar arasındaki sürükleme ivmesinin parmak hızını algılayıp tepki vermesi, UI/UX deneyimi açısından ürünümüzün kalitesini premium segmente yükseltti.


Bu içerik kişisel geliştirme laboratuvarımdan ve prodüksiyon maceralarımdan derlenmiştir.