3
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JSでgithubの草を作る(Cal-Heatmap)

Last updated at Posted at 2023-09-09

Cal-Heatmap

githubの草(ヒートマップカレンダーと言います)のようなグラフをwebアプリで実装しようと調べまくってた時に、結構良いライブラリを発見したので、書いていきたいと思います。
jsonのようなデータを入れるだけで、カレンダーを一発で生成でき、かつ細かい設定もいじることができる優れものです。cssの羅列を書くよりはよっぽどマシだと思いました。

めっちゃがんばると、こんなのができたりします。そっくりですね
スクリーンショット 2023-09-09 10.17.19.png

インストールとか

CDNとNPMに対応しています。

CDN

index.html
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://unpkg.com/cal-heatmap/dist/cal-heatmap.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/cal-heatmap/dist/cal-heatmap.css">

NPM

$npm install cal-heatmap
main.js
import CalHeatmap from 'cal-heatmap';
import 'cal-heatmap/cal-heatmap.css';

カレンダーのペイント

HTMLにレンダリングする場合、カレンダーの入るdivタグを入れておきます。
後にセレクトするため、idやらclassやらを指定しておくとよさそうです。

index.html
<div id="cal-heatmap"></div>

HTML側での操作はこれで以上なので、ここからはJSだけで作っていきます。
まず、CalHeatmapクラスを呼び出し、paintメソッドでインスタンスを構築していきます。

main.js
const cal = new CalHeatmap();
cal.paint();

Options

paintメソッドでは、引数のOptionsオブジェクトによってカレンダーをカスタマイズする役割があります。今回は重要なところ以外は省かせていただきます。

Type Signature
type Options = {
  itemSelector: Element | string,
  range: number,
  domain: DomainOptions,
  subDomain: SubDomainOptions,
  verticalOrientation: boolean,
  date: DateOptions,
  data: DataOptions,
  label: LabelOptions,
  animationDuration: number,
  scale: ScaleOptions,
  theme: 'light' | 'dark',
}

itemSelector

ここでレンダリングするタグをセレクトします。DOMかW3Cセレクターに対応しています。

main.js
cal.paint({ itemSelector: '#cal-heatmap' });

// または
cal.paint({ itemSelector: document.getElementById('cal-heatmap') });

domain

カレンダーのドメイン(画像の赤枠)の要素やフォーマットを設定できます。
スクリーンショット 2023-09-09 13.06.18.png

