Skip to content
React Native Skia ガイドブック
Esc
navigateopen⌘Jpreview
On this page

画像を扱う

useImageによる画像の読み込みとImageコンポーネントのfit・sampling、SVGの描画、アニメーション画像やViewのスナップショットまで、画像まわりのAPIをまとめて解説します。

1枚の画像をcontain、cover、fillで配置する違いと、拡大時のサンプリングの違い

fitは画像を矩形へどう収めるか、samplingは拡大縮小時のピクセルをどう補間するかを決めます。

ここまでは図形とPaintの話が中心でしたが、実際のアプリではラスター画像(写真のようなピクセルの集まりで表現された画像)やSVGを描画する場面も多くあります。この章ではSkiaで画像を読み込み、描画するためのAPIをまとめて見ていきます。

useImage: 画像を読み込む

画像はuseImageフックで読み込みます。戻り値はSkImageインスタンスで、これを<Image>コンポーネントに渡します。

import { useImage } from "@shopify/react-native-skia";

const image1 = useImage(require("./assets/photo.jpg")); // JSバンドル内の画像
const image2 = useImage("https://example.com/photo.jpg"); // ネットワーク画像

読み込みは非同期なので、完了するまでuseImagenullを返します。以下のデモでもこのnullチェックをしたうえで<Image>をレンダリングしています。

useImageの第2引数にはエラーハンドラーを渡せます。手動で画像インスタンスを作りたい場合は、Base64データから生成するSkia.Image.MakeImageFromEncodedや、ピクセルデータから直接生成するSkia.Image.MakeImageも用意されています。ただしアプリ開発で使うのはほとんどの場合useImageです。

Image: 描画する

<Image>は出力先の矩形と、その中への収め方(fit)を指定して画像を描画します。

Name Type Description
image SkImage 描画する画像インスタンス
x number 出力先矩形の左位置
y number 出力先矩形の上位置
width number 出力先矩形の幅
height number 出力先矩形の高さ
fit? Fit 矩形への収め方。デフォルトはcontain
sampling? SamplingOptions 拡大縮小時のサンプリング方法
import { Canvas, Image, useImage } from "@shopify/react-native-skia";

const ImageDemo = () => {
  const image = useImage(require("./assets/photo.jpg"));
  return (
    <Canvas style={{ flex: 1 }}>
      {image && (
        <Image image={image} fit="contain" x={0} y={0} width={256} height={256} />
      )}
    </Canvas>
  );
};

fitにはcontain(アスペクト比を保って収める、デフォルト)、cover(アスペクト比を保って矩形いっぱいに埋める、はみ出す部分は切れる)、fill(アスペクト比を無視して矩形に合わせる)、fitHeightfitWidth(高さ・幅のどちらかを基準に合わせる)、scaleDown(画像が矩形より大きいときだけ縮小する)、none(等倍のまま配置)が指定できます。

以下のデモは同じ画像・同じ出力矩形(正方形)に対して、fitの値だけを変えて並べたものです。矩形が画像の縦横比と一致しない場合に、それぞれどう振る舞うか見比べてみてください。

Skiaを読み込み中…

サンプリングオプション

samplingpropで拡大縮小時の補間方法を指定できます。デフォルトはニアレストネイバー法(filter: "nearest")ですが、写真のような画像を拡大する場合はCubicSamplingを使うと滑らかになります。

import { Canvas, Image, useImage, CubicSampling, FilterMode, MipmapMode } from "@shopify/react-native-skia";

const SamplingDemo = () => {
  const image = useImage(require("./assets/photo.jpg"));
  return (
    <Canvas style={{ flex: 1 }}>
      {image && (
        <Image image={image} fit="contain" x={0} y={0} width={256} height={256} sampling={CubicSampling} />
      )}
      {image && (
        <Image
          image={image}
          fit="contain"
          x={0}
          y={0}
          width={256}
          height={256}
          sampling={{ filter: FilterMode.Nearest, mipmap: MipmapMode.Nearest }}
        />
      )}
    </Canvas>
  );
};

違いは画像を拡大縮小する場合、とくに小さな画像を大きく引き伸ばす場合に顕著です。以下のデモは16×16の小さなアイコンを11倍に拡大し、nearestcubicを並べたものです。nearestはピクセルの境界がそのままブロック状に残り、cubicは補間によって滑らかになります。

Skiaを読み込み中…

ImageSVG: SVGを描画する

SVGファイルやSVG文字列も<ImageSVG>で描画できます。ファイルから読み込む場合はuseSVGフック、文字列から作る場合はSkia.SVG.MakeFromStringを使います。

Name Type Description
svg SVG SVGインスタンス
width? number 出力先の幅(ルートSVGの幅が相対単位のときに初期ビューポートの解決に使う)
height? number 出力先の高さ(同上)
x? number 表示位置のx座標
y? number 表示位置のy座標
import { Canvas, ImageSVG, useSVG } from "@shopify/react-native-skia";

const SvgDemo = () => {
  const svg = useSVG(require("./assets/icon.svg"));
  return (
    <Canvas style={{ flex: 1 }}>
      {svg && <ImageSVG svg={svg} width={256} height={256} />}
    </Canvas>
  );
};

