Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

この記事は「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>
akase244
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした