0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vue.jsでSVGの生成とPNG化をするメモ

Last updated at Posted at 2023-03-12

Vue.jsでSVGの生成とPNG化をするメモ(備忘録)

SVGで図形や文字をはめ込みPNG化する仕様を実装したのでメモとして残す。

やりたいこと

SVGタグの中に動的要素を配置してそれを画像化したい

<svg width="50" height="50" viewBox="0,0,50,50" xmlns="http://www.w3.org/2000/svg">
    <!-- ここを動的に生成したい -->
    <rect x="10" y="10" width="10" height="10" fill="#ccc"/>
</svg>

SVGタグを生成してブラウザで表示

要素を管理するオブジェクトを用意

今回は四角形とテキストを使う

interface State {
  svgItem: (SvgItemRect | SvgItemText)[];
}
//SVGの四角形用
interface SvgItemRect {
  type: 'rect';
  x: number;
  y: number;
  width: number;
  height: number;
  rx: number;
  ry: number;
  fill: string;
}

//SVGのテキスト用
interface SvgItemText {
  type: 'text';
  x: number;
  y: number;
  fontSize: number;
  fill: string;
  text: string;
}

const state = reactive<State>({
  svgItem: [
    //初期値入れてみた
    {
      type: 'rect',
      x: 20,
      y: 20,
      width: 100,
      height: 100,
      rx: 0,
      ry: 0,
      fill: '#849ff0',
    },
    {
      type: 'text',
      x: 100,
      y: 20,
      fontSize: 20,
      fill: '#e74c3c',
      text: 'sample',
    },
  ],
});

テンプレート側

<svg
    width="400"
    height="400"
    viewBox="0, 0, 400, 400"
    xmlns="http://www.w3.org/2000/svg">
    <template v-for="(row, index) in state.svgItem" :key="index">
        <template v-if="row.type === 'rect'">
            <rect
            :x="row.x"
            :y="row.y"
            :width="row.width"
            :height="row.height"
            :rx="row.rx"
            :ry="row.ry"
            :fill="row.fill"/>
        </template>
        <template v-if="row.type === 'text'">
            <text
            :x="row.x"
            :y="row.y"
            :font-size="row.fontSize"
            :fill="row.fill"
            font-family="serif">
            {{ row.text }}
            </text>
        </template>
    </template>
</svg>

結果

ブラウザで確認できる。
image.png

SVGタグからDataURLを生成

svgタグを格納するrefを追加

const svgElement = ref();

svgタグにrefをセット

<svg
    width="400"
    height="400"
    viewBox="0, 0, 400, 400"
    xmlns="http://www.w3.org/2000/svg"
+   ref="svgElement"
>

SVGタグからSVGのDataURLを生成する関数

※btoaやunescapeが非推奨になっている・・・・

import { Buffer } from 'buffer';
const ToBase64 = (elm: HTMLElement): string | null => {
    const svgData = new XMLSerializer()
      .serializeToString(elm)
      .replace(/<!--[\s\S]*?-->/g, '');//一応<!--v-if-->がいらないから消す
    const dataUrl =
      'data:image/svg+xml;charset=utf-8;base64,' +
      // btoa(unescape(encodeURIComponent(svgData))) //昔はこれで良かった
      Buffer.from(svgData).toString('base64');//今の方法は?
    return dataUrl;
};

SVGのDataURLをImgオブジェクトに変換する関数

const ToImage = (base64img: string): Promise<HTMLImageElement | null> => {
  return new Promise((resolve) => {
    const img = new Image();
    img.onload = () => resolve(img);
    img.onerror = (e) => resolve(null);
    img.crossOrigin = 'anonymous';
    img.src = base64img;
  });
};

Canvasにimgを描画してPNGデータを取得

上の関数を使いPNGデータを生成する

const createPngImg = async () => {
  let canvas: HTMLCanvasElement | null = null;
  let ctx: CanvasRenderingContext2D | null = null;
  try {
    const svgDataUrl = ToBase64(svgElement.value);
    if (svgDataUrl === null) return null;
    const img = await ToImage(svgDataUrl);
    canvas = document.createElement('canvas');
    canvas.width = 400;
    canvas.height = 400;
    ctx = canvas.getContext('2d');
    if (img === null || ctx === null) return null;
    ctx.drawImage(img, 0, 0);
    return canvas.toDataURL('image/png');
  } catch (error) {
    console.error(error);
    return null;
  } finally {
    canvas = null;
    ctx = null;
  }
};

最終的に

あとは最初に用意したリアクティブな要素を操作できるようにすれば幸せになれるはず・・・・
image.png

最後に

SVGはタグですからもちろん・・・
image.png
image.png
image.png

イベントが使えます。
ということは、ドラッグ操作やクリック、マウスホイールなどで自在にSVGが操れますね(幸せ)

GitHubにサンプル上げてます

ついでにGitHubにデモページも・・・

0
2
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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?