以下のデモはSkia.SVG.MakeFromStringで文字列から作った独自のSVGマークを、<Group>transformで回転・拡大縮小しています。

Skiaを読み込み中…

<ImageSVG>は内部でSkiaのSVGモジュール(ブラウザのSVGエンジンとは別の実装)を使っており、他の描画コンポーネントと同じペイントルールには従いません。色やぼかしなどの効果をかけたい場合は、<Group>layerpropに<Paint>を渡します。

import { Canvas, ImageSVG, Group, Paint, Blur, useSVG } from "@shopify/react-native-skia";

const BlurredSvgDemo = () => {
  const svg = useSVG(require("./assets/icon.svg"));
  return (
    <Canvas style={{ flex: 1 }}>
      <Group layer={<Paint><Blur blur={10} /></Paint>}>
        {svg && <ImageSVG svg={svg} width={256} height={256} />}
      </Group>
    </Canvas>
  );
};

以下のデモは同じSVGマークに<Group>layerpropで<Blur>をかけたものです。blurを0から動かすと、<ImageSVG>自体には効かないぼかしが、Groupのレイヤー合成を経由することで適用される様子が確認できます。

Skiaを読み込み中…

SVGの中にテキストや<image>要素を含めたい場合は、Skia.SVG.MakeFromStringの第2引数にフォントマネージャー(useFontsで作る)、第3引数に画像リソースのマップを渡せます。詳しくは公式ドキュメントのSVG Imagesを参照してください。

対応していないSVG要素(<animate><foreignObject><script>など)やCSSスタイル、RGBA色記法もあるので、複雑なSVGが正しく描画されない場合は公式ドキュメントのSVG Supportの注意点一覧を確認してください。

アニメーション画像(GIF・WebP)

React Native SkiaはアニメーションGIFとアニメーションWebPの再生に対応しています。Reanimatedと組み合わせる場合、useAnimatedImageValueが毎フレームの更新を自動的に行ってくれます。

import { Canvas, Image, useAnimatedImageValue } from "@shopify/react-native-skia";

const AnimatedImageDemo = () => {
  const bird = useAnimatedImageValue(require("./assets/bird.gif"));
  return (
    <Canvas style={{ width: 320, height: 180 }}>
      <Image image={bird} x={0} y={0} width={320} height={180} fit="contain" />
    </Canvas>
  );
};

第2引数にReanimatedの共有値を渡すと、再生・一時停止を制御できます。より低レベルなuseAnimatedImageフックを使えば、decodeNextFramegetCurrentFrameなどのメソッドでフレームを自分で進めることも可能です。

以下のデモはuseAnimatedImageValueで読み込んだ自作のループアニメーション(円軌道を移動するドット)をGIF/WebPそれぞれで再生しています。プレビューをタップするとpaused共有値が切り替わり、再生・一時停止できます。

Skiaを読み込み中…

Viewのスナップショット

makeImageFromViewを使うと、通常のReact NativeのView(Skiaの外側にある部分)をSkiaのSkImageとして取得できます。ネイティブでは、対象のViewにrefを張り、collapsable={false}を指定したうえで呼び出します。

import { useRef, useState } from "react";
import { View, Text, Pressable } from "react-native";
import { makeImageFromView, Canvas, Image } from "@shopify/react-native-skia";
import type { SkImage } from "@shopify/react-native-skia";

const SnapshotDemo = () => {
  const ref = useRef<View>(null);
  const [image, setImage] = useState<SkImage | null>(null);

  const onPress = async () => {
    const snapshot = await makeImageFromView(ref);
    setImage(snapshot);
  };

  return (
    <View style={{ flex: 1 }}>
      <Pressable onPress={onPress}>
        <View ref={ref} collapsable={false} style={{ backgroundColor: "cyan", flex: 1 }}>
          <Text>スナップショット対象のView</Text>
        </View>
      </Pressable>
      {image && (
        <Canvas style={{ flex: 1 }}>
          <Image image={image} x={0} y={0} width={image.width()} height={image.height()} />
        </Canvas>
      )}
    </View>
  );
};

collapsable={false}は必須です。これがないとReact Nativeの最適化でViewそのものが省略され、makeImageFromViewが失敗したり誤った結果を返したりします。

このAPIはネイティブのViewタグを介して実装されているため、Web版では動作が異なります。Web上でmakeImageFromViewを呼ぶ場合は、第2引数にコールバック関数を自分で渡し、DOM要素からの画像化処理(たとえばhtml2canvasのようなライブラリを使う)を実装する必要があります。本書のデモ環境も含め、Web向けにこのAPIをそのまま使うことはできない点に注意してください。

まとめ

useImageで画像を読み込み<Image>で描画する、useSVG(またはSkia.SVG.MakeFromString)で読み込み<ImageSVG>で描画する、という2つの基本パターンを押さえておけば、静的な画像アセットの大半は扱えます。アニメーションGIF・WebPやViewのスナップショットは少し特殊なAPIですが、必要になったときに参照できるよう概要を紹介しました。

次章からはテキストの扱い方を見ていきます。