はじめに
以下の記事を拝見して、これまで描画系の実装経験が無いことに気づいたのをきっかけに、今回の「乗り物お絵かきアプリ」を作りました。
react-konva
というライブラリを使った記事で、ライブラリの説明がとても分かりやすく大変参考になりました。
今回筆者の個人開発もreact-konva
を用いてお絵かき機能を実装しています。
- 技術構成
@eslint/js@9.28.0
@tailwindcss/vite@4.1.8
@types/react-dom@19.1.5
@types/react@19.1.6
@vitejs/plugin-react@4.5.0
eslint-plugin-react-hooks@5.2.0
eslint-plugin-react-refresh@0.4.20
eslint@9.28.0
globals@16.2.0
konva@9.3.20
react-dom@19.1.0
react-konva@19.0.4
react@19.1.0
tailwindcss@4.1.8
typescript-eslint@8.33.0
typescript@5.8.3
use-image@1.1.4
vite@6.3.5
作成したもの
これは「タップ(クリック)すると色々な乗り物アイコンが表示されて、タップ(クリック)し続けている間は自由に線を引いてお絵かきできる」というシンプルなものです。
シール帳にシールを張るように、タップするだけで色々な乗り物を表示できます。
ちなみに、地味な機能ですがお絵かき中(ドライブ中)は乗り物のエンジン音が再生されます。
次項では、上記の音声再生などを含めて今回の実装で少し詰まった部分などを書いていきたいと思います。
自動再生ポリシー
自動再生ポリシーの影響でユーザーアクションが一度もないものは再生できないようになっています。
実装時、ログに当該エラーが出力されて気づくとともに、別の個人開発で同じような状況に遭遇したことがあったのでそれにならって今回対処しました。
オーディオ要素は非制御コンポーネントとしてuseRef
で扱い、DOM操作を行っています。
サイトの初期表示時に音声再生の注意喚起ボタンを用意していて、それを押下することで以下の処理が行われる仕組みです。
// 初回のユーザー操作で音声を再生するための関数
// ブラウザの自動再生ポリシーにより、ユーザーの操作がないと音声が再生されないため
const firstInteractionForAudio: () => void = () => {
setAudioPlayOn(true);
// サンプル再生なのでBGMは即時終了
audioRef_mov.current?.play();
audioRef_mov.current?.pause();
}
これにより、自動再生ポリシーの問題はクリアしました。
react-konva
のコンポーネント内にはDOM要素は配置できない
上記の自動再生ポリシーの問題は解消しましたが、修正時にaudio
タグを含んだ乗り物コンポーネントをStage
内に入れて実装を進めていたところエラーで画面が真っ白(レンダリング不可)になりました。
理由としては「react-konva
のStage
は内実canvas
要素であり、その中にDOM要素は置けない」というものでした。
react-konva
ライブラリでは、Stage
というコンポーネントをベースに、Adobe Ai や Ps のレイヤーのようなLayer
コンポーネント、線の描画などを担うLine
など固有コンポーネントがいくつかあります。
今回筆者は以下のような実装をしようとしており、レンダリングエラーが出ていたのです。
(※説明用の極端な実装例です)
<Stage>
<Layer>
<audio src="sound.mp3" /> {/* DOM要素なので不可 */}
<figure><img src={sampleImg} alt="sample画像" /></figure> {/* DOM要素なので不可 */}
</Layer>
</Stage>
先の説明通りreact-konva
の対象エリア内ではDOM要素は扱えないので、audio
や一部画像データは別々の要素(コンポーネント)として切り分けました。
ちなみに、描画時に表示される各種乗り物アイコンに関してはreact-konva
のImage
コンポーネントを使っています。
Image
のデータローディングに伴う非同期処理にはuse-image
を使っていて、これを使うことでシンプルに画像描画を実装できました。(※別に使わなくともuseEfeect
やuseState
でも実装できます)
さいごに
今まで使ったことがないようなものは個人開発で実装してみると良い経験になりますね。
簡単な個人開発でしたが、自動再生ポリシーやreact-konva
(canvas
要素)の制約などを学べて勉強になりました。
あとAI協業での学びで言えば、今回GitHub Copilot
を主に使用しましたが、実装はAIが担っていくのを前提としてその実装方針・アプローチを決めるための設計に関する知見(設計力) が重要だと実感しました。
具体的には、各種乗り物アイコンのLine
をState管理しているのですが、各種オブジェクト型の内容はこちらで指定(方針決め)して、描画部分など関連ロジックを含めた具体的な実装はGitHub Copilot
に行ってもらいました。
今回の個人開発GitHubを以下に置いておきますので、関心のある方はご自由に使ってください。