この記事はフラー株式会社 Advent Calendar 2021 の1日目の記事です。

2021年のJ2の順位変動を第1節から振り返るデータ可視化をやってみました。(たくさんの感動をありがとうアルビレックス!)

完成したデモはこちらです。

SvelteとBar Chart Race

Svelte Summit Spring 2021で “Declarative Data Visualization” という面白いトークがありました。今回はこのトークの内容を参考にしてJリーグデータのBar Chart Raceをつくってみようと思います。

SvelteとD3.jsを組み合わせて使用しますが,使うのはd3-scalescaleLinear*のみです。棒グラフの配置と横軸のticksを描くために使用します。 また,SVGではなくHTMLを使う点もポイントです。棒グラフや軸はdiv要素で表現します。棒の内部先端へのテキスト配置はSVGによる座標指定よりもボックスモデルのレイアウトの方が簡単に実装できます。

データを集める

J. League Data SiteでJリーグの公式記録を調べることができます。こちらのサイトの各節ごとの順位表からデータセットをつくりました。Webクローラーのソースコードはこちらのリポジトリで公開しています。

deno-puppeteer というDeno上で動くPuppeteerを使用しました。Denoはブラウザ標準との互換を目指しているのでURLのパース処理が簡単でした。

集まったデータはこんな見た目をしています。

[
  {
    "section": { "label": "第1節", "value": "1" },
    "rankRecords": [
      { "id": "niigata", "rank": 1, "name": "アルビレックス新潟", "points": 3 },
      { "id": "tokyov", "rank": 2, "name": "東京ヴェルディ", "points": 3 },
      ...
    ]
  },
  {
  "section": { "label": "第2節", "value": "2" },
  "rankRecords": [
    { "id": "niigata", "rank": 1, "name": "アルビレックス新潟", "points": 6 },
    { "id": "ryukyu", "rank": 2, "name": "FC琉球", "points": 6 },
    ...
  ]
  ...
]

アプリをつくっていく

データの準備ができたのでアプリをつくっていきます。Viteの svelte-ts テンプレートを使用します。

yarn create vite app --template svelte-ts

前述したSvelte Summitのトークと本家のデモアプリのコードのほぼそのままですが,こちらのリポジトリで公開しているのでよろしければご覧ください。

グラフのアニメーションは svelte/motion モジュールの tweenedによって実現しており,d3-transitionなどは使用していません。tweenedは書き込み可能なstoreで,値を変更すると変更前と変更後の値を補間しながら変化します。配列やオブジェクトを書き込むこともでき,2つの値が同じ形をしていれば「葉」の数値を補間してくれます。

例えば今回のデータでは,以下のように各節での順位を以下のように更新していきます。(配列の場合は要素の並びを保存しないとうまく補間できないので注意が必要です。)

// 第1節のデータ
const store = tweened([
  { id: "niigata", rank: 1, points: 3 },
  { id: "tokyov",  rank: 2, points: 3 }
]);

// 第2節のデータ
store.set([
  { id: "niigata", rank: 1, points: 6 },
  { id: "tokyov",  rank: 8, points: 3 }
]);

すると,storeの値は "niigata""points" は3から6に,"tokyov""rank" は2から8に線形補間しながら変化します。storeを購読しているコンポーネントは,その変化に応じてUIをアップデートするだけで簡単にTweenアニメーションを実現できます。

例えば以下の画像のように,シークバーの変化に合わせて節のデータをtweenedのstoreに書き込むだけでスムーズなアニメーションができます。

おわりに

Svelteはリアクティブな変数や双方向データバインディングを簡単に利用する手段を提供しているので,Bar Chart Raceのようなアプリを少ないコード量で実装できました。

実は来週末にJ2の最終節の試合が行われます。試合結果が出たら反映しておこうと思います。(2021年12月07日追記:最終節までの試合結果を反映しました。)