LoginSignup
7
2

More than 5 years have passed since last update.

react-native-expo-image-cacheの足りない所を直す

Last updated at Posted at 2018-02-16

この記事は以下の記事の意訳です。
https://hackernoon.com/5-things-to-know-about-images-react-native-69be41d2a9ee?gi=647eb0cc1bf1

react-ntaive-expo-image-cacheは私のプロジェクトにはまだ足りないところがある。と話している記事です。その上で、画像を扱う時の注意を書いています。

1 prefetch()を使ってはいけない。

React Nativeは画像を事前ロードするためのImage.profetchを用意している。しかし、いくつかのケースでは適していない。

例えばフリックするような画像はローカルにある画像を使いたい。下のサンプルではImage.prefetchとローカルにある画像とのレンダリングスピードの差が出ている。
(元記事のgifを見てね)

左のケースが、prefetchを使い、右のケースはcacheを使っている。

画像を、 Image 以外のタグで使いたい時あるよね。
例えばローカルのpathとdata URIでのみ動く SVGImage とかさ。
そんな時も画像をローカルに置くのが適している。

2 ExpoKitを使う。

なるべく、画像はローカルに保存すべきだ。もし君がExpoを既に使っているならファイルシステムのライブラリがあることを知っているだろう。
もし君のプロジェクトがdetatch(react-native linkを実行可能にすること)されたなら、さっき言ったreact-native-fetch-blobライブラリは大半の機能を失ってしまう。
ExpoKitはとても便利で、たくさんのコンポーネントを提供してくれる。例えば、Progressiveな画像のローディングの際のアニメーションをしてくれるBlurViewとかね。

Androidでの同時実行の扱い方

ある一つの画像を取得する時に、取得する処理を何回も繰り返してしまうことがある。(本来は一回でいいのに関わらず)つまりアプリが遅くなる。
AndroidのFileSystem.exists(localURI)は画像のインストールが終了しなくても、trueを返してしまう。
そこでobserver patternを画像をインストールする際に一度だけ、画像のインストールが終了してから実行するようにする。

react-native-expo-image-cacheのAPIだと以下のようになる。

import {CacheManager} from 'react-native-expo-image-cache';

// Remote URI
const {uri} = this.props;
CacheManager.cache(uri, localURI => this.setState({uri: localURI}));

下が、Observerの実行だ

static async cache(uri: string, listener: Listener): Promise<void> {
    const {path, exists} = await getCacheEntry(uri);
    // Is the image is already downloading, we just listen
    if (isDownloading(uri)) {
        addListener(uri, listener);
    // If it's not downloading and it exists, we serve it
    } else if (exists) {
    listener(path);
    // Else, we download the image and notify everyone when done
    } else {
      addListener(uri, listener);
      await FileSystem.downloadAsync(uri, path);
      notifyAll(uri, path);
      unsubscribe(uri);
    }
}

Progressive な画像のロード

画像のURIをデータベースに保存する時、bse64 previewとして保存される。これが、画像をとてもスムースにロードするのを可能にする。
下は例だ。

{
  preview: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
  uri: "https://firebasestorage.googleapis.com/v0/b/react-native-e.appspot.com/o/b47b03a1e22e3f1fd884b5252de1e64a06a14126.png?alt=media&token=d636c423-3d94-440f-90c1-57c4de921641"
}

これであなたはすぐにblurred(ぼかし処理)されたプレビューを見ることができる。

iosではExpokitからのがネイティブのアニメーションdividerをサポートしている。しかしandroidではデフォルトでopacityしてくれない。
そこで自分で作る。

// intensity is an Animated.Value
const opacity = intensity.interpolate({
    inputRange: [0, 100],
    outputRange: [0, 1]
});
{
    Platform.OS === "ios" && (
        <AnimatedBlurView
            tint="dark"
            style={computedStyle}
            {...{intensity}}
           />
    )
}
{
    Platform.OS === "android" && (
        <Animated.View
            style={[computedStyle, { backgroundColor: "rgba(0, 0, 0, 0.5)", opacity }]}
        />
    )
}

react nativeのimageのバグについて

例えば以下のバグがある。
https://github.com/facebook/react-native/issues/9195
画像のuriをstateに入れて、
問題は、時々その画像がリフレッシュされないことだ。
keyを使うことで、その問題を解決できる。
しかしこれが完璧な解決方法だろうか?
=> 画像を重ねてしまうことでうまく解決できる。

{
    // If show the preview if it exists
    hasPreview && (
        <RNImage
        source={{ uri: preview }}
        resizeMode="cover"
        style={computedStyle}
    />
  )
}
{
    // If the image is loaded, we show it on top
    // this.onLoadEnd is used to start the deblurring animation
    (uri && uri !== preview) && (
        <RNImage
            source={{ uri }}
        resizeMode="cover"
        style={computedStyle}
        onLoadEnd={this.onLoadEnd}
    />
    )
}

終わり

reduxを先に勉強しないといけないのに俺は何をやってるんだろう。
redux-observableは理解できるけど、コードの因果関係が全く掴めない。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2