Algorithm & UI/UX
Canvasで描く「叙情的な雨」と
軽量ブラーのハック
本ツールでは、外部ライブラリを一切使用せず、ブラウザ標準のCanvas APIのみで「雨粒越しの視界」を演出しています。モバイル端末でのパフォーマンスを維持しつつ、切なさを最大化するための独自の画像処理アルゴリズムについて解説します。
1. 低負荷な疑似ガウスぼかしの実装
通常、Canvasで画像をぼかすにはfilter = "blur(px)"を使用しますが、このプロパティは一部のモバイルブラウザ(特にSafari)で描画パフォーマンスが著しく低下したり、挙動が不安定になることがあります。
そこで本ツールでは、「オフスクリーンCanvasへの縮小描画」と「多重描画」を組み合わせた疑似ブラーハックを採用しました。まず画像を1/5〜1/10程度に縮小してピクセルを潰し、それを元のサイズに引き伸ばしながら、わずかに座標をずらして透明度を重ねて描画します。これにより、GPU負荷を抑えながら滑らかなボケ味を実現しています。
// 疑似ブラーのコアロジック概要
const scale = // ぼかし強度に応じた縮小率の計算
const spread = // 独自のサンプリング幅
// 1. 低解像度で中間バッファに描画
offCtx.drawImage(img, drawX * scale, drawY * scale, dw * scale, dh * scale);
// 2. 複数のオフセット(座標のズレ)を与えて多重合成
ctx.globalAlpha = // 合成用の透明度設定
const offsets = [[0,0], [1,1], [-1,-1], ...]; // 独自のサンプリングパターン
for (let [ox, oy] of offsets) {
ctx.drawImage(offCanvas, 0, 0, CW_Scaled, CH_Scaled, ox * spread, oy * spread, CW, CH);
} 2. ピクセル操作による「叙情的な色彩」の調整
「エモさ」の正体は、彩度を落としつつコントラストを微調整した独特のトーンにあります。CSSフィルタを使わず、CanvasのgetImageDataから取得した生ピクセルデータ(RGBA配列)を直接操作することで、各ピクセルの輝度に基づいた高度な色調補正を行っています。
具体的には、各ピクセルのRGB値からLuma(輝度)を算出し、その輝度値に寄せることで彩度を制御し、最後に独自の係数を掛けて全体的なトーンを落ち着かせています。
// ピクセルレベルの色調補正アルゴリズム
const imgData = ctx.getImageData(0, 0, CW, CH);
const data = imgData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i], g = data[i+1], b = data[i+2];
// 輝度の算出(独自の加重平均)
const luma = // 輝度計算のアルゴリズムが入ります
// 彩度の減衰と明るさの調整
data[i] = (r * // 独自の補正値 + luma * // 独自の補正値) * // 明るさの係数;
data[i+1] = (g * // 独自の補正値 + luma * // 独自の補正値) * // 明るさの係数;
data[i+2] = (b * // 独自の補正値 + luma * // 独自の補正値) * // 明るさの係数;
}
ctx.putImageData(imgData, 0, 0); 3. 雨粒のランダム生成とキャッシュ戦略
雨粒はellipse関数を用いて描画していますが、スライダーを動かすたびに全ての雨粒の位置をランダム生成すると、雨粒が画面上で「踊って」しまい、リアリティが損なわれます。
これを防ぐため、アプリ起動時に数千個の雨粒の座標、サイズ、透明度、および「伸び率(縦長具合)」を事前に計算して配列にキャッシュしています。ユーザーが密度スライダーを動かした際は、その配列の「参照するインデックス範囲」を増減させるだけでよいため、描画が非常に高速かつ安定します。
Developer's Note
一番こだわったのは、「窓越しの風景」をいかに数学的に再現するかという点です。単に雨のテクスチャを重ねるだけではなく、周辺減光(ヴィネット効果)を放射状グラデーションで加え、背景をぼかすことで、視点が自然と「窓ガラス」に固定されるような視覚的誘導を設計しました。
また、プライバシーへの配慮として「一切のデータをサーバーに送らない」という制約を課したため、HEIC形式の変換から画像処理まで全てブラウザ上のリソースで完結させる必要がありました。一見シンプルなツールですが、中身はフロントエンドの画像処理テクニックを凝縮した構成になっています。