Algorithm & UI/UX
映画の「情緒」をCanvasで再現する:
動的タイポグラフィと描画最適化の裏側
映画風の字幕を写真に重ねる際は、文字の縁取り、画像に対する文字サイズ、黒帯との位置関係が見え方を左右します。この解説では、実際に使っている比率と描画順を説明します。
このツールの入力から出力まで
現行コードの確認日:
- 入力
- 写真と字幕の文章を入力し、位置や文字の見た目を調整します。
- 処理
- 写真をキャンバスへ描いた上に、字幕の文字と縁取りを重ねます。
- 出力
- 長辺 1500px 以下に縮小した写真を、元の縦横比の JPEG(品質指定 0.95)で保存します。
まず確認する点:文字の内容から自動で映像を生成しません。長い行のはみ出しや、明るい背景での輪郭の読みやすさを確認します。
仕上がりの例
写真に黒い帯と字幕を加え、映画風にした例です。
掲載画像はサイトにある既存の公開作例です。保存済みの資料では運営者の愛猫の写真を使用しています。今回の確認日が、この作例を実行した日を表すわけではありません。
保存済み検証記録の条件を見る
資料の種類は「ツールの出力画像を保存した記録」です。公開作例と保存済み記録で素材や色などが異なる場合があるため、下の数値は保存済み検証記録の条件として読んでください。
- 記録の入力画像
- 800×1222px (JPEG)
- 記録の出力画像
- 800×1222px (JPEG)
| 画面の項目 | 記録の値 | 現行コードの初期値・範囲 |
|---|---|---|
| 記録で選択されていた項目 | 明朝体、あり、 | — |
| メイン字幕 | 本当にこれでよかったのかな | 初期値 本当にこれでよかったのかな |
| サブ字幕 (英語など) | Was this really the right choice? | 初期値 Was this really the right choice? |
記録では「この画像で完成!」から完成表示へ進んでいます。初期値と記録値を比べてから、効果を1項目ずつ変えると作用を確認しやすくなります。
資料ファイルの作成日は実行日として扱っていません。個別端末の処理速度や、ほかの写真に対する検出・加工の精度を測定した資料でもありません。
実装の確認箇所:src/pages/image/movie-subtitles/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。
仕組みを読み進めるときは、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)、File APIとObject URL(選んだ画像の読み込みとBlob URLの解放)が参考になります。
1. 視認性を極める「多重描画アルゴリズム」
映画の字幕において最も重要なのは、背景がいかなる色であっても文字がはっきりと読めることです。
本ツールでは、strokeText(外枠)と fillText(塗り)を組み合わせるだけでなく、外枠の太さをフォントサイズに比例して動的に算出(Dynamic Stroke Weight)しています。また、lineJoin = 'round' を適用することで、角の鋭利さを抑えた「フィルム上映」のような柔らかい質感を演出しています。
字幕の黒縁と塗りを重ねる最小例です。ctx の font・位置合わせは描画前に設定します。
function outlinedSubtitle(ctx, text, x, y, fontSize) {
ctx.lineJoin = 'round';
ctx.lineWidth = Math.max(3, fontSize * 0.12);
ctx.strokeStyle = 'rgba(0,0,0,0.85)';
ctx.fillStyle = '#fff';
ctx.strokeText(text, x, y);
ctx.fillText(text, x, y);
} 2. 画像の高さを基準にした文字サイズ
スマホの写真は、4Kクラスの高解像度から古い低解像度のものまで千差万別です。
フォントサイズを固定値にすると、高解像度画像では文字が豆粒のようになり、逆に低解像度では画面を埋め尽くしてしまいます。この問題を解決するため、画像の高さ(Height)を基準に「メイン字幕は5%」「サブ字幕は2.5%」といった比率計算を画像読み込み時に実行し、初期サイズを画像に合わせています。長文が必ず収まるという意味ではありません。
3. パフォーマンスと機能性を両立する「遅延インポート」
iPhoneで標準的なHEIC形式をブラウザで扱うには重量級の変換ライブラリが必要です。しかし、すべてのユーザーがHEICを使うわけではありません。
本ツールでは、ファイル読み込み時に拡張子を判定し、HEIC形式の場合のみ dynamic import を用いて heic2any モジュールを非同期でロードします。これにより、通常のJPG/PNG利用ユーザーのHEIC 用コードの取得を省けます。LCP の改善量を測定した説明ではありません。
出力の仕様と確認する点
長辺 1500px 以下に縮小した写真を、元の縦横比の JPEG(品質指定 0.95)で保存します。黒帯を有効にすると画像の上下 12% ずつを覆うため、その部分の写真は隠れます。
メインとサブの字幕はそれぞれ改行できます。行間は文字サイズの 1.3 倍で、メインとサブの間にも余白を入れます。自動折り返しはないため、横に長い文章は手動改行してください。
字幕はひとまとまりとしてドラッグします。字幕ファイル(SRT 等)や動画を書き出す機能ではなく、文字を写真へ焼き付けます。画像端と黒帯への重なりを保存前に確認します。
