この記事は以下の記事の意訳です。
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は理解できるけど、コードの因果関係が全く掴めない。