【マイレース開発記録(15)】canvas描画との戦い                    

「マイレース」は、入力した情報から計算した結果を、うさぎのレースとして“眺められる”ことが核になっているアプリです。
だからこそ、Canvas描画が安定しないとアプリ全体の体験が崩れます。

この回では、Canvasで「動くレース」を成立させるまでに詰まった点と、どう整理して前に進めたかをまとめます。

マイレースは、単に順位を出す占いではありません。
「仕事・金運・恋愛・健康・勝負」の5つを、それぞれ別のうさぎとして走らせ、速度とジャンプの違いから「流れ」と「リズム」を読み取れるようにしています。

そのためにCanvasで実現したかったのは、次の3点でした。

  • 5レーンを並走する
  • スピード差が視覚的に伝わる
  • ジャンプ(上下の揺れ)でリズムを感じられる

Canvasは自由度が高い反面、HTMLのように「勝手に整ってくれる」仕組みがありません。
描画の基本から、ひとつずつ積み上げる必要がありました。

Canvasは「座標で置く」ので、設計が曖昧だと破綻します。

今回のレースは、画面幅によって見え方が変わる前提なので、
「開始地点とゴール地点」を固定値で決めると、以下が起きました。

  • 画面が広いとレースが間延びする
  • 画面が狭いとゴールラインが見切れる
  • うさぎと文字が重なる

結果、座標計算を「Canvas幅に対する割合」で持つ方針に変えました。

function updateRaceBounds(){
SX = cv.width * 0.12;
GX = cv.width * 0.93;
}

固定値で逃げずに、描画領域の基準を数式に落とすことが必要でした。

Canvasの基本は、毎フレーム「描き直す」ことです。
最初は「一度描画すれば動く」感覚で考えてしまい、当然動きませんでした。

ここで理解したのが、

  • 更新:状態を進める
  • 描画:状態を画面に反映する
  • これを毎フレーム繰り返す

という分離です。

今回の実装はこの形に落ち着きました。

  • up():状態更新
  • dr():描画
  • requestAnimationFrame(lp):次のフレームへ

function lp(){
if(!st.r)return;
up(); dr();
requestAnimationFrame(lp);
}

この分離をしたことで、「どこが壊れているか」が追えるようになりました。

ジャンプは、単純に「上下に揺らす」だけだと面白くなりません。
そこで「占星術的なフェーズ(moon / mars)」を位相に使う設計にしました。

function j(r){
let ph=(r.k==="love"||r.k==="health")?st.p.moon:st.p.mars;
return Math.sin(st.f/45*2*Math.PI+ph*6)*8;
}

しかしこの段階で起きたのが、

  • 跳ねが大きすぎてレーンから飛び出す
  • うさぎが揃って跳ねてしまい“違い”が消える
  • フェーズが変わっても違いが分かりづらい

ここはAIとの対話がかなり役立ちました。
「式を変えれば解決」というより、何を表現したいのかを言語化し直したのが大きいです。

  • ジャンプは派手さではなく「リズムの差」
  • 跳ね幅はレーン内に収める
  • 位相は“ズレ”として効かせる

結果として振幅を控えめにして、差が見える方向に寄せました。

芝生背景(画像)を入れたことで雰囲気は出たのですが、そのままだと「レーンの文字」が読めなくなりました。

CanvasはCSSのように簡単に「背景を暗くする」ができないので、描画段階で半透明プレートを敷く方法にしました。

cx.fillStyle = "rgba(0, 0, 0, 0.28)";
cx.fillRect(0, 0, PLATE_W, cv.height);

この一枚で、UIが一気に“使える画面”になりました。
見た目の調整も、Canvasでは「描画処理の一部」になります。

見た目はCSSで width:100% にできますが、Canvasは厄介です。
CSSで見た目を変えても、内部座標は変わらず、描画がぼやけたりズレたりします。

今回は、極端にCanvasサイズを動的変更せず、

  • 見た目はCSSで調整
  • 内部座標は固定(560×180)
  • SX/GXを割合で調整

という「破綻しにくい方式」に寄せています。

これが結果的に、安定性を上げました。

Canvasで詰まったことは、技術的には細かい話が多いです。
でも就活目線で大事なのは、「詰まり方」と「解き方」だったと思います。

  • 曖昧なまま描くと破綻する → 基準を数式で固定する
  • ループがないと動かない → 更新と描画を分ける
  • 見えないUIは存在しない → 表示補助は描画に組み込む
  • レスポンシブはCanvasの外だけでは完結しない → 設計方針を決める

そして個人的には、ここがAI協働の良さが最も出た部分でした。

Canvasは「何が原因か分からないまま沼る」領域になりやすい。
自分の状況を言葉にして、仮説を立て、試して、整理する。
その反復が途切れにくくなりました。

次回は、Canvasを動かした上で

  • レース進行の気持ちよさ
  • ゾーン演出(CSSグロー)
  • “占い”として読めるUI

を整える話に入ります。

次の記事は。

【マイレース開発記録(16)】requestAnimationFrameの理解

として、動きの安定化と「止め方」「終わらせ方」の設計を書きます。

\ 最新情報をチェック /

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です