SVGを画像化する
はじめに
昨今のクライアントサイドでは、動的な画像のレンダリング、アニメーション、拡張・縮小を求められることが多々あります。そのような際にSVGは利用しやすい形式です。一方で、画像として内容を保存したくなることもあります。そのような場合に使える、SVG画像をPNG画像に変換する方法を簡単に述べます。
SVGとは
SVGファイル(スケーラブル・ベクター・グラフィックス、Scalable Vector Graphics)は画像形式の1つです。XMLをベースにした二次元ベクターデータで画像を描きます。ベクターデータとは「画像を、点の座標とそれを結ぶ線(ベクター、ベクトル)などの数値データをもとにして演算によって再現する方式」です。その大きな特徴は「拡大・縮小しても画質が損なわれない」ことです。
Illastratorなどでも作れますが、JavaScriptで簡単に作れます。実際にSVGを業務などでがっつりと利用するのであれば、d3jsがお勧めです。かなり扱いやすくできています。
実際の変換処理
やり方は非常に簡単で、以下の3ステップで可能です。
- SVG画像を作成する
- XMLSerializerを使ってSVG画像のデータを取り出す
- Canvasを使ってPNG形式に変換する
XMLSerializerは最新のブラウザであればほぼ使用可能です(詳細はこちら)。
コード
実際のコードは次のようになります。svg2jpeg関数にSVG要素(DOM)を渡すことで、PNG画像に変換します。変換処理では、SVG要素と同じサイズのCanvasを使い、Imageオブジェクトを利用してSVGデータをCanvasに貼り付けます。その後、CanvasのtoDataURL()メソッドを使用してPNG画像データを取り出します。
function svg2jpeg(svgElement, sucessCallback, errorCallback) {
var canvas = document.createElement('canvas');
canvas.width = svgElement.width.baseVal.value;
canvas.height = svgElement.height.baseVal.value;
var ctx = canvas.getContext('2d');
var image = new Image;
image.onload = () => {
// SVGデータをPNG形式に変換する
ctx.drawImage(image, 0, 0, image.width, image.height);
sucessCallback(canvas.toDataURL());
};
image.onerror = (e) => {
errorCallback(e);
};
// SVGデータを取り出す
var svgData = new XMLSerializer().serializeToString(this.damageMap.nativeElement);
image.src = 'data:image/svg+xml;charset=utf-8;base64,' + btoa(svgData);
}
// 使い方
svg2jpeg(document.getElmentById('SVG要素のID'), function(data) {
// data: JPEGのbase64形式データ(文字 列)
}, function(error) {
// error: 何らかのエラーオブジェクト
})
HTMLサンプル
実際に試すには、以下のコードをHTMLファイルに保存してブラウザで開いて見てください。「変換する」ボタンを押すことで、SVGをPNG画像に変換し、表示します。Chrome、Firefox、Safariでは動作確認済みです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Sample</title>
<style>
.image {
margin: 0 auto;
display: flex;
align-items: center;
width: 900px;
}
.image__svg {
width: 400px;
}
.image__sep {
flex-grow: 1;
text-align: center;
}
.image__converted {
width: 400px;
height: 400px;
border: 1px solid rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div class="image">
<div class="image__svg">
<svg
id="svg"
xmlns="http://www.w3.org/2000/svg"
width="400"
height="400"
viewBox="0 0 400 400"
preserveAspectRatio="xMidYMid meet"
style="background-color: #eee;">
<circle cx="50" cy="50" r="50" fill="rgba(255,0,0,0.5)" />
<circle cx="200" cy="200" r="50" fill="rgba(0,255,0,0.5)" />
<circle cx="350" cy="350" r="50" fill="rgba(0,0,255,0.5)" />
<circle cx="350" cy="50" r="50" fill="rgba(255,255,0,0.5)" />
<circle cx="50" cy="350" r="50" fill="rgba(0,255,255,0.5)" />
</svg>
</div>
<div class="image__sep">
<div>==>></div>
<div><button id="convert-button">変換する</button></div>
</div>
<div class="image__converted">
<img src="" id="converted-image">
</div>
</div>
<script>
(function() {
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('convert-button').addEventListener('click', function() {
svg2imageData(document.getElementById('svg'), function(data) {
console.log(data);
document.getElementById('converted-image').src = data;
}, function(error) {
console.log(error);
alert('failed to convert');
});
});
});
function svg2imageData(svgElement, successCallback, errorCallback) {
var canvas = document.createElement('canvas');
canvas.width = svgElement.width.baseVal.value;
canvas.height = svgElement.height.baseVal.value;
var ctx = canvas.getContext('2d');
var image = new Image();
image.onload = () => {
ctx.drawImage(image, 0, 0, image.width, image.height);
successCallback(canvas.toDataURL());
};
image.onerror = (e) => {
errorCallback(e);
};
var svgData = new XMLSerializer().serializeToString(svgElement);
image.src = 'data:image/svg+xml;charset=utf-8;base64,' + btoa(svgData);
}
}());
</script>
</body>
</html>
実際にブラウザで表示すると次のように表示されます。
変換ボタンをクリックすると、右側の領域にPNG画像が表示されます。開発ツールなどで確認してください。
おわりに
画像や何らかのグラフ表示などの課題に直面した際には、SVGとCanvasを使えればほぼ解決可能です。画像化の方法を組み合わせることで、サーバへ保存できるようにもなり、なお便利になります。