猫の手ツール

Algorithm & UI/UX

サーバーレスで実現する「見切れない」画像加工の実装ロジック

縦長・横長の写真を切り取らずに正方形のCanvasへ置き、足りない部分を白または黒で埋めます。写真の読み込み、長辺2000pxへの縮小、中央への配置という処理を解説します。投稿先での表示や切取りは、サービス側の仕様にも左右されます。

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

現行コードの確認日:

入力
写真を1枚選び、余白を白または黒から選びます。
処理
写真全体を中央へ収め、長辺に合わせた正方形Canvasの足りない部分を選んだ色で埋めます。
出力
出力はJPEG品質0.95です。

まず確認する点:縦長写真には左右、横長写真には上下の余白が付きます。元が正方形なら余白は増えず、枠幅や写真位置を変える機能はありません。

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

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

1. モバイル安定化のための「プリ・サンプリング」処理

近年のスマートフォンで撮影された写真は解像度が極めて高く、そのままCanvasで編集・描画しようとすると、モバイルブラウザのメモリ上限に達してクラッシュすることがあります。本ツールでは、編集開始前に「編集に十分かつ負荷の少ない解像度」へダウンサンプリングを行っています。

メリット: 処理用の画素数を抑えられます。ただし画像のデコードやHEIC変換は縮小より先に行われるため、非常に大きな入力が必ず処理できるわけではありません。

読み込み・必要な縮小を終えた画像とCanvasを渡す例です。長辺を正方形の一辺にし、短辺側の不足分を左右または上下へ均等に分けます。

function drawSquare(canvas, image, background) {
  const w = image.width, h = image.height;
  const size = Math.max(w, h);
  canvas.width = canvas.height = size;
  const ctx = canvas.getContext('2d');
  if (!ctx) return;
  ctx.fillStyle = background;
  ctx.fillRect(0, 0, size, size);
  ctx.drawImage(image, (size - w) / 2, (size - h) / 2);
}

2. 動的インポートによる「オンデマンド」なHEIC変換

iPhoneの標準形式であるHEICは、ブラウザがネイティブでサポートしていない場合があります。しかし、変換ライブラリはファイルサイズが大きいため、初期ロードに含めるとサイトが重くなります。本ツールでは、HEICファイルが選択された時のみライブラリを読み込む「動的インポート」を採用しています。

メリット: 通常のJPG/PNGユーザーには余計なJSを読み込ませず、iPhoneユーザーが来た時だけ賢く対応することで、表示速度と機能性を両立しています。

3. Canvasによる「1:1 センタリング」アルゴリズム

正方形のキャンバスを作成し、そこに元の画像を配置する際の計算です。画像の長辺を基準に「正方形のサイズ(squareSize)」を決定し、短辺側の不足分を「余白」として均等に分配するロジックを実装しています。

メリット: 縦長・横長どんな比率の画像でも、一切トリミングすることなく「ちょうど真ん中」に、おしゃれな余白付きで配置されます。

出力の仕様と確認する点

出力はJPEG品質0.95です。長辺2000pxを超える入力は縮小するので、元の解像度を常に維持する仕様ではありません。透明部分も含め、出力画像は選択した背景色と合成されます。

縦長画像では左右、横長画像では上下に余白が付きます。元から正方形の画像には余白が増えず、四辺に一定幅の枠を付ける機能や余白幅の設定はありません。

白黒を切り替えて全体を確認し、投稿する写真の比率に合うか判断してください。Instagramのプロフィール表示を制御する機能ではないため、投稿前のサービス側プレビューでも見え方を確認します。