FrontEnd

React Konvaで状態管理されたCanvasを描画してみた

投稿日:

はじめに

着せ替えメーカーが流行っているらしく、自分でも作ってみたくなったので、技術調査をしました。着せ替えメーカーとは、用意されたキャラクターのパーツ(顔・目・髪・口など)を組み合わせたり、位置を調整したりしてオリジナルのキャラクターを作ることのできるサービスです。
スマホでの利用も考えていたので、異なる画面サイズでも、同じ比率でパーツを描画したり、位置調整をするか、という点が技術課題でした。この点を調べていくうちに、Canvasであれば、描画領域をスケールさせることで、Canvas内部の座標系を維持したまま拡大・縮小できることが分かったので、今回はCanvasと、CanvasをReactで使いやすくするためのReact Konvaについて調査しました。

Canvasとは?

Canvasとは、Web上で2Dグラフィックスを描画するためのAPIです。一番簡単な例だと、このようにCanvasに描画することができます。

以下のように、緑色の図形が描画されます。

このように、CanvasのAPI自体はすぐに試すことができるのですが、命令型のAPIとなっているので、状態の変化に応じてCanvasの内容を操作するようなアプリケーションを操作する場合、複雑な処理になることが想像できると思います。
また、Reactを使ってアプリケーションを構築したいため、相性の良いライブラリを探してみたところ、react-konvaが便利そうだったので、試してみたいと思います。

React Konvaとは

まず、Konvaは、Canvasのラッパーライブラリです。Canvasの命令型APIをラップし、Konva独自の「レイヤー」オブジェクト上に、図形などを追加したり、イベントトリガーを追加していくことで、Canavsへの描画などを行なってくれます。詳しくはこちらのサンプルを見てください。
React Konvaは、KonvaをReactで使いやすくするライブラリで、React Componentを宣言するようにCanvasを描画することができます。
以下が簡単な例です。(React Konvaのサンプルコードです。)

Stageコンポーネントから始まり、直下にLayer、そしてその配下に描画したいオブジェクトを並べていくと、実際にCanvasに描画されます。また、通常のReactコンポーネントと同じような使い勝手なので、状態管理してCanvasに描画するには、通常のDOMをレンダリングする場合と同じように、mapを使って描画していきます。
それでは今回は、konva reactを使って簡単な着せ替えアプリを作ってみたいと思います。

着せ替えアプリっぽいサンプルを作成

サンプルを作成しました。完成形はこちらです。

こちらが全体のソースコードです

React Konvaの導入

yarnでインストールします。

react-konvakonva が最低限必要なパッケージです。 use-image はURLから画像ファイルを読み、React Konvaの <Image /> コンポーネントに画像を読ませるためのhookです。

画像の描画

Canvasで画像を描画するには、React Konvaの <Image image={img} /> コンポーネントを使用するのですが、propsに渡す画像は、 CanvasImageSource という型になっています。

これらの型が意味するところはMDNで解説されているので詳しくは省略しますが、Canvas自体が画像を描画するには CanvasImageSource 型が必要となるため、React Konvaでもこの型で渡す必要があるのです。
今回はPNG画像を表示するのですが、その場合はimg要素に画像を読み込ませ、そのrefをImageコンポーネントに渡す必要があります。その実装の手間を省くため、konvajsが直々に公開している use-image hookです。

このhookはシンプルなソースコードなので一読して欲しいのですが、imgタグを生成し、そのrefを返しています。ただ、エラーハンドリングもやってくれているので、おそらくこのhookを使った方が、楽に実装できるのではないかなと思います。
今回は、 PartImage というコンポーネントを作り、ここで useImage hookを使って画像を取得し、 <Image /> コンポーネントを返しています。
以下は、先ほどのコードで実際に使っている部分のコードの切り抜きです。

 

stateによるCanvas描画

このサンプルでは stageParts というステートがCanvasに描画すべき値を持っているので、このstateの変化に応じてStageの内容を書き換えています。以下がその部分です。

stateをmapして、React Konvaで用意されたコンポーネントをを返していくだけで、Canvasに描画されていきます。通常のDOMと同じように、stateが変更されれば、Canvas内の描画も更新されるようになっているため、Reactに慣れているととても分かりやすいインターフェイスです。

レスポンスシブ対応

Stageのpropsには、widthとheightの他に、scaleX、scaleYも指定することができます。このスケールを設定することで、Canvas内を拡大・縮小することができます。例えば、次のようにスケールを0.5にすると、Canvas内は半分に縮小されて表示されるため、縦横ともに、実際の倍の1000ピクセル分の領域まで描画することができます。

この仕組みを使って、レスポンシブ対応をしてみました。その部分に関連するコードです。

ウインドウサイズの変更を契機に、親divのサイズを取得し setStageSize() でセットします。このサイズは直接Stageのwidthとheightに入ります。そして、scaleは stageSize / BASE_SIZE で求めます。これで、Stageのサイズが変化しても、Stage内はそのまま拡大・縮小されて表示できるようになりました。

画像のエクスポート

Stageの画像をエクスポートするには、Stageのrefを取得し、 toDataURL() を呼び出します。これはCanvasの関数で、Canvasに描画されている内容を画像化したData URIを取得することができます。
デフォルトではPNG画像が取得できるため、後はダウンロードしたり、どこかへアップロードしたりすることができます。今回は、ローカルにダウンロードするように実装しました。こちらが該当部分を切り抜いたソースです。

さいごに

React Konvaは、Reactの状態管理の仕組みと親和性が高く、アプリケーションに組み込みやすいことが分かりました。今後はReact Konvaが内部的にどのような仕組みCanvasに描画しているか、またそのレンダリングコストについても調査したいと思います。

おすすめ書籍

Reactハンズオンラーニング 第2版 ―Webアプリケーション開発のベストプラクティス プログラミングTypeScript ―スケールするJavaScriptアプリケーション開発

blog-page_footer_336




blog-page_footer_336




-FrontEnd
-,

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


関連記事

react-icon

React Big Calendarの複数タイムゾーン対応

1 はじめに2 端末のタイムゾーンのみに対応する場合3 複数のタイムゾーンに対応する。3.1 日時を指定のタイムゾーンに変換する3.2 イベントの開始日時・終了日時を動的に設定する3.3 ラップクラス ...

Cordovaの実機デバッグでハマった話

1 はじめに2 Cordovaとは2.1 React Nativeとの違い3 Cordovaでモバイルアプリを作る3.1 Node.jsをインストール3.2 Cordovaのコマンドラインツールをイン ...

JQueryとmark.jsでマークダウンのリアルタイムプレビューをつくる

1 はじめに1.1 環境2 mark.js2.1 公式ドキュメント2.2 インストール2.3 実際に使用してみる2.4 オプションについて3 プレビュー機能3.1 vue.jsに関して3.2 JQue ...

icon

TypeScript初心者が押さえておくべき型システム

1 はじめに2 型アノテーション2.1 プリミティブ型2.2 リテラル型2.3 any2.4 nullとundefined2.5 ユニオン型2.6 交差型2.7 タプル型2.8 列挙型2.9 配列2. ...

Vue.js 3.0のComposition APIを使ってみた

1 はじめに1.1 Composition APIとは1.2 環境構築2 Composition API での書き方2.1 function2.2 computed2.3 watch2.4 lifec ...

フォロー

blog-page_side_responsive

2022年9月
 123
45678910
11121314151617
18192021222324
252627282930  

アプリ情報

私たちは無料アプリもリリースしています、ぜひご覧ください。 下記のアイコンから無料でダウンロードできます。