LoginSignup
7
9

More than 5 years have passed since last update.

svg.jsとcanvgを使って、SVGタグ内のオブジェクトをクライアント側だけで画像化させてみる

Last updated at Posted at 2014-12-08

この記事は「SVG Advent Calendar 2014」の8日目の記事です。前日の記事も私が書いてまして、「svg.jsとsvg.draggable.jsを使って、SVGタグ内のオブジェクトをドラッグ&ドロップさせてみる」としてまとめています。

この記事では、サーバーを挟まずクライアント側の処理だけで、SVGタグを画像化させるということを実現しています。

やり方は簡単でcanvgを使います。canvgはsvgをパースしcanvas化することができるツールです。canvas化してしまえば「toDataURL()」が使えるので、クライアント側だけで処理が完結するという仕組みです。

実際のコードはこんな感じで、canvg関数にcanvasのDOMオブジェクトとsvgタグを引数として渡し、コールバック関数(renderCallback)の中で、canvasのtoDataURL()を呼び出すだけです。カンタン!!(※もちろん、svg.jsとjQueryは使わなくても書けますよ。)

HTML
<head>
    <!-- build:js(.) scripts/vendor.js -->
    <!-- bower:js -->
    <script src="bower_components/jquery/dist/jquery.js"></script>
    <script src="bower_components/svg.js/dist/svg.js"></script>
    <script src="bower_components/canvg/dist/canvg.js"></script>
    <!-- endbower -->
    <!-- endbuild -->
</head>
<body>
    <svg id="drawing" style="border:solid 1px;"></svg>

    <div><button id="button">convert to image</button></div>

    <script>
        var draw = SVG('drawing').size(500, 500);
        var image = draw.image('images/NKJ56_gatsudanshi500.jpg', 150, 100).dx(30).dy(30);
        var text = draw.text('SVGを画像化できますか?').move(150, 150).fill('#f06');
        var rect = draw.rect(100, 100).attr({fill: '#eee'}).dx(200).dy(200);
        var circle = draw.circle(100).attr({fill: '#ff0'}).dx(300).dy(300);

        $('#button').on('click', function () {
            var svgTag = $('svg').get(0).outerHTML;
            var canvas = $('<canvas>').get(0);
            canvg(canvas, svgTag, {
                renderCallback: function() {
                    $('body').find('.convImg').remove(); 
                    $('body')
                        .append($('<img>')
                        .css('border', 'solid 1px')
                        .addClass('convImg')
                        .attr('src', canvas.toDataURL())); 
                },
            }); 
        });
    </script>
</body>

ただし、これを実行してみると分かりますが、残念ながら画像が粗くなってしまいます。
canvgのドキュメントを見てみても、クオリティやスムージングといったキーワードが見つからず、こんなものなのかと嘆いています。


このようなsvgタグが

before.PNG


画像化されました。が、粗い。。。フォントもなんかジャギってる。つーか、文字自体が小さくなってる。。。

after.PNG

ちなみにこのコード、Chromeで動作確認を取りましたが、IEでは動いてくれませんでした。色々調べてみるとIEでは一工夫必要なようですので、ぜひ調べてみてください。

で、画像の粗さについて、もうちょっと改善する方法がないものか調べてみたところ、canvgにはcanvasの「drawImage」と似た「drawSvg」という関数が準備されていることが分かりました。
canvasのgetContextの際に、「imageSmoothingEnabled」というパラメータが渡せるので、これで少しは違うかもしれないと思い試してみたところ。。。残念ながら結果は変わらず。(どうやらまだexperimentalな仕様のようですね)

他にもsvgの「shape-rendering」やcanvasの「image-rendering」といったパラメータがあるので、この辺を組み合わせるともしかしたら改善するかもしれないです。が、今回は未調査。

HTML
<body>
    <svg id="drawing" style="border:solid 1px;"></svg>

    <div><button id="button">convert to image</button></div>

    <script>
        var draw = SVG('drawing').size(500, 500);
        var image = draw.image('images/NKJ56_gatsudanshi500.jpg', 150, 100).dx(30).dy(30);
        var text = draw.text('SVGを画像化できますか?').move(150, 150).fill('#f06');
        var rect = draw.rect(100, 100).attr({fill: '#eee'}).dx(200).dy(200);
        var circle = draw.circle(100).attr({fill: '#ff0'}).dx(300).dy(300);

        $('#button').on('click', function () {
            var svgTag = $('svg').get(0).outerHTML;
            var $canvas = $('<canvas>').attr('width', '500px').attr('height', '500px').addClass('convCanvas');
            var context = $canvas.get(0).getContext('2d');
            context.imageSmoothingEnabled = true;
            context.drawSvg(svgTag, 0, 0);
            $('body').find('.convCanvas').remove(); 
            $('body').append($canvas); 
       });
    </script>
</body>
7
9
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
7
9