猫の手ツール

Algorithm & Coordinate Logic

Canvas座標の正規化と
マルチタッチ制御の最適化

サインという「個人の筆跡」をデジタルで忠実に再現するためには、画面サイズに依存しない座標計算と、モバイル特有の挙動をねじ伏せる実装が必要です。

1. レスポンシブ環境における「表示サイズ」と「内部解像度」のズレ補正

Canvas要素は、CSSで指定された「表示サイズ(getBoundingClientRect)」と、Canvas自体が持つ「内部解像度(width/height属性)」が異なる場合があります。この比率を考慮せずに描画を行うと、マウスの位置と実際に線が引かれる位置がズレてしまいます。

本ツールでは、描画イベントが発生するたびにスケール比(scaleX / scaleY)を算出し、座標をリアルタイムに正規化しています。これにより、PCのブラウザからスマホの小さな画面まで、等倍感覚での滑らかな描画を可能にしました。

// 描画座標の正規化ロジック
function getPointerPos(e) {
    const rect = canvas.getBoundingClientRect();
    
    // イベント種別(Mouse/Touch)に応じたクライアント座標の抽出
    const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
    const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;

    // キャンバス内部解像度と表示サイズの比率を算出
    const scaleX = canvas.width / rect.width;
    const scaleY = canvas.height / rect.height;
    
    return {
        // ここで表示上の座標をキャンバス内部の絶対座標に変換
        x: (clientX - rect.left) * scaleX,
        y: (clientY - rect.top) * scaleY
    };
}

2. `passive: false` によるデフォルト挙動の完全抑制

スマートフォンでCanvasに指を置くと、通常はページ全体のスクロールが優先されてしまいます。これを防ぐために touch-action: none をCSSで指定しつつ、JavaScript側ではイベントリスナーの passive オプションを明示的に false に設定しています。

これはモダンブラウザにおける「スクロールの最適化(デフォルトでパッシブになる挙動)」をあえて無効化する処理です。描画処理(preventDefault)を確実に実行させることで、サイン中の意図しない画面ガタつきを徹底的に排除しています。

// スクロールを防止して描画を最優先させる設定
canvas.addEventListener('touchstart', startDrawing, { 
    passive: false // ブラウザのデフォルト最適化を無効化 
});
canvas.addEventListener('touchmove', (e) => {
    // 描画中のみデフォルトのスクロール挙動をキャンセル
    if (isDrawing) {
        e.preventDefault();
        // ここに描画ロジックが入ります
    }
}, { passive: false });

Developer's Note

「電子サイン」という極めてプライベートな情報を扱うツールだからこそ、技術的には「いかに何もしないか」を追求しました。具体的には、外部サーバーとの通信を一切行わず、すべての描画と画像生成をクライアントサイドのメモリ上だけで完結させています。

また、iPhoneユーザー向けにわざわざ「画像長押し」を案内するモーダルを実装したのは、モバイルSafari特有の「ダウンロード属性の制限」を考慮した結果です。単に「作れる」だけでなく、ユーザーが最終的に手元の端末に確実に保存できるまでを一つの「実装」と捉えてこだわりました。