Paintを理解する
色・スタイル・ブレンドモードといった描画属性をまとめる塗り(Paint)の仕組みと、propsとして渡す方法、コンポーネントとして渡す方法を解説します。
Paintは塗りや線だけでなく、継承・透明度・重なり方までまとめて決めます。
Skiaで何かを描くときは、色・線の太さ・背景との合成方法といった「どう描くか」を決める設定が必要です。これらの描画属性をまとめて塗り(Paint)と呼びます。
Paintを指定する3つの方法
React Native Skiaでは、塗りの属性を次の3通りの方法で指定できます。
<Circle>や<Rect>などの描画コンポーネント、または<Group>にpropsとして直接渡す<Paint>コンポーネントを子として渡すSkia.Paint()で作った塗りオブジェクトをrefとして渡す
ここまでの章で使ってきたcolorpropは、実は1番目の書き方の一例です。
塗りとストロークを重ねる
塗りは、色などの単純な属性であればpropsで済みますが、1つの図形に複数の塗りを重ねたいときは<Paint>を子として渡します。以下の例では、円に塗り1つとストローク2つを重ねています。
import { Canvas, Circle, Paint } from "@shopify/react-native-skia";
const r = 118;
const PaintDemo = () => {
return (
<Canvas style={{ flex: 1 }}>
<Circle cx={128} cy={128} r={r} color="red">
<Paint color="lightblue" />
<Paint color="#adbce6" style="stroke" strokeWidth={10} />
<Paint color="#ade6d8" style="stroke" strokeWidth={5} />
</Circle>
</Canvas>
);
};
<Paint>を複数子として渡すと、それぞれが独立した描画パスとして順番に適用されます。propsのcolor="red"は、子に<Paint>が1つもない場合のフォールバックとして扱われ、この例のように子がある場合は使われません。
継承の仕組み
前章のGroupで見た通り、<Group>に指定した塗りの属性は子孫要素に継承されます。<LinearGradient>のような複雑な属性も同様に、子として渡せば継承の対象になります。
import { Canvas, Circle, Group, LinearGradient, vec } from "@shopify/react-native-skia";
const r = 128;
const InheritanceDemo = () => {
return (
<Canvas style={{ width: 256, height: 256 }}>
<Circle cx={r} cy={r} r={r}>
<LinearGradient start={vec(0, 0)} end={vec(2 * r, 2 * r)} colors={["#00ff87", "#60efff"]} />
</Circle>
<Group>
<LinearGradient start={vec(2 * r, 2 * r)} end={vec(4 * r, 4 * r)} colors={["#0061ff", "#60efff"]} />
<Circle cx={3 * r} cy={3 * r} r={r} />
</Group>
</Canvas>
);
};
グラデーションやシェーダー、フィルター類の具体的な使い方は第4部で扱います。ここでは「塗りは継承される」という仕組みだけ押さえておけば十分です。
Paintのプロパティ
<Paint>やdrawing componentのpropsとして渡せる属性は以下の通りです。
| Name | Description |
|---|---|
| color | 色。文字列(CSS color)または数値(ARGB形式)で指定 |
| opacity | 不透明度。RGBはそのままにアルファ値だけを置き換える(0〜1) |
| blendMode | 背景色との合成方法 |
| style | fill(デフォルト)またはstroke |
| strokeWidth | ストロークの太さ |
| strokeJoin | ストロークの角の形状(bevel / miter / round) |
| strokeCap | ストロークの端の形状(butt / round / square) |
| strokeMiter | 角を斜めにカットする(bevel化する)しきい値 |
| antiAlias | 輪郭のアンチエイリアスを要求する(強制ではない) |
| dither | 色の誤差をディザリングで分散させることを要求する |
colorは"red"のようなCSSカラー名だけでなく、"hsl(120, 100%, 50%)"のような関数記法や0xffff0000のようなARGB数値でも指定できます。opacityは<Group>に指定すると、配下すべての色のアルファ値に一括で乗算されます。
blendModeにはsrcOver(デフォルト)、multiply、screen、darken、lightenなど多数の値が用意されています。詳しくは公式ドキュメントのblendMode一覧を参照してください。
以下のデモでradius・strokeWidth・hue(色相)を動かして、style="stroke"のPaintがどう変化するか確認できます。
Skia.Paint()で塗りを直接組み立てる
Reactのpropsを介さず、Skia.Paint()で塗りオブジェクトを直接組み立ててrefとして渡すこともできます。フレームごとに塗りを細かく作り変える(Immediate Mode)や、多数の図形で同じ塗りを使い回したいときに使う書き方です。
import { Canvas, Circle, Skia } from "@shopify/react-native-skia";
const paint = Skia.Paint();
paint.setColor(Skia.Color("lightblue"));
const ManualPaintDemo = () => {
return (
<Canvas style={{ flex: 1 }}>
<Circle paint={paint} cx={128} cy={128} r={128} />
</Canvas>
);
};
以下のデモは、上のコードと同じようにSkia.Paint()で作った塗りオブジェクトをrefとして<Circle>に渡しています。hueを動かすと、Reactのpropsを介さずpaint.setColor()を直接呼び出して塗りを書き換えていることが確認できます。
塗りに渡せる子要素はpropsで表現できる基本属性だけではありません。シェーダー、イメージフィルター、カラーフィルター、マスクフィルター、パスエフェクトも<Paint>や<Group>の子として渡せます。これらは表現の幅が大きいトピックなので、第4部で個別に扱います。