猫の手ツール

Algorithm & UI/UX

ブラウザ完結型・顔隠しスタンプツールの高度な座標変換とリソース管理

「画像をサーバーに送らずに加工したい」というプライバシー要求と、「スマホでもサクサク快適に操作したい」という操作性を両立させる。本稿では、HTML5 Canvasを用いたレスポンシブな座標計算と、メモリ効率を考慮した履歴管理の仕組みを解剖します。

このツールの入力から出力まで

現行コードの確認日:

入力
写真を選び、絵文字とサイズを指定して隠したい場所をタップします。
処理
CSS表示領域からCanvas座標へタップ位置を換算し、絵文字を文字として描きます。操作前のImageDataを容量制限付き履歴へ入れます。
出力
長辺 1500px 以下の JPEG(品質指定 0.95)を保存します。

まず確認する点:絵文字の字形は端末のフォントに依存します。見た目の大きさと実際に不透明に覆う範囲を分けて、顔の端や字形の空白を確認します。

仕上がりの例

写真の一部を猫スタンプで覆った例です。

加工前・加工後
左はソファに座る茶トラの愛猫の写真。右は猫の体の一部に黄色い猫スタンプを重ねた画像。

加工前・加工後

左はソファに座る茶トラの愛猫の写真。右は猫の体の一部に黄色い猫スタンプを重ねた画像。

掲載画像はサイトにある既存の公開作例です。保存済みの資料では運営者の愛猫の写真を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。

保存済み検証記録の条件を見る

資料の種類は「ツールの出力画像を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。

記録の入力画像
800×1066px (JPEG)
記録の出力画像
800×1066px (JPEG)
保存済み記録の主な操作・出力条件
画面の項目記録の値現行コードの初期値・範囲
記録のスタンプ🐾—
記録のスタンプサイズ特大—

資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。

実装の確認箇所:src/pages/image/stamp-hide/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。

仕組みを読み進めるときは、メモリ管理と画像処理(画素バッファ、縮小と容量予算)、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)が参考になります。

1. レスポンシブ環境における座標マッピング

このツールの最大の特徴は、表示上のサイズ(CSSピクセル)と、Canvasの内部解像度(実際の画像ピクセル)を動的に同期させる座標計算アルゴリズムにあります。画面サイズに合わせてプレビューが縮小されていても、常に「タップした正確な位置」にスタンプを配置するため、独自のスケール変換ロジックを実装しています。

getBoundingClientRect() を用いて画面上の描画領域を取得し、Canvasの内部解像度との比率を算出することで、デバイスの種類を問わない精密な編集を可能にしています。

スタンプの大きさと影は長辺を基準にします。ratio はサイズ選択に対応する割合です。

function stampMetrics(width, height, ratio) {
  const size = Math.max(width, height) * ratio;
  return { size, shadowBlur: size * 0.1, shadowOffsetY: size * 0.05 };
}

2. 縦横比を維持するリサイズとHEIC対応

ブラウザのメモリ消費を抑えつつ、保存時の画質を担保するため、読み込み時に動的なリサイズ処理を施しています。長辺が特定の閾値を超える巨大な画像のみを、アスペクト比を維持したまま縮小することで、後続の描画に使う画素数を減らします。クラッシュしない保証ではありません。

また、iPhone独自のHEIC形式をクライアントサイドでJPEGに変換する非同期読み込みを採用しており、サーバーを介さない「プライバシー重視」のアーキテクチャを実現しています。

3. ImageDataを活用した高速な履歴管理(Undo)

Canvas上の編集を元に戻す機能には、ピクセル情報を直接保持する ImageData 配列によるスタック管理を採用しました。各ステップの状態を丸ごとバッファに保存することで、複雑なベクトル情報を保持することなく、過去の操作を再実行せずに1枚分の画素を戻します。

履歴によるメモリ使用を抑えるため、ImageDataのbyteLengthを数え、32MiBの予算を超える場合は古い履歴から順に破棄します。1枚が予算より大きい状態は追加せず、元画像など履歴以外のメモリはこの予算に含みません。

出力の仕様と確認する点

スタンプは絵文字を Canvas の文字描画で置きます。端末の絵文字フォントにより形や色が違い、画像上の顔を検出して大きさを合わせる処理はありません。

タップした場所へその時点のサイズで描き、後からスタンプ単体を移動する機能はありません。失敗した場合は Undo して置き直します。履歴はImageDataの合計容量を32MiB以内に制限します。枚数は画像寸法によって変わり、予算を超える古い履歴から破棄します。

長辺 1500px 以下の JPEG(品質指定 0.95)を保存します。絵文字の輪郭外や字形の空白から顔が見える場合があるため、保存画像を確認してください。スタンプを置くだけで匿名性が保証されるわけではありません。