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

最初の描画とレンダリングモード

最小構成のCanvasを読み解きながら、Retained ModeとImmediate Modeという2つの描画パラダイムの違いを解説します。

固定した要素ツリーの値を変えるRetained Modeと、フレームごとに描画命令を組み立てるImmediate Modeの比較

左は構造を保持して値を更新、右はフレームごとに描く内容そのものを組み立て直します。

前の章でSkiaの描画パイプラインの全体像を見たので、ここでは実際に動くコードを読みながら、最小構成の<Canvas>がどう組み立てられているかを確認します。

最初のCanvas

以下が、React Native Skiaで書ける最小のCanvasです。

import { Canvas, Circle, Fill } from "@shopify/react-native-skia";
import { StyleSheet, View } from "react-native";

export default function FirstCanvas() {
  return (
    <View style={styles.container}>
      <Canvas style={styles.canvas}>
        <Fill color="#1e1e2e" />
        <Circle cx={128} cy={128} r={64} color="#b58df1" />
      </Canvas>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  canvas: {
    flex: 1,
  },
});

Skiaを読み込み中…

<Canvas>は通常のViewと同じようにstylepropでレイアウトを指定でき、他のReact Nativeコンポーネントの中に自由に配置できます。中身は次の2つの図形だけです。

  • <Fill color="#1e1e2e" /> はCanvas全体を単色で塗りつぶします。背景を敷くときによく使う書き方です
  • <Circle cx={128} cy={128} r={64} color="#b58df1" /> は中心座標(128, 128)、半径64の円を描きます

座標系はCanvasの左上が原点(0, 0)で、右方向がx軸のプラス、下方向がy軸のプラスです。これは通常のRN Viewの座標系や、SVG・Canvas 2D APIの座標系と同じ感覚で扱えます。

描いた図形は「後から書いたものが上」

<Fill>を先に、<Circle>を後に書いているのには理由があります。Skiaは子要素を書かれた順番通りに描画するため、後から描いた図形が先に描いた図形の上に重なります。もし順番を逆にすると、円が背景色で塗りつぶされて見えなくなってしまいます。

複数の図形を重ねるレイアウトでは、この「上から下へのz順序」を意識しておくと意図通りの見た目になります。

Retained ModeとImmediate Mode

React Native Skiaには、描画のやり方として大きく2つのパラダイムがあります。**保持モード(Retained Mode)即時モード(Immediate Mode)**です。どちらも同じ<Canvas>の中で使えるので、混在させることもできます。この章以降は、公式ドキュメントでの表記にならい英語名(Retained Mode / Immediate Mode)で呼びます。

Retained Mode(デフォルト)

ここまで見てきた書き方が、まさにRetained Modeです。シーンをReactコンポーネントのツリーとして宣言し、React Native Skiaがそれをディスプレイリストに変換します。

const RetainedModeExample = () => {
  const radius = useSharedValue(50);
  useEffect(() => {
    radius.value = withSpring(radius.value === 50 ? 100 : 50);
  }, []);
  return (
    <Canvas style={{ flex: 1 }}>
      <Circle cx={128} cy={128} r={radius} color="cyan" />
    </Canvas>
  );
};

Skiaを読み込み中…

radiusのようなReanimatedの共有値をpropsに渡しておけば、値の更新はUIスレッド上で完結し、React側の再レンダーはほとんど発生しません。ツリーの構造自体は変わらず、プロパティの値だけがアニメーションするようなユーザーインターフェースやインタラクティブな描画に向いています。

Immediate Mode

一方Immediate Modeでは、フレームごとに描画命令を直接発行します。PictureコンポーネントとPictureRecorderを使い、コンポーネントツリーの形そのものを毎フレーム変えられます。

const ImmediateModeExample = () => {
  const progress = useSharedValue(0);
  const recorder = Skia.PictureRecorder();
  const paint = Skia.Paint();

  const picture = useDerivedValue(() => {
    "worklet";
    const canvas = recorder.beginRecording(Skia.XYWHRect(0, 0, 256, 256));
    const count = Math.floor(progress.value * 20);
    for (let i = 0; i < count; i++) {
      paint.setColor(Skia.Color(`rgba(0, 122, 255, ${(i + 1) / 20})`));
      canvas.drawCircle(128, 128, (i + 1) * 6, paint);
    }
    return recorder.finishRecordingAsPicture();
  });

  return (
    <Canvas style={{ flex: 1 }}>
      <Picture picture={picture} />
    </Canvas>
  );
};

Skiaを読み込み中…

描く円の数(count)がフレームごとに変わっている点に注目してください。Retained Modeではこのように要素数が動的に変わる表現は苦手ですが、Immediate Modeならネイティブ呼び出し(FFI)のコストを払う代わりに、毎フレーム自由に描画命令を組み立てられます。

どちらを選ぶか

シナリオ 推奨モード 理由
プロパティがアニメーションするUI Retained アニメーション中のFFIコストがほぼゼロ
データ可視化 Retained 通常は構造が固定されている
スプライトやタイルの枚数が固定の表現 Retained Atlas APIと組み合わせれば1回の描画命令で済む
エンティティが増減するゲーム Immediate オブジェクトが動的に生成・破棄される
ジェネラティブアート Immediate 描画命令自体が毎フレーム変わる

迷ったときはRetained Modeから始めるのがおすすめです。第2部以降で扱う図形やアニメーションの大半はRetained Modeで書けます。Immediate Modeは、Pictureを扱う章(第2部のPictureと再利用可能な描画命令)で改めて取り上げます。

環境構築やインストール手順は本書では扱いません。プロジェクトへの導入方法は公式のGetting Startedを参照してください。