猫の手ツール

Algorithm & UI/UX

Canvas座標変換と動的スケーリングによる「自由なレイアウト合成」の実装

本ツールでは、静的な画像合成ではなく、ユーザーがマウスやタップで「値札の位置を自由に動かせる」インタラクティブな体験を重視しています。デバイスを問わず、高画質な合成結果を得るためのスケーリング手法について技術的な裏側を解説します。

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

現行コードの確認日:

入力
商品写真とブランド名・価格を入力し、値札の形と位置を選びます。
処理
Canvasで値札の角丸背景と文字を描き、ドラッグ位置へ合成します。価格には地域形式の桁区切りと円記号を付けます。
出力
入力は長辺 2500px 以下に縮小し、JPEG(品質指定 0.9)で保存します。

まず確認する点:長いブランド名や価格を枠内へ自動縮小しません。文字の収まりを確認し、写真の被写体を避けて置きます。

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

仕組みを読み進めるときは、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)、File APIとObject URL(選んだ画像の読み込みとBlob URLの解放)が参考になります。

1. 解像度に依存しない「相対スケーリング」エンジンの構築

ユーザーが読み込む画像の解像度は、数百ピクセルから数千ピクセルまで幅があります。固定のピクセル値で値札を描画すると、高解像度画像では値札が豆粒のように小さくなってしまいます。

本実装では、描画領域の横幅を基準とした scale 変数を算出し、すべての描画(矩形のサイズ、角丸の半径、フォントサイズ、オフセット)にこの係数を掛けることで、どんな解像度の画像でも「見た目の比率」を一定に保つ設計にしています。

画像幅とサイズスライダーから値札の大きさを求める例です。size は 0.5〜2.0 を想定します。

function priceTagSize(imageWidth, size = 1) {
  const scale = imageWidth / 1000 * size;
  return { width: 320 * scale, height: 120 * scale,
    brandFont: 22 * scale, priceFont: 46 * scale };
}

2. タッチ/マウス共通の正規化座標変換

Canvas上の「ドラッグ操作」を実現するには、画面上のタッチ座標をCanvas内の内部座標系に正確に変換する必要があります。特に、ブラウザがCanvasをCSSで伸縮させて表示している場合(w-fullなど)、単純な座標取得ではズレが生じます。

本ツールでは getBoundingClientRect で取得した実際の表示サイズと、Canvasが持つ internal width/height の比率を用いて、入力座標を正規化しています。これにより、PCでもスマホでも、指で触れた場所にピタリと値札が吸い付く操作感を実現しました。

3. dynamic import による HEIC 変換の最適化

HEIC用の変換ライブラリを常に読み込むと、JPGやPNGだけを使う場合にも追加の通信が発生します。

本実装では import('heic2any') による動的インポートを採用し、ユーザーがアップロードしたファイルの拡張子が HEIC であることを検知した瞬間にのみライブラリをロードします。これにより、初期ロード速度の向上と、HEIC を選択したときの変換処理を両立させています。

出力の仕様と確認する点

値札は1つをドラッグして置きます。画像幅を基準に 0.5〜2 倍のサイズを選び、明色・暗色と角丸の形を切り替えます。被写体を検出して避ける機能はありません。

ブランド名は大文字へ変換し、空欄なら SHOP NAME。価格は数値を地域形式の桁区切りにして ¥ を付けます。税・割引・通貨換算は計算しません。長い名前や大きい金額には自動縮小がないため枠への収まりを確認します。

入力は長辺 2500px 以下に縮小し、JPEG(品質指定 0.9)で保存します。値札と写真は1枚の画像になり、保存後に値札だけを動かす編集データは含みません。