猫の手ツール

Algorithm & UI/UX

黄金角サンプリングによる高品質ブラーと二段階描画の最適化

本ツールでは、タイムラインで際立つ3:4比率の画像を生成する際、単なる単色の余白ではなく、被写体を活かした「背景ぼかし」を実現しています。ブラウザ負荷を抑えつつ、縮小画像を重ねたボケ感を作るためのコア・ロジックを紐解きます。

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

現行コードの確認日:

入力
写真を1枚選び、出力比率と余白の埋め方を選びます。
処理
写真全体を固定比率の枠へ中央配置し、足りない余白をぼかした写真・白・黒で埋めて書き出します。
出力
入力は長辺 2000px 以下、保存は 3:4 が 1200×1600px、16:9 が 1600×900px の JPEG(品質指定 0.95)です。

まず確認する点:保存画像全体とSNSが一覧で見せる範囲は異なる場合があります。端にある文字や顔が欠けない構図にします。

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

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

1. 黄金角スパイラルを利用した高密度サンプリング・ブラー

Canvasの filter = 'blur()' は手軽ですが、ブラウザの実装や解像度によってパフォーマンスが著しく低下したり、フチが白く抜けるなどの課題があります。本ツールでは、低解像度のテンポラリCanvasを作成し、それを「黄金角(約137.5度)」に基づいて螺旋状にオフセットさせながら多重描画する独自アルゴリズムを採用しています。

角度を黄金角ずつ進め、半径を広げながら重ねることで、描画位置を円盤内へ散らします。反復数は36回で、厳密なガウスぼかしや速度を保証するものではありません。

写真全体を切らずに枠へ収める contain の計算例です。背景の cover 配置とは倍率の選び方が逆になります。

function containImage(iw, ih, width, height) {
  const scale = Math.min(width / iw, height / ih);
  return { x: (width - iw * scale) / 2, y: (height - ih * scale) / 2,
    width: iw * scale, height: ih * scale };
}

2. プレビューと本番を分ける「二段階パイプライン」

ぼかしを再計算する画素数を抑えるため、内部では「表示用の軽量Canvas(Preview)」と「保存用の高精細Canvas(Final)」の二段階で処理を行っています。

ユーザーが操作している間は、オリジナルの50%以下の解像度で計算を行い、保存ボタンが押された瞬間にのみ保存用の規定寸法でレンダリングを実行することで、プレビュー時の縦横を保存用の 50% にしています。処理自体はメインスレッドで行います。

出力の仕様と確認する点

前景の写真全体を縦長の枠内へ収め、残った余白をぼかした写真・白・黒で埋めます。写真を画面いっぱいに切り抜く方式ではないため、横長写真でも端まで残せます。

ぼかし背景は縦横 20% の小画像を 36 回重ねます。ずらしの最大半径はプレビュー 10px、保存 20px。ガウスぼかしそのものではなく、前景の写真にはこのぼかしを適用しません。

入力は長辺 2000px 以下、保存は 3:4 が 1200×1600px、16:9 が 1600×900px の JPEG(品質指定 0.95)です。SNS 上での表示サイズ・切り抜きは投稿先にも依存するため、重要な文字は投稿プレビューでも確認します。