猫の手ツール

Algorithm & UI/UX

証明写真の規格をCSSとCanvasで制する

運転免許証の写真は、単なる「縦横比」だけでなく、頭頂部やあごのラインといった「顔の配置」に厳しい規定があります。本ツールがどのようにして、専門知識のないユーザーでも警察署で受理されるデータを作成可能にしているのか。その実装の核心に迫ります。

1. CSS Pseudo-elementsによる「動的ガイドライン」

最も重要なのは、ユーザーがどの位置に顔を合わせればよいかを直感的に理解できるUIです。本ツールでは、Cropper.jsが生成するクロップ領域に対して、CSSの擬似要素を用いて「頭頂部」と「あご」のガイド線をオーバーレイさせています。

なぜこの実装か?

JavaScriptでCanvas上にガイドを描画する手法もありますが、プレビュー領域がリサイズされるたびに再計算が必要になります。CSSの擬似要素(::before/::after)とパーセント指定(%)を組み合わせることで、クロップ領域の大きさが変わっても常に適切な比率を維持したままガイドラインを追従させることが可能になります。

/* クロップ領域内の顔配置ガイドライン */
.cropper-view-box::before {
    content: '--- 頭頂部 ---';
    position: absolute;
    top: 10%; /* 独自のチューニング値:免許証規定の余白を担保 */
    /* ...中略... */
}
.cropper-view-box::after {
    content: '--- あご ---';
    position: absolute;
    top: 70%; /* 独自のチューニング値:顔の大きさを規定内に収める */
    /* ...中略... */
}

2. L判プリントに最適化されたタイリングアルゴリズム

コンビニでの印刷コストを最小限にするため、L判1枚(約127mm×89mm)に9枚の写真を並べる仕様を採用しています。ここで課題となるのが、印刷時の「フチなし設定」による拡大率の変動と、個々の写真が正確に「30mm×24mm」で出力されるためのピクセル計算です。

なぜこの実装か?

Canvas上で直接「ミリメートル」を扱うことはできないため、標準的なDPI(300dpi相当)に基づいた解像度設計を行っています。3x3のグリッドを描画する際、等間隔に配置するだけでなく、切り取りやすさを考慮したマージン(余白)を動的に計算してキャンバスを構築しています。

function renderOutput() {
    // 印刷用の高解像度キャンバスを生成
    const canvas = document.createElement('canvas');
    // L判比率に基づいた独自の解像度設定
    canvas.width = // 独自のチューニング値;
    canvas.height = // 独自のチューニング値;
    
    // ここでマージンと各写真の配置座標を計算
    // ループ処理により3x3の配置を実行
    for (let row = 0; row < 3; row++) {
        for (let col = 0; col < 3; col++) {
            // 精密な座標計算による描画
            const x = // 独自のグリッド計算ロジック;
            const y = // 独自のグリッド計算ロジック;
            ctx.drawImage(originalCroppedCanvas, x, y, targetW, targetH);
        }
    }
}

3. クライアントサイドでのHEIC変換と軽量化ハック

iPhoneの標準画像形式であるHEICをそのままブラウザで扱うことはできません。本ツールでは、サーバーを介さずクライアントサイドで完結させる(=プライバシーを守る)ため、heic2anyを用いた変換と、メモリパンクを防ぐためのリサイズ処理を並列で行っています。

なぜこの実装か?

高解像度のままクロップ処理を行うと、低スペックなデバイスでは動作が著しく重くなります。そのため、一度Canvasに描画する際に「編集に支障のない最大解像度」へダウンスケールし、UIのレスポンスを確保しています。

// HEIC変換と最大解像度の制限
async function handleFile(file) {
    let blob = file;
    if (isHEIC(file)) {
        // ここでライブラリを用いたHEIC -> JPEG変換処理
    }

    // 安定性のためのリサイズ処理
    const MAX_RESOLUTION = 3000; // 動作安定のためのしきい値
    const resizedBlob = await new Promise((resolve) => {
        // Canvasを用いて画像を適切なサイズに縮小
        // ここにリサイズ計算ロジックが入ります
    });
    
    // この後の編集フェーズへBlobを渡す
}

Developer's Note

証明写真は、写真機で撮れば800円以上、アプリの「証明写真プリント」メニューでも200円ほどかかります。しかし、中身はただのデジタルデータです。普通の「L判写真」として30円で印刷すれば、残りのコストは削れるはずだ――。そんな節約志向からこのツールは生まれました。

最も苦労したのは、やはり「警察署の審査」との戦いです。頭上の余白が数ミリ足りないだけで受理されないケースがあるため、ガイド線の位置は何度も実測とテストを繰り返して調整しました。ユーザーが「ただ線を合わせるだけ」で、プロが撮影したようなバランスのデータを持てること。それが、このシンプルの裏側にある最大のこだわりです。