📱

expo-splash-screenでのアニメーションサンプル

2024/04/13に公開

React Native / Expo で「Linky Notes」という無料のノートアプリを
個人開発しリリースしました。

https://apps.apple.com/jp/app/linky-notes/id6483615717

https://note.com/colers/n/n17be0c248846

はじめての React Native を利用した開発だったこともあり、
色々と気づきがあったことをアウトプットしたく初投稿します🙇‍♂

背景

「Linky Notes」では初回起動時に Splash Screen が
フェードアニメーションするように実装しています。

この実装にあたって 公式のサンプルコード を参考にしつつ、
TypeScriptで書き直したうえでバンドルした画像を利用するように変更しました。

サンプルコード

アプリのコードそのままではないものの、
サンプルで動くコードを以下リポジトリに用意しました。
https://github.com/yasuhiro-yamamoto/react-native-and-expo-example/tree/main/animated-splash-screen

アニメーションさせる実装についてかんたんな補足

いくつかポイントをご紹介します。

expo-splash-screen

通常、Expo のプロジェクトではassest/splash.pngを配置するだけで
アプリの起動時にその画像を表示してくれる仕様です。

これを例えばアニメーションさせたいケースなど、
明示的に画像の表示・非表示をコントロールするために
expo-splash-screen が提供されています。

アニメーションさせる方法

冒頭でご紹介したとおり、公式のサンプルコード が用意されているので、
基本的にはこちらのコードを参考にすれば実装可能です。

「Linky Notes」の場合は以下の要件があったため、
参考にしつつ書き直して実装したという背景です。

  • TypeScriptを利用
  • 画像はダウンロードせずローカルのバンドルした画像を利用したい

画像については直接参照するのみなので割愛しますが、
アニメーションの流れについてはポイントを抜粋します。

スプラッシュ画像の自動非表示を止める

assest/splash.pngに画像を配置し初回起動時に画像を表示した場合、
アプリが表示できる状態になると自動で非表示になる仕様です。

expo-splash-screenpreventAutoHideAsync()を利用することで、
明示的にスプラッシュ画像を非表示にしないようにすることができます。

import * as SplashScreen from 'expo-splash-screen'

// Instruct SplashScreen not to hide yet, we want to do this manually
SplashScreen.preventAutoHideAsync().catch(() => {
  /* reloading the app might trigger some race conditions, ignore them */
})

アプリの準備状態の管理

useStateを利用してアプリの準備状態は管理しています。

const [isAppReady, setAppReady] = useState(false)
const [isSplashComplete, setSplashComplete] = useState(false)
  • isAppReadyはアプリの準備状態
  • isSplashCompleteはスプラッシュスクリーンのアニメーション完了判定
    • 再度ホーム画面に戻ってきたときにアニメーションしないようにするフラグ

画像の準備ができるとonLoadEnd属性に渡している
onImageLoaded関数が呼ばれます。

// Presentation
<Animated.Image
  style={{
    width: '100%',
    height: '100%',
    resizeMode: splashResizeMode,
  }}
  source={imagePath}
  onLoadEnd={onImageLoaded}
  fadeDuration={0}
/>

// Container
const onImageLoaded = useCallback(() => {
    void (async () => {
      try {
        await SplashScreen.hideAsync()
      } catch (e) {
        console.log(e)
      } finally {
        // isAppReadyをtrueにする
        setIsAppReady(true)
      }
    })()
}, [])

isAppReadytrueになるとアニメーションが開始するという流れです。

useEffect(() => {
    // isAppReadyがtrueになったらアニメーションを開始
    if (isAppReady) {
      // ここでアニメーションの内容を指定
      Animated.timing(animation, {
        toValue: 0,
        duration: 1000,
        useNativeDriver: true,
      }).start(() => setIsSplashComplete(true))
    }
}, [isAppReady, animation])

最後に

普段はWEB寄りのフロントエンジニアですが、
モバイルアプリ開発はまた違った楽しさがありました。

知識不足の点もあると思いますが、
参考になれば幸いです🙇‍♂

Discussion