猫の手ツール

Algorithm & UI/UX

Canvas APIで実現する幻想的な光の表現:虹の光・フレア合成ツールの舞台裏

写真に「エモさ」を加えるライトリーク(光漏れ)や虹の表現。これらを画像素材に頼らず、ブラウザ上のCanvas APIのみで動的に生成・合成するためのテクニックを解き明かします。

1. 「Screen」ブレンドモードによる物理的な光の再現

このツールの肝は、Canvasの globalCompositeOperation'screen' を指定している点にあります。スクリーン合成は、背景の色と重ねる色を反転させて乗算し、再び反転させる処理です。

この手法のメリットは、「黒い部分は透過し、明るい部分だけが重なって明るくなる」という、現実の光学現象(プロジェクターの光を壁に投影するような状態)を極めて低コストに再現できることです。

// 光を合成するコア設定
ctx.globalCompositeOperation = 'screen';
ctx.globalAlpha = // ユーザーが指定した透明度の割合;

// ここで虹やフレアの描画を実行
// ...

2. プロシージャルな「虹の光」生成アルゴリズム

虹の表現には固定の画像ファイルを使用していません。createLinearGradient を使い、複数のカラーストップを精密に配置することで、コード上で虹を生成しています。

単なる七色ではなく、両端を透明にする(アルファ値を0にする)ことで、どんな写真にも自然に馴染む「淡い光」を実現しました。また、Canvasの rotate メソッドを組み合わせることで、光が差し込む角度を表現しています。

// 虹色グラデーションの生成
const grad = ctx.createLinearGradient(/* 開始座標, 終了座標 */);

// 独自のチューニングに基づいたカラー配置
grad.addColorStop(0.0, "rgba(255, 0, 0, 0)"); // 外側は透明
grad.addColorStop(0.2, "rgba(255, 0, 0, // 独自の強度)");
grad.addColorStop(0.5, "rgba(0, 255, 0, // 独自の強度)");
grad.addColorStop(0.8, "rgba(138, 43, 226, // 独自の強度)");
grad.addColorStop(1.0, "rgba(138, 43, 226, 0)"); // 反対側も透明

ctx.fillStyle = grad;
ctx.fillRect(/* 座標とサイズ */);

3. レンズフレアの「ゴースト」配置ロジック

サンフレア(レンズフレア)モードでは、光源から画面中心を通る直線上に出現する「ゴースト(光の輪)」を再現しています。

光源の座標 (x, y) とキャンバスの中心座標 (cx, cy) の差分ベクトルを計算し、その延長線上に複数の円形グラデーションを配置するアルゴリズムを採用しました。これにより、光源を動かすとゴーストも物理的に正しい方向へ連動して動く、リアルな操作感を提供しています。

// 光源と中心の距離に基づいた相対ベクトル
const dx = canvasCenter.x - flareSource.x;
const dy = canvasCenter.y - flareSource.y;

// ゴースト(光の輪)を描画する関数
const drawGhost = (distMultiplier, radius, color) => {
    // 独自の計算式で延長線上の座標を特定
    const gx = flareSource.x + dx * distMultiplier;
    const gy = flareSource.y + dy * distMultiplier;
    
    // 円形グラデーション等の描画処理
    // ...
};

// 異なる倍率で複数のゴーストを配置
drawGhost(/* 独自の係数1 */, /* サイズ1 */, "rgba(..., 0.3)");
drawGhost(/* 独自の係数2 */, /* サイズ2 */, "rgba(..., 0.2)");

Developer's Note

今回の実装で最もこだわったのは、「端末内完結のプライバシー保護」と「プレビューの軽快さ」の両立です。

リッチな光のエフェクトを実現するために巨大な透過PNG画像を重ねる手法も検討しましたが、それではユーザーがサイズを調整するたびにメモリ負荷が高まり、特にモバイルブラウザでクラッシュの原因になります。

あえてすべてのエフェクトを数学的なグラデーション(プロシージャル)で記述することで、プログラムのサイズを数KBに抑えつつ、リアルタイムでヌルヌル動く操作性を実現しました。また、HEIC形式の画像をブラウザで扱うために heic2any を動的インポートするなどの工夫も、初期読み込み速度の向上に寄与しています。