Algorithm & UI/UX
運転免許証写真をCSSガイドとCanvasで配置する仕組み
このツールは、顔位置のガイドを表示し、切り抜いた写真を印刷用の画像に並べます。ガイドは配置の補助で、顔の検出や提出先での受理判定は行いません。ここでは実装上の寸法と、印刷時に確認する点を説明します。
このツールの入力から出力まで
現行コードの確認日:
- 入力
- 証明写真に使う画像を選び、顔の位置・ズームを手で合わせます。
- 処理
- 規格の出力枠に合わせて写真をトリミングし、確認画面と保存画像を作ります。
- 出力
- 保存は 1051×1500px の白いシートに 283×354px の同じ写真を3列×3行で並べた JPEG(品質指定 0.95)です。
まず確認する点:ツール内の位置合わせと提出先の審査は別です。頭部の大きさ、背景、最新の提出要件を利用者が確認します。
実装の確認箇所:src/pages/license/photo/index.astro。以下のコード抜粋と出力条件は、この本番実装に基づいて説明します。
仕組みを読み進めるときは、Canvas APIと画像処理(画像寸法、drawImage、画素と透明度の扱い)、File APIとObject URL(選んだ画像の読み込みとBlob URLの解放)が参考になります。
1. CSS Pseudo-elementsによる「動的ガイドライン」
最も重要なのは、ユーザーがどの位置に顔を合わせればよいかを直感的に理解できるUIです。本ツールでは、Cropper.jsが生成するクロップ領域に対して、CSSの擬似要素を用いて「頭頂部」と「あご」のガイド線をオーバーレイさせています。
なぜこの実装か?
JavaScriptでCanvas上にガイドを描画する手法もありますが、プレビュー領域がリサイズされるたびに再計算が必要になります。CSSの擬似要素(::before/::after)とパーセント指定(%)を組み合わせることで、クロップ領域の大きさが変わっても常に適切な比率を維持したままガイドラインを追従させることが可能になります。
ガイドは切り抜き領域の上から 10% と 70% にあります。この割合を画面上の位置に換算する例です。
function licenseGuides(cropHeight) {
return { top: cropHeight * 0.10, chin: cropHeight * 0.70 };
} 2. L判プリントに最適化されたタイリングアルゴリズム
コンビニでの印刷コストを最小限にするため、L判1枚(約127mm×89mm)に9枚の写真を並べる仕様を採用しています。ここで課題となるのが、印刷時の「フチなし設定」による拡大率の変動と、個々の写真を縦 30mm×横 24mm 相当で配置するためのピクセル計算です。
なぜこの実装か?
Canvas上で直接「ミリメートル」を扱うことはできないため、L判 89×127mm に画像全体を配置した場合に約 300dpi となる寸法設計を行っています。3x3のグリッドを描画する際、等間隔に配置するだけでなく、切り取りやすさを考慮したマージン(余白)を動的に計算してキャンバスを構築しています。
3. クライアントサイドでのHEIC変換と軽量化ハック
このツールでは、HEICを選んだ場合はブラウザ内で別の画像形式に変換してから編集します。本ツールでは、サーバーを介さずクライアントサイドで完結させる(=プライバシーを守る)ため、heic2anyを用いた変換と、変換後の画像の画素数を抑えるリサイズ処理を行っています。
なぜこの実装か?
高解像度のままクロップ処理を行うと、低スペックなデバイスでは動作が著しく重くなります。そのため、一度Canvasに描画する際に「編集に支障のない最大解像度」へダウンスケールし、UIのレスポンスを確保しています。
出力の仕様と確認する点
入力は長辺 3000px 以下に縮小し、4:5 の範囲を 600×750px に切り抜きます。保存は 1051×1500px の白いシートに 283×354px の同じ写真を3列×3行で並べた JPEG(品質指定 0.95)です。
横の隙間 60px、縦の隙間 80px を含めて中央に配置します。印刷機の自動拡大やフチなし補正を制御する機能はないため、実際の用紙サイズ・倍率を合わせ、印刷後に定規で測ってください。
ガイド線は保存画像に含めません。背景色、表情、顔の遮蔽物、鮮明さ、撮影時期などの適否は判定しないため、提出先の最新の案内を確認します。写真の受理や印刷費用を保証するものではありません。