定義
Canvas 上に描くノード(グラフ・ネットワーク可視化の点)の色・サイズ・きらめきなどの装飾値を、毎フレーム Math.random() ではなく ノード ID から常に同じ値が出るように 計算し、描画直後に Canvas 2D コンテキスト状態(globalAlpha、shadowBlur など)をデフォルトに戻すレンダリングパターンである。
なぜ必要か
force-directed graph ライブラリは通常 Canvas 2D(ブラウザがピクセルバッファに直接描く API)でノードを描く。DOM 要素ではないため、CSS @keyframes をノード単位には直接当てられない。Math.random() で色を選ぶと、同じドキュメントノードがリロードのたびに異なる色・リズムで見え、グラフが不安定に見える。
きらめき・光のにじみを入れるには ノード draw コールバック内で 時間に応じた透明度を計算する必要がある。このとき globalAlpha や shadowBlur を設定したまま復元しないと、ラベル・エッジ・次のノードまでぼやける副作用が出る。prefers-reduced-motion(アニメーションを減らすアクセシビリティ設定)のユーザーにはきらめきを止める必要がある。
動作原理
- ID → ハッシュ: ドキュメント ID 文字列を小さな整数シードに変換する(暗号学的ハッシュは不要、視覚的分散が目的)。
- attach 時に visual メタ: グラフデータにノードが載るとき、色(hue/パレット index)、きらめき周期(period)、位相(phase)を一度計算してノードオブジェクトに保存する。
- draw コールバック: 毎フレーム
alpha = base + amplitude * sin(2π·t/period + phase)のように 透明度だけ 更新する。色パレットは固定。 - 光: 中心円はくっきり描き、
shadowColor・shadowBlurだけで浅いにじみを付ける。 - 状態復元: ノード 1 つ描いた直後に
ctx.globalAlpha = 1、ctx.shadowBlur = 0などでリセットする。 - reduced motion:
matchMedia('(prefers-reduced-motion: reduce')なら alpha=1 固定、グラフライブラリの自動 redraw(autoPauseRedraw)を有効にしてきらめきループを止める。 - 凡例の分離: ノード色がカテゴリを表さないなら、凡例の点は中立色を使う。
実務での適用
- 別の
requestAnimationFrameループを作るより 既存の graph redraw ループ を再利用する。 - 視覚回帰は reduced motion on/off、320px ビューポートでキャプチャ前に force layout の安定を待つ。
トレードオフ
- ID ハッシュパレットは衝突時に似た色になるが、数百ノード規模では許容できる。
shadowBlurは小半径・低ノード数向き。数千ノードではペイントコストが大きくなる。- Canvas アニメーションはメインスレッドで動く。重い場合はノード数・効果強度を下げるか WebGL レイヤーを検討する。
使ってはいけない場合
- ノードが数十個だけで DOM/SVG で足りるなら、Canvas カスタム draw は保守コストだけ増える。
- 色が カテゴリ符号 でなければならない場合、ID ハッシュ色と要件が衝突する — 凡例・データチャネルを分けるか、色スケールを明示的にマッピングする。
- reduced motion を無視してきらめき続けるのはアクセシビリティ違反である。
よくある間違い
globalAlpha・shadowBlurをリセットせず、テキストラベル全体がぼやける。- 毎フレーム ID 文字列を再ハッシュして CPU を無駄にする — attach 時点でメタを付ける。
- 背景は CSS できらめくのにノードは静止で UX がずれる — Canvas 側に別 twinkle ロジックが必要。
autoPauseRedrawの意味を逆に理解し、通常モードで redraw が止まる。
関連概念
- bfs-document-relation-graph — ローカルグラフデータ・BFS 探索
- critical-rendering-path — Canvas ペイントはメインスレッドのレンダーパス
- css-view-timeline-entry-animation — DOM 以外の Canvas ノードには CSS タイムライン不可