テキストの基本
`<Text>`によるシンプルなテキスト描画、フォントの読み込み方(useFont・matchFont・システムフォント)、そして複雑なレイアウトを扱えるParagraph APIの基本を解説します。
Skiaで文字を描くには、通常のTextコンポーネントとは別のAPIが必要です。SkiaはReact Nativeの外側にある独立した描画エンジンなので、OSの標準的なテキストレンダリングをそのまま使わず、自分でフォントを読み込んで描画します。
Text: シンプルなテキストを描く
<Text>は1行のテキストを指定した位置に描画するコンポーネントです。y座標がテキストの上端ではなく、ベースライン(文字の下端)を指す点に注意してください。
| Name | Type | Description |
|---|---|---|
| text | string |
描画する文字列 |
| font | SkFont |
使用するフォント |
| x | number |
左位置(デフォルトは0) |
| y | number |
ベースラインの位置(デフォルトは0) |
import { Canvas, Text, useFont, Fill } from "@shopify/react-native-skia";
const HelloWorld = () => {
const fontSize = 32;
const font = useFont(require("./assets/my-font.ttf"), fontSize);
return (
<Canvas style={{ flex: 1 }}>
<Fill color="white" />
{font && <Text x={0} y={fontSize} text="Hello World" font={font} />}
</Canvas>
);
};
フォントを読み込む
useFontはフォントファイルを読み込んでSkFontを返すフックです。第1引数にフォントファイル、第2引数にサイズを渡します。読み込みは非同期なので、完了するまではnullが返ります。
Web版のCanvasKitには、ネイティブのようなOS標準フォントが組み込まれていません。Skia.FontMgr.System()を呼んでも空のフォントマネージャーが返るため、フォントを明示的に読み込まない限りテキストは何も描かれません(文字化けではなく、完全に無描画になります)。これはブラウザで動く本書のデモ全体に共通する制約で、以降のテキスト系デモはすべてuseFontまたはuseFontsで明示的にフォントを読み込んだうえで描画しています。iOS/Androidのネイティブ環境ではシステムフォントが使えるので、この制約はWebだけのものです。
日本語を描画するには、日本語グリフを含むフォントファイルが必要です。本書のデモではNoto Sans JP(SIL Open Font License 1.1)から、デモで使う文字だけを抜き出したサブセットフォントを同梱しています。
以下のデモは、この日本語フォントを使って<Text>を描画しています。fontSizeと色相(hue)を動かして確認してみてください。
matchFontとuseFonts
useFontは単一のフォントファイルを読み込む素朴なAPIですが、太字・イタリックといったスタイルに応じてフォントを切り替えたい場合はmatchFontが使えます。useFontsで複数のフォントファイルをファミリー名ごとにまとめて読み込み、matchFontにCSSライクなスタイル指定を渡すと、対応するSkFontを返します。
import { useFonts, Text, matchFont } from "@shopify/react-native-skia";
const Demo = () => {
const fontMgr = useFonts({
Roboto: [
require("./assets/Roboto-Regular.ttf"),
require("./assets/Roboto-Bold.ttf"),
],
});
if (!fontMgr) {
return null;
}
const font = matchFont({ fontFamily: "Roboto", fontWeight: "bold", fontSize: 16 }, fontMgr);
return <Text text="Hello World" y={32} x={32} font={font} />;
};
matchFontはfontMgrを省略するとSkia.FontMgr.System()を使います。ネイティブ環境ではこれでOS標準フォントにマッチできます。
ただしWeb版のCanvasKitでは、matchFontが内部で呼び出すfontMgr.matchFamilyStyleが未実装で、呼び出すと例外を投げます。これはSkia.FontMgr.System()(前述の通り空のフォントマネージャーを返す)でも、useFontsで自前に読み込んだfontMgrでも同じで、fontMgrの有無にかかわらずmatchFont自体がWeb環境では使えません(react-native-skiaの現行バージョンでの制約で、本書のデモ環境を含むWeb全般に共通します)。本書でもこの制約のためmatchFontのライブデモは用意していません。Web対応が必要なアプリで太字・イタリックを切り替えたい場合は、matchFontに頼らず、あらかじめ複数のuseFontインスタンス(通常用・太字用など)を読み込んでおき、条件分岐でどちらを使うか自分で切り替える方法が安全です。
システムに入っているフォントファミリー名の一覧はlistFontFamilies()で取得できます(ネイティブのみ有効)。
Paragraph: 複雑なレイアウトを扱う
複数行・複数スタイルのテキストや、折り返し・整列を伴うレイアウトには<Text>ではなくParagraph APIを使います。Skia.ParagraphBuilderでスタイル付きテキストを組み立て、<Paragraph>で描画する流れです。
import { Skia, useFonts, TextAlign, Canvas, Paragraph } from "@shopify/react-native-skia";
const MyParagraph = () => {
const fontMgr = useFonts({
Roboto: [require("./assets/Roboto-Regular.ttf"), require("./assets/Roboto-Medium.ttf")],
});
if (!fontMgr) {
return null;
}
const paragraph = Skia.ParagraphBuilder.Make({ textAlign: TextAlign.Center }, fontMgr)
.pushStyle({ color: Skia.Color("black"), fontFamilies: ["Roboto"], fontSize: 50 })
.addText("Say Hello to ")
.pushStyle({ color: Skia.Color("black"), fontFamilies: ["Roboto"], fontSize: 50, fontStyle: { weight: 500 } })
.addText("Skia")
.pop()
.build();
return (
<Canvas style={{ width: 256, height: 256 }}>
<Paragraph paragraph={paragraph} x={0} y={0} width={300} />
</Canvas>
);
};
pushStyleでスタイルを積み、addTextでそのスタイルのテキストを追加、popで1つ前のスタイルに戻します。build()を呼ぶまではただのビルダーで、実際の描画には<Paragraph>にビルド結果を渡します。
<Paragraph>も<ImageSVG>と同様に独自のペイントルールを持つコンポーネントで、colorのようなpropsは効きません。ぼかしなどの効果をかけたい場合は<Group>のlayerpropを使います。
以下のデモはfontSizeとwidth(折り返し幅)を動かせるParagraphの例です。widthを狭めると、2行目の日本語がどう折り返されるか確認できます。
主なスタイルプロパティ
パラグラフ全体に効くプロパティ(ParagraphBuilder.Makeの第1引数)の一部です。
| Property | Description |
|---|---|
| textAlign | テキストの整列(left / right / center / justifyなど) |
| textDirection | テキストの方向(LTR / RTL) |
| maxLines | 最大行数 |
| ellipsis | 行が収まらない場合に使う省略記号 |
| heightMultiplier | 行の高さの倍率 |
pushStyleに渡すテキストスタイルの一部です。
| Property | Description |
|---|---|
| color | 文字色 |
| fontFamilies | フォントファミリー名の配列(フォールバック順) |
| fontSize | フォントサイズ |
| fontStyle | 太さ・幅・傾き({ weight, width, slant }) |
| letterSpacing | 文字間隔 |
| decoration | 装飾(下線・上線・打ち消し線) |
レイアウト情報を取得する
layout(width)を呼ぶと折り返し幅を確定でき、その後getHeight()やgetLongestLine()で高さ・幅が取得できます。文字の実際の輪郭に沿った厳密な範囲が必要な場合はgetPath(lineNumber)(Webでは非対応)や、行ごとにグリフの位置・輪郭を巡回できるextendedVisitも用意されています。詳しくは公式ドキュメントのParagraphを参照してください。
まとめ
シンプルな1行テキストなら<Text>とuseFont、複数行・複数スタイルのレイアウトが必要ならSkia.ParagraphBuilderと<Paragraph>を使う、という使い分けを押さえておけば十分です。次章ではさらに低レベルな、グリフ単位でテキストを扱うAPIを見ていきます。