猫の手ツール

Algorithm & UI/UX

Canvasで再現する「テレビ番組風」テロップの多重縁取りと座標変換

ブラウザ上で「バラエティ番組のような派手な縁取り」をどう実現するか。単なる文字入れツールを超えた、Canvas 2D APIの使いこなしとレスポンシブ対応の勘所を解説します。

1. Canvasにおける「多重縁取り」のレンダリング・アルゴリズム

バラエティ番組のような視認性の高いテロップを再現するため、本ツールではCanvasの strokeText を重ね書きする手法を採用しています。

Canvas標準の機能では一度に1つの縁取りしか設定できませんが、太い枠から順に描画し、最後に塗り(fillText)を重ねることで、多重の縁取りを実現しています。この際、lineJoin = 'round' を設定することで、文字の角が尖らずに滑らかなテレビ風の質感を表現しています。

function renderMultiStrokeText(ctx, line, x, y, fontSize) {
    ctx.lineJoin = 'round';
    
    // 1. 最背面:極太の白枠
    ctx.lineWidth = fontSize * // 独自のチューニング値;
    ctx.strokeStyle = '#ffffff';
    ctx.strokeText(line, x, y);
    
    // 2. 中間:太めの黒枠
    ctx.lineWidth = fontSize * // 独自のチューニング値;
    ctx.strokeStyle = '#1e293b';
    ctx.strokeText(line, x, y);
    
    // 3. 最前面:メインの文字色
    ctx.fillStyle = '#fde047';
    ctx.fillText(line, x, y);
}

2. 解像度依存を排除する座標同期スケーリング

このツールはレスポンシブ対応のため、CanvasをCSSで伸縮させています。しかし、ドラッグ操作でテロップを動かす際、マウスの座標(表示サイズ)とCanvas内部のピクセル座標(論理サイズ)が一致しないという問題が生じます。

これを解決するため、getBoundingClientRect() から算出される「表示上の比率」を常に計算し、座標を正規化するアルゴリズムを組み込みました。これにより、PCでもスマホでも、触った場所をそのまま動かせる直感的なUXを提供しています。

function getCanvasPosition(event, canvas) {
    const rect = canvas.getBoundingClientRect();
    
    // 表示サイズと内部ピクセル解像度の比率を算出
    const scaleX = canvas.width / rect.width;
    const scaleY = canvas.height / rect.height;

    // クライアント座標をCanvas内の座標系へマッピング
    const clientX = event.touches ? event.touches[0].clientX : event.clientX;
    const clientY = event.touches ? event.touches[0].clientY : event.clientY;

    return {
        x: (clientX - rect.left) * scaleX,
        y: (clientY - rect.top) * scaleY
    };
}

3. 端末内完結を実現する動的リサイズとHEIC処理

「プライバシー重視」を掲げる本ツールでは、すべての処理をブラウザ内で完結させる必要があります。特にiPhoneのHEIC形式への対応と、メモリ不足によるクラッシュ防止が課題でした。

巨大な画像が読み込まれた場合、アスペクト比を維持しながら MAX_SIZE (1500px程度)に自動縮小するロジックを実装しています。オフスクリーンCanvasを用いて再描画することで、画質を維持しつつ、後続のテロップ合成処理のパフォーマンスを最適化しています。

async function processAndResize(file) {
    // ここにHEICからJPEGへのデコード処理が入ります
    // ...
    
    const MAX_RESOLUTION = // 独自のチューニング値;
    let { width, height } = originalImage;

    // 解像度制限を超えている場合のスケーリング
    if (width > MAX_RESOLUTION || height > MAX_RESOLUTION) {
        const scale = MAX_RESOLUTION / Math.max(width, height);
        width = Math.floor(width * scale);
        height = Math.floor(height * scale);
    }
    
    // オフスクリーンCanvasでリサイズされた画像を生成
    // これにより以降の描画負荷を一定に保つ
}

Developer's Note

テレビ番組を見ている時、私たちは無意識にテロップの色やフォントから「感情」を読み取っています。この「空気感」をWebツールで再現するために、フォントのウェイトや縁取りの太さのバランスには数ピクセル単位でこだわりました。

特に苦労したのは、バラエティ風の「斜体(Italic)」と「極太縁取り」の組み合わせです。Canvasの strokeText は文字の外側だけでなく内側にも太くなるため、計算を誤ると文字が潰れてしまいます。本ツールではフォントサイズに対する最適な比率を動的に計算することで、どんな長さの文章でも「それっぽく」見えるように調整しています。

「ただの文字入れ」ではなく、「演出を楽しめるツール」として、日常の何気ない写真をエンターテインメントに変えるお手伝いができれば嬉しいです。