React Native Animasyonlar, Animated API

javascriptreact-nativeanimasyon

1 yıl önce 0 yorum

Merhaba, react native üzerinde animasyonların kullanımlarından bahsedeceğim. Tamamı olmasada bu makaleden alıntı yapılmıştır. (Birebir aynı değildir) Nader Dabit'e teşekkür ederim.

React native içinde animasyon kullanmak için tavsiye edilen yol Animated özelliğini kullanmaktır.
Animated ile animasyon oluşturmak için kullanılan üç adet method :

  1. Animated.timing() - Zaman’a göre değeri yumuşatır.
  2. Animated.decay() - Başlangıç hızıyla başlar ve kademeli olarak yavaşlar.
  3. Animated.spring() - Basit tekli-esneme fiziksel modeli (Rebound ve Origami merkezli).

Biz çoğunlukla Animated.timing() ve Animated.spring() kullanımından bahsedeceğiz.

Bu üç method ile birlikle bazı animasyonlar oluşturmak için üç yöntem daha var :

  1. Animated.parallel() - Aynı anda bir dizi animasyon başlatır.
  2. Animated.sequence() - Bir dizi animasyonu sırayla başlatır ve her animasyonun bir sonraki animasyona başlamadan önce tamamlanmasını bekler. Çalışan animasyon durdurulursa, diğer animasyonlar başlatılmaz.
  3. Animated.stagger() - Animasyon dizisi paralel çalışabilir (örtüşebilir), fakat ardışık geçikmelerle sırayla başlatılır. Animated.parallel() işlevine çok benzer, fakat bunda animasyonlara gecikmeler (delay) ekleyebilirsiniz.

1. Animated.timing()

Oluşturduğumuz ilk animasyon Animated.timing() kullanarak döndürme işlemi olacak:

Animated.timing(
  birDeger,
  {
    toValue: gidecegiDeger,
    duration: sure,
    easing: easingFunction,
    delay: gecikme
  }
)

Bu tür sonsuz animasyon, yükleniyor pencereleri oluştururken kullanışlı olabilir. Bu kavram yukarı aşağı boyutlandırma veya başka türde sonsuzluk içeren animasyonlar oluşturmak için kullanılabilir.

Başlamak için, var olan bir react native projesine veya yeni bir react native projesine ihtiyacımız var. react-native init ile projenizin ismini yazarak projeyi oluşturun, sonra çalışmak için cd ile dizine geçin.

react-native init animasyonlar
cd animasyonlar

Şimdi bu klasördeyiz ve index.android.js veya index.ios.js dosyalarını açın.

Şimdi projeyi oluşturduk, ilk yapmamız gereken Animated, Image ve Easing modüllerini import etmek.

import {
  ...
  Animated,
  Image,
  Easing
} from 'react-native'

Animated, animasyonları oluşturmamız için kullanacağımız kütüphanedir ve React Native ile birlikte gelir.

Kullanıcı Arayüzünde bir resim oluşturmamız için Image kütüphanesi gerekli.

Easing React Native ile gelen bir modüldür. Hazırda bulunan tanımlı linear, ease, quad, cubic, sin, elastic, bounce, back, bezier, in, out, inout ve diğer methodları var. Tutarlı bir doğrusal hareket için linear kullanacağız. Bu anlatılan bölüm bittiğinde nasıl uygulayacağınız konusunda daha iyi bir fikre sahip olacaksınız.

Sonra, döndürme değerimiz için başlangıçta bir animasyon değeri belirlememiz gerekiyor. Bunu yapmak için constructor’ı kullanacağız.

constructor () {
  super()
  this.donusDegeri = new Animated.Value(0)
}

donusDegeri değerine yeni bir Animated.Value tanımlıyoruz ve başlangıç için 0 (sıfır) veriyoruz

Sonra dondur adında bir method oluşturuyoruz ve bunu view yüklendiğinde yani componentDidMount() olduğunda çalıştırıyoruz.

componentDidMount () {
  this.dondur()
}

dondur () {
  this.donusDegeri.setValue(0)
  Animated.timing(
    this.donusDegeri,
    {
      toValue: 1,
      duration: 4000,
      easing: Easing.linear
    }
  ).start(() => this.dondur())
}

