始めるチュートリアルレスポンシブデザインimgixを使用してReact Nativeで画像をより高速にレンダリングする

imgixを使用してReact Nativeで画像をより高速にレンダリングする

組み込みのReact Native <Image />コンポーネントは多機能で、ページロード時間を改善する多くの方法を提供します。しかし、レスポンシブ画像のベストプラクティスに従うためには、多くのエンジニアリング時間を費やす必要があります。

imgixをReact Nativeのワークフローに統合することで、<Image />コンポーネントの機能を活用しながら、少ない時間でそれを実現することができます。

カバーする内容

imgixを活用してアプリの速度を向上させる方法を例を通じて説明します。最終的には、次のことができる画像コンポーネントが完成します:

  • デバイスの寸法に合わせて画像を自動的にリサイズ
  • デバイスのDPRに合わせて画像解像度を自動的に更新
  • 低品質画像プレースホルダー(LQIP)からの「ぼかしアップ」で低帯域幅ユーザーに対応

各ステップをコミット履歴で確認しながら進めることができます。最終成果物は以下の埋め込みReact Nativeアプリで確認できます。

各ステップで画像がどのように読み込まれるかを確認するために、Expoプレビューを切り替えてみてください。

<Image />コンポーネントを使用してimgix画像をレンダリングする

アプリケーションにimgixを統合するためには、まずアカウントを作成し、最初の画像をアップロードする必要があります。セットアップが完了したら、React Native(RN)の<Image />コンポーネントを使用してimgix画像をレンダリングできます。

まず、画像URLをImageコンポーネントのsourceURIとして設定します。また、heightといくつかのスタイルを<Image />に追加する必要があります。そうしないと、React Nativeは画像をどのサイズでレンダリングするかを認識できません。

これでアプリ内にimgix画像がレンダリングされました!

フラッシュオブアンスタイルドコンテンツ(FLOUC)を回避するための画像スタイリング

ユーザーの接続が遅い場合、読み込み中に何かを表示したい場合はどうすればよいでしょうか?画像に背景色を追加してみましょう。これにより、ユーザーは画像の読み込み中に画像の寸法を把握できます。

@imgix/core-jsを使用してimgixのレンダリングAPIを活用する

次に進んで、imgixのレンダリングAPIと@imgix/js-coreライブラリを使用して画像を最適化し、迅速に読み込むようにしましょう。まず、パッケージをインストールする必要があります。

# プロジェクトのルートディレクトリで実行
npm i @imgix/js-core
# または
yarn add @imgix/js-core

次に、コンポーネント内でクライアントをインスタンス化する必要があります。これにより、後でURL作成機能を利用できます。

const imgix = new ImgixClient({ domain: "sdk-test.imgix.net" })

次に、画像を希望の高さにリサイズするためにこれを使用できます。デザイナーはこのページに950ピクセルの高さの画像を求めています。これをimgixレンダリングAPIパラメーターとしてImgixClientbuildURL()関数に渡すことができます。

const imgix = new ImgixClient({ domain: "sdk-test.imgix.net" })
const height = 950
const imgixParams = { h: height }
const uri = {
  uri: imgix.buildURL("amsterdam.jpg", imgixParams),
}

これで画像は設定された高さの値に自動的にリサイズされます。

レンダリングAPIを使用してデバイスの寸法に合わせて自動的にトリミングする

さらに改善できます。画像を表示されるウィンドウのサイズに自動的に合わせましょう。これにより、どのデバイスでも画像が適切なサイズになります。React NativeのuseWindowDimensionsモジュールを使用して現在のウィンドウの寸法を取得します。

// ...
const imgix = new ImgixClient({ domain: "sdk-test.imgix.net" })
const { height } = useWindowDimensions()
const imgixParams = { h: height }
const uri = {
  uri: imgix.buildURL("amsterdam.jpg", imgixParams),
}
// ...

デバイスの寸法を測定できるようになったので、画像をデバイスの高さと幅に合わせてトリミングしてみましょう。これにより、多くのバイトが節約され、画像の読み込みがさらに速くなります。これを行うには、imgixレンダリングAPIパラメーターfit: cropを使用します。

画像は自動的にリサイズされ、どのデバイスでも適切にトリミングされます。

レンダリングAPIを使用して各デバイスのデバイスピクセル比(DPR)に自動的に適応する

モバイルデバイスで画像を扱う際の最も難しいことの1つであるデバイスピクセル比(DPR)にまだ対応していません。

モバイルデバイスの画面解像度は大きく異なり、1つのデバイスで見栄えが良い画像が、DPRの違いを考慮しないと他のデバイスで見栄えが悪くなることがあります。

