Edited at

Chart.js をサーバーサイドで使う方法

More than 1 year has passed since last update.

Chart.js は、JavaScriptで利用できるメジャーなチャート(グラフ)描画ライブラリーの1つですが、HTML DOM、より正確に言えば HTML5 Canvas API に依存しているため、Node.jsの環境では動作できません。

しかし、Canvas以外への依存は殆ど無いので、Canvas APIさえ用意できれば、サーバーサイド・レンダリング (SSR) が可能となります。

今回は私自作のCanvas API互換ライブラリー red-agate-svg-canvas を使用して、Node.js上でSVGにレンダリングしたいと思います。

今回の全ソースは こちら にあります。


追記 (2018/9/18)

今回説明する以外にchartjs-node等のパッケージを用いてもサーバーサイドでChart.jsを使うことができます。

chartjs-nodeのメリットは、jpegやpngといったラスタイメージ、svg、さらにはpdf出力にも対応しているところですが、それらを実現するためにcairoというネイティブのライブラリーに依存しているため、セットアップは少し面倒となっています。


解説

import { SvgCanvas,

Rect2D,
SvgCanvas2DGradient } from 'red-agate-svg-canvas/modules';
import * as ChartJs from 'chart.js';

// グローバルスコープを取得します。
// もし、Node.jsで動作してるならば、 "g" は "global" オブジェクトになります。
// ブラウザー上ならば、 "g" は "window" オブジェクトになります。
const g = Function('return this')();

// グラフの描画オプションです。
// https://www.chartjs.org/docs/latest/getting-started/usage.html
const opts: any = { /* 中略 */ };

function main() {
// SvgCanvas は "CanvasRenderingContext2D" と互換のインターフェイスを持っています。
const ctx = new SvgCanvas();

// しかしながら、 SvgCanvas には canvas プロパティーがありません。
// canvas プロパティーには本来、このコンテキストの親となる HTMLCanvasElement が設定されています。
// Chart.js では、以下のプロパティーのみ存在すれば問題ありません。
(ctx as any).canvas = {
width: 800,
height: 400,
style: {
width: '800px',
height: '400px',
},
};

// SvgCanvas の実装は、ホスト環境のフォント(グリフ)情報にアクセスしません。
// 文字列のピクセルでの幅を求められるようにするために、フォントの縦横比を手動で設定します。
ctx.fontHeightRatio = 2;

// Chart.js には "HTMLCanvasElement" と互換のインターフェイスを持つオブジェクトを渡す必要があります。
// そのオブジェクトは、"getContext()" メソッドのみを持っていれば問題ありません。
// "getContext()" は "CanvasRenderingContext2D" と互換のインターフェイスを持つオブジェクトを返さなければなりません。
const el = { getContext: () => ctx };

// もし、描画オプションに "devicePixelRatio" を設定しないと、
// Chart.js は devicePixelRatio を "window" オブジェクトから取得しようとします。
// その場合、 node.js では window オブジェクトが存在しないため、エラーとなってしまいます。
opts.options.devicePixelRatio = 1;

// アニメーションを無効にします。
opts.options.animation = false;
opts.options.events = [];
opts.options.responsive = false;

// Chart.js は描画設定によっては、グローバルスコープの "CanvasGradient" を参照しようとします。
// エラーを防ぐために、 SvgCanvas のグラデーションオブジェクトをグローバルスコープに設定します。
const savedGradient = g.CanvasGradient;
g.CanvasGradient = SvgCanvas2DGradient;
try {
const chart = new ChartJs.Chart(el as any, opts);
} finally {
// グローバルスコープを元に戻します。
if (savedGradient) {
g.CanvasGradient = savedGradient;
}
}

// SVGとして描画します。
const svgString = ctx.render(new Rect2D(0, 0 , 800, 400), 'px');
console.log(svgString);
}


レンダリング結果 (Chromeでの表示結果)


  • SVGイメージはQiitaのエディターでアップロードされないため、Chromeで表示してキャプチャーしました。




バー

1.png


ドーナッツ

2.png


ライン

3.png


エリア

4.png


備考


  • 現状、ビルドには Webpack と babel が必要です。



さいごに

上記のチャートやUML、バーコード・2次元コードをドキュメントに組み込んでHTMLやPDFにレンダリングする

コンソールアプリ Ménneu を公開しました。

後発のアプリですが、併せてご覧いただければと思います。