dondur() - Bu method şunları yapar:

  1. this.donusDegeri değerini kullanır
  2. Animated.timing method’unu çağırır ve this.donusDegeri değerini 4000 milisaniyelik bir süreyle ve Easing.linear ile yumuşak bir şekilde 1 değerine indirir. Animated.timing iki argüman alır. İlki bir değişken (this.donusDegeri) ikincisi özelleştirme değerleri. Bu özelleştirmede bir tane toValue (gideceği değer), bir tane duration (süre), bir tane easing (yumuşatma türü) ve bir tane de delay (geçikme süresi) barındırıyor.
  3. Son olarak start methodunu çağırdık ve içinde bir callback fonksiyon tanımladık. start animasyonumuzu başlatmamız için gereklidir. start() methodu animasyon tamamlandığında içindeki callback’e {finished: true} değerini döner, fakat eğer animasyon başka bir animasyon tarafından kesilmişse {finished: false} döner. Animasyonumuz sonsuza kadar çalışacak.

Animasyon için yapmamız gerekenler bu kadardı, şimdi onu UI üzerinde görelim. render methodunu güncelliyoruz.

render() {
  const don = this.donusDegeri.interpolate({
    inputRange: [0, 1],
    outputRange: ['0deg', '360deg']
  });
  return (
    <Animated.Image
        style={{
          width: 323,
          height: 170,
          transform: [{rotate: don}]
        }}
        source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}}
    />
  )
}
  1. this.donusDegeri.interpolate fonksiyonu ile const don değerine 0deg ile 360deg arasında bir geçiş attık. interpolate() methodu inputRange’e girilen değerleri outputRange’e uygun olarak değiştirir ve yeni bir geçiş oluşturur. Yani 0-1 (veya 0-100 değiştirilebilir) arasındaki değerler interpolate sayesinde 0deg ile 360deg arasına çevrilir. (Aynı işlemleri renkler içinde yapabilirsiniz)
  2. Geriye Animated.Image view’ıyla genişliği, yüksekliği ve transform stili don adındaki değerle bir resim döndürürüz :
transform: [{rotate: don}]

Her şey tamamsa örnek çalışıyor! Örnek

Easing farkları ve daha fazlası

Easing’in kaynak kodlarına bakarak biraz inceleme yapabilirsiniz. @dabit’in yaptığı örneğe bakarak (biraz türkçeleştirdim) anlayabilirsiniz.

2. Animated.timing() örnekleri

resim1

Bu bölümde basitçe Animated.timing() animasyonlarından ve Animated.timing() ile birlikte interpolate kullanımından söz edeceğiz.

Şimdi de this.animasyonDegeri adındaki değişkenle ve bu değer ile birlikte interpolate() ile çoklu animasyonlar oluşturacağız. Şu stilleri kullanacağız:

  1. marginLeft
  2. opacity
  3. fontSize
  4. rotateX

Tüm kodları temizleyin veya yeni brach’e geçiş yapın

Şimdide constructor’da yeni animasyon değerini atmamız gerekiyor.

constructor () {
  super()
  this.animasyonDegeri = new Animated.Value(0)
}

Ardından oynat methodunu oluşturduk ve onu componentDidMount() olunca çalıştırdık

componentWillMount() {
  this.oynat()
}

oynat() {
  this.animasyonDegeri.setValue(0)
  Animated.timing(
    this.animasyonDegeri,
    {
      toValue: 1,
      duration: 2000,
      easing: Easing.linear
    }
  ).start(() => this.oynat())
}

render methodunda ise 5 tane farklı interpolate oluşturduk

render() {
  const marginLeft = this.animasyonDegeri.interpolate({
    inputRange: [0, 1],
    outputRange: [0, 300]
  })
  const opacity = this.animasyonDegeri.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: [0, 1, 0]
  })
  const movingMargin = this.animasyonDegeri.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: [0, 300, 0]
  })
  const textSize = this.animasyonDegeri.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: [18, 32, 18]
  })
  const rotateX = this.animasyonDegeri.interpolate({
    inputRange: [0, 0.5, 1],
    outputRange: ['0deg', '180deg', '0deg']
  })
...
}

interpolate methodu tek bir this.animasyonDegeri ile çok yönlü animasyonlar yapmamızı sağlayan etkili bir methoddur.

Sonra bu oluşan değerleri Animated.View ve Animated.Text component’leri üzerinde deneyelim.

return (
  <View style={{flex: 1, marginTop: 100}}>
    <Animated.View
      style={{
        marginLeft,
        height: 30,
        width: 40,
        backgroundColor: 'red'}} />
    <Animated.View
      style={{
        opacity,
        marginTop: 10,
        height: 30,
        width: 40,
        backgroundColor: 'blue'}} />
    <Animated.View
      style={{
        marginLeft: movingMargin,
        marginTop: 10,
        height: 30,
        width: 40,
        backgroundColor: 'orange'}} />
    <Animated.Text
      style={{
        fontSize: textSize,
        marginTop: 10,
        color: 'green'}} >
        Animasyonlu yazı
    </Animated.Text>
    <Animated.View
      style={{
        transform: [{rotateX}],
        marginTop: 50,
        height: 30,
        width: 40,
        backgroundColor: 'black'}}>
      <Text style={{color: 'white'}}>Bu TransformX kullanıyor</Text>
    </Animated.View>
  </View>
)

Her şey tamamsa örnek çalışıyor! Örnek

3. Animated.spring()

resim

Şimdi ise animasyonumuzu Animated.spring() kullanarak oluşturalım.

// Örnek kullanım
Animated.spring(
    animasyonDegeri,
    {
      toValue: numara,
      friction: numara
    }
)

Aynı projeden devam edebiliriz. Sadece birkaç yeri güncellemeliyiz. constructor’da animasyonDegeri yerine springDegeri diye tanımlayalım ve 0.3 verelim.

constructor () {
  super()
  this.springDegeri = new Animated.Value(0.3)
}

oynat methodu yerine springOynat yapalım ve onuda componentDidMount() olduğunda çalıştıralım.

springOynat () {
  this.springDegeri.setValue(0.3)
  Animated.spring(
    this.springDegeri,
    {
      toValue: 1,
      friction: 1
    }
  ).start()
}
  1. springDegeri 0.3 diye ayarladık
  2. Animated.spring() methodunu çağırdık, ilk parametresi animasyon değerimiz, ikinci ise config nesnesi. Config nesnesi keyleri:
    • toValue (gerekli)
    • overshootClamping
    • restDisplacementThreshold
    • restSpeedThreshold
    • velocity
    • bounciness
    • speed
    • tension
    • friction
  • toValue gereklidir, ama friction ve tension ile spring animasyonunu daha fazla kontrol edebilirsiniz.
  1. start() ile animasyonu başlattık.

Animasyonu bittiğine göre, view üzerinden tıklayarak event tetiklesin bunuda eskiden yaptığımız react native logosuna yapalım:

<Text
  style={{marginBottom: 100}}
  onPress={this.springOynat.bind(this)}>Spring Yap</Text>
<Animated.Image
  style={{ width: 227, height: 200, transform: [{scale: this.springDegeri}] }}
  source={{uri: 'https://facebook.github.io/react/img/logo_og.png'}}/>
  1. Text component’inin onPress’ine springOynat fonksiyonunu çağırdık
  2. Animated altında bulunan Image’e scale için this.springOynat değerini verdik

Her şey tamamsa örnek çalışıyor! Örnek

4. Animated.parallel()

resim

Animated.parallel() başlarken aynı zamanda bir dizideki animasyonları başlatır.

// API
Animated.parallel(birDiziAnimasyon)

// Kullanımı:
Animated.parallel([
  Animated.spring(
    animasyonDegeri,
    {
      //config seçenekleri
    }
  ),
  Animated.timing(
     animasyonDegeri2,
     {
       //config seçenekleri
     }
  )
])

Başlamak için constructor’a 3 tane animasyon değeri tanımlayalım.

constructor () {
  super()
  this.animasyonDegeri1 = new Animated.Value(0)
  this.animasyonDegeri2 = new Animated.Value(0)
  this.animasyonDegeri3 = new Animated.Value(0)
}

Sonra oynat methodumuzu componendDidMount() olduğunda çağıralım

componentDidMount () {
  this.oynat()
}

oynat () {
  this.animasyonDegeri1.setValue(0)
  this.animasyonDegeri2.setValue(0)
  this.animasyonDegeri3.setValue(0)
  const animasyonYap = function (value, duration, easing, delay = 0) {
    return Animated.timing(
      value,
      {
        toValue: 1,
        duration,
        easing,
        delay
      }
    )
  }
  Animated.parallel([
    animasyonYap(this.animasyonDegeri1, 2000, Easing.ease),
    animasyonYap(this.animasyonDegeri2, 1000, Easing.ease, 1000),
    animasyonYap(this.animasyonDegeri3, 1000, Easing.ease, 2000)        
  ]).start()
}

oynat methodunda üç animasyonun değerini sıfıra çevirdi. Animasyonları oluşturmak için animasyonYap() methoduyla animasyon değeri, duration, easing ve delay argümanlarını kullandık. delay gelmemişse onu sıfıra ayarladık

render methodunda sonraki animasyonlara interpolate ile değer atmamız gerekiyor.