main.js
cal.paint({ 
    domain:{
        type: string,
        gutter: number,
        padding: [number, number, number, number],
        dynamicDimension: boolean,
        label: LabelOptions,
        sort: 'asc' | 'desc',
    }
});
  • type: ドメイン1つ分の期間(default: 'hour'

  • gutter: ドメイン同士の間隔px(default: 4

  • padding: ドメインのパディング(default: [0,0,0,0]

  • label: ドメインのラベル(画像の赤枠)を設定できます。
    スクリーンショット 2023-09-09 13.30.39.png

  • sort : ドメインの順序。ascで昇順、descで降順(default : asc

subDomain

ドメイン内の子要素(画像の赤枠)の要素やフォーマットを設定できます。
スクリーンショット 2023-09-09 13.45.17.png

main.js
cal.paint({ 
    subdomain:{
        type: string,
        gutter: number,
        width: number,
        height: number,
        radius: number,
        label:
        | string
        | null
        | ((timestamp: number, value: number, element: SVGElement) => string),
        color:
        | string
        | ((timestamp: number, value: number, backgroundColor: string) => string),
    }
});
  • type: 子要素の期間を指定します。親のドメインで設定したtypeよりも小さい単位を設定する必要があります。(例:Domain: year => subDomain: month

  • gutter: 子要素同士の間隔px(default: 2

  • width: 子要素の横幅(default: 10

  • height: 子要素の縦幅(default: 10

  • radius: 子要素の角の丸み(default: 0

  • label: 子要素の中にラベルを入れられます。
    スクリーンショット 2023-09-09 14.44.07.png

  • color: 子要素のlabelの文字色(default : black

  • sort : 子要素の順序。ascで昇順、descで降順(default : asc

date

カレンダーに表示する時間の範囲を設定できます。

main.js
cal.paint({ 
    date:{
        start: Date,
        min: Date,
        max: Date,
        highlight: Date[],
        locale: string | Partial<ILocale>,
        timezone: string
    }
});
  • start: カレンダーの開始日(default: new Date()
  • highlight: 指定された子要素に目立つようなデザインが施される。
  • locale: 週の最初の日が月曜日か日曜日か。
  • timezone: 日付のタイムゾーン

data

カレンダーに使用するデータを設定できます。

Type Signature
type DataOptions = {
  source: string | DataRecord[],
  type: 'json' | 'csv' | 'tsv' | 'txt',
  requestInit: object,
  x: string | ((datum: DataRecord) => number),
  y: string | ((datum: DataRecord) => number),
  groupY:
    | DataGroupType
    | ((values: (number | string | null)[]) => number | string | null),
  defaultValue: null | number | string,
};
  • source: 使用するデータを指定します。指定の仕方は2つあります。

ローカル

プログラム内でjsonデータを作成し、引数として挿入。

main.js
const data = [
    { date: '2012-01-01', value: 3 },
    { date: '2012-01-02', value: 6 },
];

const cal = new CalHeatmap();
cal.paint({
    data: {
        source: data
    },
});

リモート

外部ファイル、もしくは外部APIから取得したデータを入れることもできます。

main.js
const cal = new CalHeatmap();
cal.paint({
    data: { 
        source: 'https://your-api.com/data.json' 
    }
});

  • type: データのタイプを指定します。。json以外のデータタイプにも適応可能。(default: json
  • x: データのタイムスタンプとなるカラムのプロパティ名
  • y: データのカウントとなるカラムのプロパティ名

xとyって?

例えば以下のようなデータを使う場合、

main.js
const data = [{ column1: '2012-01-01', column2: 3 }];

cal.paint({
    data: { 
        source: data, 
        x: 'column1' ,
        y: 'column2'
    }
});

として挿入できる。jsonのプロパティ名は自由に設定できるのだ。

scale

ヒートマップでの色の付け方を設定できます。

js.main.js
cal.paint({
    scale:{
        opacity: {
            baseColor: string,
            domain: number[],
            type: string
        },
            color: {
            scheme: string,
            range: string[] | d3-scale-chromatic,
            interpolate: string | d3-interpolator,
            domain: number[],
            type: string
        }
    }
})

color

  • scheme: 既存で用意されているカラーパレットが使用される。
  • range: オリジナルのカラーパレットを指定できる。2色以上を指定する。
  • interpolate: rangeと組み合わせて使用する。色の補間範囲を定義する。
  • domain: データセットのyのプロパティに入る数値の最大値と最小値。type: thresholdを使用する場合は、しきい値になる。
  • type: 配色に使用される値のタイプを指定する。
    • linear(default) - 線形変換 (平行移動とスケール)
    • threshold - domainで設定されたしきい値によって配色される
    • sqrt - 平方根変換
      などなど

githubの草を再現するなら、type: thresholdを使用します。

作ってみた

なんだかんだあって、データをランダムで生成させて作ってみました。
スクリーンショット 2023-09-09 17.05.30.png

main.js
const cal = new CalHeatmap();

cal.paint(
    {
        itemSelector: document.getElementById('cal-heatmap'),
        //or itemSelector: document.getElementById('cal-heatmap'),

        domain: {
            type: 'month',
            gutter: 5,
            label: {
                text: 'MMM', 
                textAlign: 'start', 
                position: 'top' 
            }
        },
        subDomain: {
            type:'ghDay',
            gutter:5,
            width:17,
            height:17,
            radius:3,
            label:null
        },
        date: {
            start: new Date('2023-01-01')
        },
        data: {
            source: 'data.json',
            x: 'date',
            y: 'count'
        },
        scale: {
            color: {
                type: 'threshold',
                range: ['#b0f5e5', '#35f2c6', '#0fbdb4', '#077485'],
                domain: [4, 6, 8]
            }
        }
    }
);

うまいことできてますね。
自動生成のプログラムも用意しましたのでよければ使ってみてください。

おわりに

他にもポップアウトを表示させたりイベントをつけたりすることができたりします。今回ご紹介しましたのは一部ですがこんな感じになります。ドキュメントにはshowcaseなども用意されているので、コピペして改良してもよいかもしれません。

3
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?