一部の開発者は、画像を計画しているデバイス解像度に合わせて圧縮することでこの問題を回避しようとします。彼らの画像は素晴らしく見えますが、AppleやGoogleが新しい電話サイズや画面解像度を発表すると、その画像は見栄えが悪くなります。

代わりに、画像がデバイスに応じてDPRを自動的に調整するようにしましょう。React NativeのPixelRatioモジュールとimgixレンダリングAPIのdprパラメーターを使用してこれを実現します。

// ...
const imgixParams = {
  fit: "crop",
  h: height,
  w: width,
  dpr: PixelRatio.get(),
}
const uri = {
  uri: imgix.buildURL("amsterdam.jpg", imgixParams),
}

画像は常に正しいDPRでレンダリングされるため、低解像度または高解像度の画面でも見栄えが良くなります。ここでの欠点は、高解像度デバイスでは画像がかなり重くなることです。接続が遅いと、読み込み時間が長くなり、コンテンツの「フラッシュ」が発生する可能性があります。

低品質画像プレースホルダー(LQIP)を使用してレンダリングAPIを作成する

この問題を回避するために、この画像の2つのバージョン(LQIPとフル解像度の画像)を保存できます。まずLQIPを読み込み、準備ができたらフル解像度の画像を読み込みます。

これを行うには、フルサイズの画像の幅と高さの1/4に設定したwおよびhimgixパラメーターを設定して低解像度バージョンの画像を作成します。画像がピクセル化されないようにblurパラメーターも追加します。

注:imgixレンダリングAPIにはblurhashパラメーターがあります。これはreact-native-blurhashのようなライブラリで使用できるハッシュを生成します。これにより、よりパフォーマンスが向上しますが、Expo Snacksとは互換性がありません

//...
const imgix = new ImgixClient({ domain: "sdk-test.imgix.net" })
const { width, height } = useWindowDimensions()
/**
 * デバイスの寸法とデバイスピクセル比に合わせた「フル解像度画像」を作成します。
 */
const imgixParams = {
  fit: "crop",
  h: height,
  uri: imgix.buildURL("amsterdam.jpg", imgixParams),
}
 
/**
 * imgixレンダリングAPIを使用して低品質画像プレースホルダーを作成します
 */
const lqipParams = {
  fit: "crop",
  w: width / 4,
  h: height / 4,
  dpr: 1,
  blur: 25,
}
const lqipUri = { uri: imgix.buildURL("amsterdam.jpg", lqipParams) }

次に、プレースホルダー画像のsourceとしてlqipUriを追加します。

  return (
   // ...
      <View style={styles.imageContainer}>
        <Image source={lqipUri} style={[styles.image]} />
        <Image source={uri} style={[styles.image]} />
      </View>
    </View>
  );

最後に、画像を絶対位置にスタイリングして、互いに重なるようにレンダリングします。

 // ...
   image: {
     position: "absolute",
     backgroundColor: "#e1e4e8",
     left: 0,
     right: 0,
     bottom: 0,
     top: 0,
   },

「ぼかしアップ」アニメーションの追加

低解像度画像は、フル解像度の画像がまだリクエストされている間にレンダリングされます。

しかし、トランジションは少し驚くかもしれません。1つの画像から次の画像にスムーズにフェードインする「ぼかしアップ」アニメーションを追加しましょう。

これを行うには、React NativeのAnimatedコンポーネントとReactのuseRefフックを使用します。コンポーネントの初期「透明」状態への参照を作成し、画像が読み込まれたら、onLoadが完全な不透明状態へのトランジションをトリガーします。

再利用可能な<ImgixImage />コンポーネントを作成する

画像は素晴らしく見えます!imgixを使用することで、画像は次のことができるようになりました:

  • デバイスの寸法に合わせて自動的にリサイズ
  • デバイスのDPRに合わせて自動的に更新
  • 低帯域幅ユーザーにより良く対応するために「ぼかしアップ」

最後に、実装をクリーンアップし、ImgixImage.jsという独自のコンポーネントにリファクタリングします。

これで、このコンポーネントをアプリ全体で再利用し、どこでもimgixレンダリングAPIを活用できます。

まとめ

imgixをReact Nativeで使用することはシンプルで信頼性が高く、動的です。多くの画像を扱い、アプリを提供するすべてのデバイスに適したフォーマットとサイズで画像を提供したい人にとって、理想的なソリューションです。

その他のリソース

  • js-coreライブラリ: @imgix/js-coreライブラリの公式ドキュメントをチェックしてください