Algorithm & UI/UX
Canvasで実現する「鏡の反射」と回転対応のヒット判定
本記事では、画像を反転させて重ね合わせることで「鏡らしさ」を演出する反射ロジックと、自由な角度に配置されたオブジェクトを正確に操作するための座標変換アルゴリズムについて深掘りします。
このツールの入力から出力まで
現行コードの確認日:
- 入力
- 写真を選び、鏡越しに見せる枠や反転などの設定を調整します。
- 処理
- 写真の描画位置と反転、枠や重ねる表現をCanvasへ描いて1枚にします。
- 出力
- 長辺 1500px 以下の画像にスマホ風の図形を重ね、JPEG(品質指定 0.95)で保存します。
まず確認する点:元写真にある文字も反転する場合があります。実際の鏡の奥行きや反射の計算とは分けて、文字と顔の向きを確認します。
仕上がりの例
写真を鏡越しの自撮り風にした例です。
掲載画像はサイトにある既存の公開作例です。保存済みの資料では運営者の愛猫の写真を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。
保存済み検証記録の条件を見る
資料の種類は「ツールの出力画像を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。
- 記録の入力画像
- 800×751px (JPEG)
- 記録の出力画像
- 800×751px (JPEG)
| 画面の項目 | 記録の値 | 現行コードの初期値・範囲 |
|---|---|---|
| 記録で選択されていた項目 | ブラック | — |
| スマホの大きさ | 40 | 初期値 40 / 範囲 20〜100 |
| スマホの角度 | -15 | 初期値 -15(画像読込後。HTML定義は0) / 範囲 -45〜45 |
| 透け感(映り込み具合) | 20 | 初期値 20 / 範囲 10〜60 |
記録では「この画像で完成!」から完成表示へ進んでいます。初期値と記録値を比べてから、効果を1項目ずつ変えると作用を確認しやすくなります。
資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。
実装の確認箇所:src/pages/image/mirror-selfie/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。
仕組みを読み進めるときは、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)、File APIとObject URL(選んだ画像の読み込みとBlob URLの解放)が参考になります。
1. 反転描画による疑似反射アルゴリズム
「鏡越し自撮り」の最大の特徴は、スマホの画面内に「撮影者側の風景」が写り込んでいる点です。これを再現するために、単に画像を重ねるのではなく、元画像を左右反転(水平フリップ)させた上で、スマホの画面領域だけにクリッピングして描画する手法を採っています。
実装のメリット
- リアリティの向上: 鏡は像を反転させる性質があるため、反転描画を行うことで脳が「鏡である」と直感的に認識します。
- 軽量な合成処理: 複雑なレイトレーシングやシェーダーを使わず、Canvas標準の
scale(-1, 1)とclip()だけで完結しているため、画像の一部に反転画像を重ねられます。ぼかしには Canvas の filter を使うため、対応状況によって見え方が異なる場合があります。
点をスマホ中心の座標系へ移し、回転を逆に掛ける例です。angle は度数法、point と center は x・y を持つ値です。
function localPoint(point, center, angle) {
const a = -angle * Math.PI / 180;
const dx = point.x - center.x, dy = point.y - center.y;
return { x: dx * Math.cos(a) - dy * Math.sin(a),
y: dx * Math.sin(a) + dy * Math.cos(a) };
} 2. 回転行列を利用した逆座標変換ヒット判定
ユーザーがスマホを自由な角度に回転(-45°〜45°)できるUIにおいて、ドラッグ操作を成立させるには「クリックした地点が、回転したスマホの範囲内か」を判定する必要があります。ここでは、判定処理を単純化するために、マウス座標をスマホの回転角度に合わせて逆回転させることで、軸に平行な長方形としての判定を可能にしています。
実装のメリット
- 計算コストの低減: 複雑な多角形の当たり判定(交差判定)を行う代わりに、一次方程式の範囲チェックだけで済むため、毎フレームの判定が極めて軽量です。
- 直感的な操作感: 回転を戻した長方形の範囲で判定します。角丸の輪郭やカメラレンズ個別の形まで判定するものではありません。
出力の仕様と確認する点
長辺 1500px 以下の画像にスマホ風の図形を重ね、JPEG(品質指定 0.95)で保存します。元写真の人物や手を認識して持たせる処理はなく、スマホの位置を手動で決めます。
スマホの幅は写真の短辺の 20〜100%、高さは幅の 16/9 倍です。角度は −45〜45°。反射の濃さ 10〜60% は画面内に重ねる反転写真の濃さで、スマホ本体を透明にする設定ではありません。
反射は元画像の左右反転とぼかしによる演出です。鏡・カメラ・人物の3次元的な位置関係を計算していないため、光学的に正しい反射や自然な指との重なりを保証するものではありません。
