43
38

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 5 years have passed since last update.

一発芸!SVGでHTMLを画像化する

Last updated at Posted at 2016-10-19
1 / 16

お詫びと訂正(2016年12月5日追記)

記事初出時に React v15.3.2 では foreignObject要素がサポートされてない、と記しましたがこちらは誤りでした。
React v15.0.0 のリリースノートにてSVGの全ての要素をサポートした、とあるのですが、このタイミングではまだ公式ドキュメントが更新されていませんでした。
また v15.2.x では一部の属性が使用不可と判定されてしまって、コンポーネントのレンダリング自体は意図通りになるものの使用には不安を感じさせる挙動となっていました。

capture.png
React V15.2.x ではuse要素やforeignObject要素の子要素に必要なxmlns:*属性が使用不可であると警告される

しかし、最近になって公式ドキュメントのリライトが始まって、もともと存在した「Reactで使用できる要素と属性の一覧」ページが思いっきり削除されました(当該コミット)。
そこで気になって現時点で最新バージョンである v15.4.0 で試したところ、use要素やforeignObject要素が前述の警告もなく普通に使用できたので自分の勘違いに気付いた次第です。

手元でバージョンをさかのぼって試したところ、v15.3.0 以上ならば問題なく動作するようです。
したがって記事初出時の記載は完全に誤りです。
ここに訂正しお詫びいたします。
大変失礼しました。


このスライドについて

2016年10月19日(水)
SVGおじさん 松田さん 主催
「まぼろしのSVG勉強会 その2」 発表資料
いまさらなネタかもしれないけど許せ

※ 「まぼろし」というのは弊社のことです


これはHTMLから作った画像です

capture.png

しかもフロントだけで実現可能!


デモ作った

react-dom-imager-demo

git clone git@github.com:haribote/react-dom-imager-demo.git
cd react-dom-imager-demo && npm install
npm start


いきなり種明かし

HTMLを画像化する手順

  1. 画像化したいHTMLを作る
  2. (1)のHTMLをSVGのforeignObject要素で囲む
  3. (2)のSVGをcanvas要素に描画する

MDNに全部書いてある

DOM オブジェクトを Canvas に描画する - HTML | MDN


コード例

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 1280" width="960" height="1280">
  <style>
    h1 {
      color: red;
    }
  </style>
  <g>
    <foreignObject x="0" y="0" width="100%" height="100%">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <h1>Hello, world!</h1>
      </div>
    </foreignObject>
  </g>
</svg>

SVGをCanvasに描画する

// canvas要素とコンテキスト、SVGを読み込むimage要素を用意
const canvasEl = document.getElementById('canvas');
const context  = canvasEl.getContext('2d');
const imageEl  = new Image();

// SVGコードをBlobにしてURLを発行する
const staticSvgBlob = new Blob(['<svg xmlns...'], { type: 'image/svg+xml' });
const url           = URL.createObjectURL(staticSvgBlob);

// SVGが読み込まれたらcanvas要素に描画する
imageEl.onload = () => {
  context.clearRect(0, 0, canvasEl.width, canvasEl.height);
  context.drawImage(imageEl, 0, 0);
  URL.revokeObjectURL(url); // URL解放
};

// CORS設定をしてSVGを画像として読み込む
imageEl.crossOrigin = 'Anonymous';
imageEl.src = url;

最重要ポイント

Canvasに描画するSVGは完全にスタンドアロンでなくてはならない

つまり描画に必要な情報はすべてぶっ込むべし!


BlobにするときのMIME

new Blob(['<svg xmlns...'], { type: 'image/svg+xml' });

文字コードは不要

MDNに書かれている通りに
image/svg+xml;charset=utf-8
とすると一部のブラウザでSVGがぶっ壊れる

(なんでだろう?)


ルートのSVGに必要な属性

  • xmlns
  • viewBox
  • width
  • height

CSSの埋め込み

style要素にCSSを文字列展開して直接ぶっ込む!

<style>
  h1 {
    color: red;
  }
</style>

画像の埋め込み

Base64エンコードしてDataURLでぶっ込む!

<img src="data:image/png;base64,iVBOR..." width="400" height="382" alt="" />

フォントとか

画像同様にDataURLにする必要がある

  • データの再頒布にあたるので著作権とか注意
  • アイコンはフォントじゃなくてSVGでおk

残念なお知らせ

  • IEってなんですか? :sleepy:
  • foreignObject要素の含まれたSVGをCanvasに描き出すとtaintedとされてtoDataURLなどが使えなくなる
    • Chrome, Safari だけ?
    • ちゃんと調べてない :stuck_out_tongue_winking_eye:
    • chromiumのissueはこちら
    • Canvasのtaint(汚染)についてはこちら
  • ReactだとforeignObject要素がサポートされてないからdangerouslySetInnerHTMLでぶっ込まざるをえない :name_badge:
    • v15.3.2 時点
    • 将来的にはサポートされるはず

※1ページ目の訂正をお読みください。


ありがとうございました :bow:

43
38
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
43
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?