render() {
  const scaleText = this.animasyonDegeri1.interpolate({
    inputRange: [0, 1],
    outputRange: [0.5, 2]
  })
  const spinText = this.animasyonDegeri2.interpolate({
    inputRange: [0, 1],
    outputRange: ['0deg', '720deg']
  })
  const introButton = this.animasyonDegeri3.interpolate({
    inputRange: [0, 1],
    outputRange: [-100, 400]
  })
  ...
}
  1. scaleText yazımızı .5 ile 2 arasında büyütmemizi sağlayacak
  2. spinText yazımızı 0dan 720 dereceye kadar döndürmemizi sağlayacak
  3. introButton butonumuza -100 ile 400 arasında margin vermemizi sağlayacak

Şimdi bunları Animated.Views ile UI üzerinde görelim

return (
  <View style={{flex: 1, marginTop: 100, alignItems: 'center'}}>
    <Animated.View 
      style={{ transform: [{scale: scaleText}] }}>
      <Text>Hoş Geldiniz Dostlarım!</Text>
    </Animated.View>
    <Animated.View
      style={{ marginTop: 20, transform: [{rotate: spinText}] }}>
      <Text
        style={{fontSize: 20}}>
        Uygulama İçin Dönsün Bu TEXT!
      </Text>
    </Animated.View>
    <Animated.View
      style={{top: introButton, position: 'absolute'}}>
      <TouchableHighlight
        onPress={this.oynat.bind(this)}
        style={{
          padding: 10,
          backgroundColor: 'blue'
        }}>
        <Text
          style={{color: 'white', fontSize: 20}}>
          Buraya tıkla ve Başlat
        </Text>
     </TouchableHighlight>
    </Animated.View>
  </View>
)

scaleText ilk view, spinText ikinci view ve introButton da üçüncü view. oynat() çalıştırıldığında tüm animasyonlar paralel olarak çalışır.

Her şey tamamsa örnek çalışıyor! Örnek

5. Animated.Sequence()

resim

Şimdi api’ye bir göz atalım ve bu animasyonun nasıl yapıldığına bakalım

// API
Animated.sequence(birDiziAnimasyon)

// Kullanımı
Animated.sequence([
  Animated.timing(
    animasyonDegeri,
    {
      //config seçenekleri
    }
  ),
  Animated.spring(
     animasyonDegeri2,
     {
       //config seçenekleri
     }
  )
])

Animated.sequence() de Animated.parallel() gibi bir dizi animasyon alır. Animated.sequence() dizideki animasyonların her birinin tamamlanmasını bekler ve sonraki animasyona geçer, yani sırayla çalışır.
Temel olarak Animated.parallel()'e benzediği için önceki yaptığımız animasyonları tekrarlamıyoruz.

Buradaki örneğe bakın

Örnekte dikkat edilmesi gereken nokta, orada animasyon değerlerini bir dizi içine atmış olmamız.
Her şey tamamsa örnek çalışıyor! Örnek ^(yukarıda var)

6. Animated.Stagger()

resim

Şimdi api’ye bir göz atalım ve bu animasyonun nasıl yapıldığına bakalım

// API
Animated.stagger(delay, birDiziAnimasyon)

// Kullanımı
Animated.stagger(1000, [
  Animated.timing(
    animasyonDegeri,
    {
      //config seçenekleri
    }
  ),
  Animated.spring(
     animasyonDegeri2,
     {
       //config seçenekleri
     }
  )
])

Animated.parallel() ve Animated.sequence() gibi Animated.Stagger da bir dizi animasyon alır, fakat bu animasyonlar ardışık gecikmelerle sırayla başlatılır.

Buradaki örneğe bakın

Örnekte dikkat edilmesi gereken nokta Animated.stagger()'ın ilk parametresi delay(gecikme) içeriyor.

Her şey tamamsa örnek çalışıyor Örnek ^(yukarıda var)

Bütün projenin hepsini bu adreste bulabilirsiniz.

Bu makaleden alıntı yapılmıştır. (Birebir aynı değildir)

Bugün anlatacaklarım bu kadardı aklınıza takılan bir soru olursa çekinmeyin. Eğer bu konu hakkında veya konu dışı olarak bana soru sormak istiyorsanız https://github.com/abdurrahmanekr/bana-istedigini-sor repository’sinde bir issue açabilirsiniz. Youtube kanalıma abone olarak ve yayınladığım bu makaleyi paylaşarak bana destek olabilirsiniz. Diğer makalelerde görüşmek üzere :)

Düşündüklerin nedir ?

Abdurrahman Eker

(1010 Eylül 11111001100)

  • Full Stack Developer at Detaysoft Turkey/Sivas
  • İnternette Avare Kodcu
  • github
  • linkedin
  • youtube
  • Yeni içeriklerden haberdar olmak ister misin ?