useNativeDriver üzerine

react-nativeanimation

Eğer Raact Native'de animasyon işleriyle uğraştıysanız, animasyonu çalıştırırken useNativeDriver parametresini görmüşsünüzdür. Bu paramereyi ilk gördüğümde düşündüğüm şey, "Sanırım animasyon native kısımda çalışacak" oldu. Evet gerçekten de useNativeDriver animasyonu native tarafda çalıştırıyor. Peki bunu nasıl yapıyor?

Her şeyden önce bir şeyi açıklığa kavuşturalım. React Native, bütün ekranları native olarak oluşturur yani animasyonların da native olarak oluşturulması gerekir. Javascript bir kutunun yerinin değiştirirken değişikliği haber vermek için bir şekilde işletim sistemi ile haberleşmesi gerekir. Javascript ile Native kod farklı threadlarda çalıştığı için (UI Thread) değişikliğin React Native'in sağladığı "bridge" ile işletim sistemine aktarılması gerekir.

Animasyonları JS'ye vermek

Bir animasyon javascript tarafında kabaca şu adımları takip ederek oluşturulur.

Burada dikkat etmeniz gereken nokta şudur; Animasyon devam ederken React re-render işlemi yapmaz. Bunun sebebi Animated API' nin değeri doğrudan native tarafa göndermesidir. Eğer öyle olmasaydı, animasyon değeri bir React state'i gibi olsaydı, 60 saniyede bir component render olacaktı. Bu çok ciddi bir *performans kaybı *olurdu.

Her şey güzel JS animasyonlarımızı güzel güzel oluşturuyor. Fakat...

JS tek thread'da çalışır yani animasyonun bir sonraki değeri hesaplanırken aynı anda başka bir işlem yapamayız. Bu da JS tarafında oluşturulan animasyonun "gecikmeli(laggy)" olmasına sebep olur.

Bir örnek bu gecikmeyi daha iyi anlamanıza yardımcı olabilir.

import React, { useRef } from 'react';
import { SafeAreaView, Animated, TouchableOpacity, Text } from 'react-native';
 
const App = () => {
	const scale = useRef(new Animated.Value(1)).current;
 
	const startAnimation = () => {
		Animated.timing(scale, {
			toValue: 2,
			duration: 600,
			useNativeDriver: false // Native dirver devre dışı
		}).start();
	};
 
	return (
		<SafeAreaView style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
			<Animated.View
				style={{
					height: 100,
					width: 100,
					backgroundColor: 'red',
					borderRadius: 9999,
					transform: [{ scale }]
				}}
			/>
			<TouchableOpacity onPress={startAnimation}>
				<Text>Press Me</Text>
			</TouchableOpacity>
		</SafeAreaView>
	);
};
 
export default App;

Smooth animation

Evet JS güzel bir şekilde animasyonu çalıştırıyor. Biraz daha zorlayalım

const startAnimation = () => {
  Animated.timing(scale, {
    toValue: 2,
    duration: 600,
    useNativeDriver: false,
  }).start();
 
  setTimeout(() => {
    let r = 0
    for(let i = 0; i < 1000000000; i++) {
      r = i ** i
    }
  }, 10)

Panic!

Görüldüğü üzere JS threadinda FPS düştü ve animasyon* akıcılığını kaybetti.* Bunun nedeni daha önce dediğimiz gibi scale değerinin diğer kodlarla birlikte JS threadinda çalışması ve diğer kodları beklemesi.

Biraz daha açarsak, animasyon başladığı anda requestAnimationFramesaniyede 60 kere çalıştırılmaya çalışılacak. JS her defasında bir işlemi yapabileceği için

setTimeout(() => {
	let r = 0;
	for (let i = 0; i < 1000000000; i++) {
		r = i ** i;
	}
}, 10);

bu kod JS threadi meşgul edecek bir bir sonraki animasyon karesi atlanacak. Sonuç olarak "smooth" olmayan animsayonlar oluşacak.

Neyse ki React Native, native tarafta animasyonları oluşturmamıza izin verir.

Animasyonları native tarafta çalıştırmak

Yukardaki problemi basitçe useNativeDriver parametresini true yaparak çözebilirsiniz

Animated.timing(scale, {
	toValue: 2,
	duration: 600,
	useNativeDriver: true
}).start();

Fixed

Peki bu nasıl oluyor? Kabaca şöyle;

Bu şekilde animasyon hesaplamalarını JS threadinden ayırdık. Böylelikle hem daha hızlı animasyonlar yaptık hem de React Native Bridge'i darboğaz yapmamış olduk.

Bu noktada aklınıza şu gelebilir; "E o zaman useNativeDriver'i her zaman açalım"

Animasyonları native tarafta çalıştırmanın bazi eksileri vardır.

Sonuç olarak

Animasyonların React Native'de nasıl çalıştığını, hangi aşamalardan geçtiğini, useNativeDriver'in ne olduğunu, ne işe yaradığını göstermeye çalıştım. Umarım paylaştığım bilgiler yararlı olur.

2023 © Faruk