Next.js SVGをPNGに変換してダウンロードする処理
以下の動画のような、ボタンをクリックしてSVGファイルをPNGなどの画像ファイルとして保存できるような機能を実装しました。
使用技術
- Next.js(14.0.3)
- React(18.0.0)
- TypeScript(5.0.0)
- Tailwind CSS(3.3.0)
大まかな手順
- SVG要素の参照を取得する
- SVGデータをシリアライズする
- Blobを作成してURLを生成する
- Canvasを作成し、SVGを描画する
- Canvasから画像データを取得する
- ダウンロードリンクを作成し、クリックイベントを設定する
作成方法
1. SVG要素の参照を取得する
- Reactの
useRef
フックを使用して、SVG要素への参照(svgRef
)を作成します。これにより、後でSVG要素に直接アクセスできます。 -
コードの実装:
const svgRef = useRef<SVGSVGElement>(null);
ここでは<svg ref={svgRef} width="500" height="500">{svgPaths}</svg>
useRef
を使ってsvgRef
を作成し、SVG要素にこの参照を割り当てています。
consoleでsvgRef
を確認すると、以下のようにSVG要素にアクセスすることができます。{current: svg} current: svg __reactFiber$a5gqq177ye8: FiberNode {tag: 5, key: null, elementType: 'svg', type: 'svg', stateNode: svg, …} __reactProps$a5gqq177ye8: {width: '500', height: '500', children: Array(6)}
2. SVGデータをシリアライズする
-
XMLSerializer
を使用して、参照されたSVG要素の内容を文字列として抽出します。 -
コードの実装:
const svgData = new XMLSerializer().serializeToString(svgRef.current);
XMLSerializer
は、DOMオブジェクトをXML形式の文字列に変換するためのAPIです。serializeToString
メソッドは、与えられたDOMオブジェクト(この場合はSVG要素)をXML文字列に変換します。
3. Blobを作成してURLを生成する
- 取得したSVGデータをBlob形式に変換し、一時的なURLを生成します。
-
コードの実装:ここでは
const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' }); const svgUrl = URL.createObjectURL(svgBlob);
Blob
オブジェクトを作成し、その後URL.createObjectURL
を使用してBlobのURLを生成しています。
後続のステップで、SVGデータをCanvasに描画したり、画像としてダウンロードしたりする際にこのURLを使用します。
4. Canvasを作成し、SVGを描画する
-
新しいCanvas要素を作成し、ImageオブジェクトとしてSVGを描画します。
-
コードの実装:
const canvas = document.createElement('canvas'); canvas.width = svgRef.current.clientWidth; canvas.height = svgRef.current.clientHeight; const ctx = canvas.getContext('2d'); const img = new Image(); img.onload = () => { ctx?.drawImage(img, 0, 0); URL.revokeObjectURL(svgUrl); }; img.src = svgUrl;
document.createElement('canvas')
を使って新しいCanvas要素を作成します。
svgRef.current.clientWidth
とsvgRef.current.clientHeight
を使用して、参照されているSVG要素の現在の幅と高さを取得し、Canvasのサイズとして設定します。
canvas.getContext('2d')
は、Canvasに2Dグラフィックスを描画するためのAPIを提供します。SVGデータを画像としてCanvasに描画するため、まずImageオブジェクトにSVGデータを読み込ませる必要があるので、まず
new Image()
で新しいImageオブジェクトを作成します。
そして、img.src = svgUrl
でSVGデータのURLをImageオブジェクトのソースとして設定します。ImageオブジェクトがSVGデータを読み込み終えた後、それをCanvasに描画します。
img.onload = () => {...}
で、ImageオブジェクトがSVGデータを読み込み終えたときに実行される関数を定義します。
この関数の中でctx.drawImage(img, 0, 0)
を呼び出し、読み込まれた画像(SVG)をCanvasに描画します。
img.src = svgUrl
が実行されると、img.onload
がすぐに開始されるため、onload関数を先に記述しておくと安心です。img.onload = () => { ctx?.drawImage(img, 0, 0); URL.revokeObjectURL(svgUrl); }; img.src = svgUrl;
URL.revokeObjectURL(svgUrl);
で一時的に生成されたSVGデータのURLをメモリから解放させます。
5. Canvasから画像データを取得する
- Canvasの
toDataURL
メソッドを使用して、PNG形式の画像データを取得します。 -
コードの実装:この行はCanvas上の描画された画像をDataURL(Base64エンコードされた画像データ)として取得します。
const imageUrl = canvas.toDataURL(`image/${format}`);
このDataURLを使用して、PNG形式の画像ファイルとして保存や表示ができます。
{format}
にはPNGやJPEGなどが動的に入ることを想定しています。
6. ダウンロードリンクを作成し、クリックイベントを設定する
- 画像データのURLを使用して新しいリンク要素を作成し、プログラムでリンクを「クリック」してファイルをダウンロードします。
-
コードの実装:ここでは、新しいリンク要素を作成し、
const link = document.createElement('a'); link.href = imageUrl; link.download = `customized-icon.${format}`; document.body.appendChild(link); link.click(); document.body.removeChild(link);
href
に画像データURLを設定し、ダウンロード属性にファイル名を設定しています。
link.click()
でプログラム側で自動的にリンクをクリックすることで、ダウンロードを開始します。
ブラウザによっては、DOMに追加されていないリンク要素にlink.click()
をしても、イベントが発火しない場合もあるようなので、document.body.appendChild(link)
でリンク要素をDOMに追加し、クリック後に削除します。
Discussion