ぶれすとつーる

だいたいjavascript

サーバ側でd3を使って作ったsvgを画像に変換して返す

おはようございます、この記事はd3.js Advent Calendar 2014の21日目の記事です。

d3.js Advent Calendar 2014 - Qiita

つい先日からd3を使い始めました。

d3のpackage.json読んでたらnode側から使う場合はjsdomを使ってるっぽいのみつけた。 ドキュメントにも書いてあったけどサーバサイドでも普通にjsdom使ってdom操作的なやつできるらしい。

ならサーバサイドでデータを元にsvg組み立てて、それをnode-canvasに転写してpngなりに変換してstreamでレスポンスにながせるよねって思ったのでやってみた。

必要なモジュールのインストール

svgをnode-canvasに転写するのfabricが多分一番簡単なのでこれをいれる

でもその前にそれが依存してるnode-canvasのためにcairoいれる必要がある。

多分一番辛い作業だけどこの辺がんばってください

Home · Automattic/node-canvas Wiki · GitHub

このcairoがんばる作業が終わったらあとは普通に必要なモジュールをnpmからもってきます

npm install fabric

npm install d3

アプリケーションの作成

まずはサーバサイドでsvgを組み立てます

var d3 = require('d3')
  , body = d3.select('body')
  , svg
  ;

// 赤い丸を作る例
body
  .append('svg')
  .attr({width: 300, height: 300})
  .append('circle')
  .attr({cx: 150, cy: 150, r: 100, fill: '#ff0000'});

svg = body.node().innerHTML
console.log(svg);

// <svg width="300" height="300">
//   <circle cx="150" cy="150" r="100" fill="#ff0000"></circle>
// </svg>

これをfabricを使ってcanvasに転写する

// svg文字列から画像情報を読みこむ
fabric.loadSVGFromString(svg, function (objects, options) {
  var canvas, obj, stream;

  canvas = fabric.createCanvasForNode(300, 300);
  obj = fabric.util.groupSVGElements(objects, options);
  canvas.add(obj).renderAll();
});

あとはサーバ書いてレスポンスに垂れ流す 完成版

var http = require('http')
  , d3 = require('d3')
  , fabric = require('fabric').fabric
  ;

http.createServer(function(req, res) {
  var canvas = fabric.createCanvasForNode(300, 300)
    , body = d3.select('body')
    , svg
    ;

  body
    .append('svg')
    .attr({width: 300, height: 300})
    .append('circle')
    .attr({cx: 150, cy: 150, r: 100, fill: '#ff0000'});

  svg = body.node().innerHTML;

  fabric.loadSVGFromString(svg, function (objects, options) {
    var obj, stream;

    obj = fabric.util.groupSVGElements(objects, options);
    canvas.add(obj).renderAll();
    stream = canvas.createPNGStream();
    stream.on('data', function(chunk) {
      res.write(chunk);
    });

    stream.on('end', function() {
      res.end();
    });
  });
}).listen(3000);

サーバ起動して該当ページ ( http://localhost:3000/ )にアクセスすると日の丸ができてるはず。

表示

f:id:nazomikan:20141221075206p:plain

それっぽいやつ

// ...

  // それっぽいデータ
  color = d3.scale.category20();
  data = [5, 10, 2, 20, 5, 8, 13, 25, 26, 29, 22, 6];

  body
    .append("svg")
    .attr({width: width, height: height})
    .append('g')
    .selectAll('rect')
    .data(data).enter()
    .append('rect').attr('x', function (d,i) { return (i * 25) + 5 })
    .attr('y', function (d,i) { return height - (d * 10) })
    .attr('width', 20).attr('height', function (d, i) { return d * 10 })
    .attr('fill', function (d, i) { return color(i); });

  svg = body.node().innerHTML;

fabric.loadSVGFromString(svg, function (objects, options) {
  // ...

表示

f:id:nazomikan:20141221084021p:plain

それっぽいですね!><