PerfectVector
著者: Irene Kim2 分で読める

Fabric.jsのSVG読み込み:Canvas上で画像を編集可能に保つ

SVGをFabric.jsの編集可能なオブジェクトにし、1つの部品の色を変え、SVGコピーを書き出して確認します。検証済みの例で元データとAPIの問題を区別します。

目次

編集可能なSVGをFabric.jsへ読み込むには、loadSVGFromString()またはloadSVGFromURL()をawaitし、返されたオブジェクトをcanvasへ加えます。編集する部品への参照を保持し、塗りを変えてcanvas.toSVG()で書き出します。画像として読み込む場合は編集モデルが異なり、その画像を選択しても、各色領域が別の形状として現れるわけではありません。

製品のカスタマイズ、バッジ編集、色変更可能なアイコンでは、この区別が重要です。ユーザーが変える部品に対応するオブジェクトが必要です。拡張子.svgは、その構造を保証しません。

この手順ではFabric.js 7.4.0と小さな手書きのランプを使います。3つの形を読み込み、色変更、書き出しで試します。独自のコード素材であり、PerfectVectorの変換結果や、すべてのSVG機能が同じ往復で残るという主張ではありません。

読み込み方法より先にオブジェクトモデルを選ぶ

Fabric.jsはSVGを独自のcanvasオブジェクトへ解析します。FabricのAPIで編集するもので、canvas内のライブSVG DOM要素ではありません。現在のloadSVGFromStringリファレンスは、解析結果を返すpromiseと、各元要素と作成オブジェクトを受け取るreviverを説明しています。

作業役立つ出発点続行前の確認
別々の形を変えるSVGをFabricオブジェクトへ解析対象領域が固有のオブジェクトとして存在するか
画像を1単位として動かす解析したオブジェクトを意図してグループ化子の編集と選択が意図どおりか
部品を編集せずイラストを配置画像の手順1つの選択可能な画像で十分か
PNGやJPGだけの画像の色を変える先にベクター形状を準備トレースした領域が必要な編集に合うか

見た目から構造を推測しないでください。2色が別のパスの場合も、離れた複数の島が1本のパスの場合もあります。SVGのグループ解除ガイドが、元ファイルの診断を説明しています。

小さな読み込み、色変更、書き出しの例を実行する

空のフォルダーから始め、固定した依存パッケージを入れます。

npm install --save-exact fabric@7.4.0

index.htmlとして保存します。

<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Fabric SVG import test</title>
<canvas id="editor" width="320" height="240"></canvas>
<button id="recolor">Recolor shade</button>
<button id="export">Export SVG</button>
<div id="output"></div>
<pre id="report"></pre>
<script type="module" src="main.js"></script>
</html>

次をmain.jsとして保存します。相対的なモジュールパスにより、バンドラーなしで実行できます。バンドルするアプリでは、同じ名前をfabricからインポートします。

import { Canvas, loadSVGFromString } from './node_modules/fabric/dist/index.min.mjs';
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="320" height="240" viewBox="0 0 320 240">
  <path id="shade" fill="#e6a23c" d="M115 40 H205 L230 135 H90 Z"/>
  <rect id="stem" x="151" y="135" width="18" height="57" fill="#214c54"/>
  <path id="base" fill="#214c54" d="M110 192 H210 L220 210 H100 Z"/>
</svg>`;
const canvas = new Canvas('editor', { width: 320, height: 240 });
const parts = new Map();
const parsed = await loadSVGFromString(svg, (element, object) => {
  const id = element.getAttribute('id');
  if (id) parts.set(id, object);
});
canvas.add(...parsed.objects.filter(Boolean));
canvas.requestRenderAll();
function report(exported) {
  const xml = new DOMParser().parseFromString(exported, 'image/svg+xml');
  const data = {
    version: '7.4.0',
    importedObjects: canvas.getObjects().length,
    types: canvas.getObjects().map(object => object.type),
    shadeFill: parts.get('shade').fill,
    exportedPaths: xml.querySelectorAll('path').length,
    exportedRects: xml.querySelectorAll('rect').length,
    exportedImages: xml.querySelectorAll('image').length,
  };
  document.querySelector('#report').textContent = JSON.stringify(data, null, 2);
  document.querySelector('#output').innerHTML = exported;
}
document.querySelector('#recolor').onclick = () => {
  parts.get('shade').set('fill', '#397e86');
  canvas.requestRenderAll();
  document.querySelector('#report').textContent = 'Shade recolored. Export to check delivery SVG.';
};
document.querySelector('#export').onclick = () => report(canvas.toSVG());
report(canvas.toSVG());

HTMLを直接開かず、HTTPでフォルダーを配信します。Pythonがあれば、例えばpython3 -m http.server 8000を実行し、http://localhost:8000を開きます。Recolor shade、次にExport SVGを押します。ボタンはcanvasの下に出力文字列を描画し、ファイルのダウンロードは行いません。

このデモのプレビューへ挿入するのは、自身の固定素材の出力だけです。信頼できないSVGの一般的なアップロード処理に、このinnerHTMLプレビューを流用しないでください。アプリ全体のエラー処理やファイル検証も省略しています。

mapは、解析中に元のIDを取得します。返された最初のオブジェクトが常に目的の部品だと想定せず、明示的な参照でシェードの色を変えます。製品用エディターでは、文書の保存と再読み込みで識別子と参照をどう格納するか決めます。

テストで確認したこと

素材はpath、rect、pathの3つのオブジェクトになりました。シェードを#e6a23cから#397e86へ変えると、書き出しプレビューも新しい色になり、支柱と台座は元の暗い塗りを保持しました。出力にはパス2本、矩形1つがあり、image要素はありませんでした。

色変更したランプのcanvas、対応する書き出しSVGプレビュー、読み込みオブジェクト3つと出力画像ゼロの報告を示す実際のローカルFabric.jsデモ
Fabric.js 7.4.0で手書き素材を色変更し、書き出した実際の画像です。これらの観察は、ここで試した単純な3形状に適用されます。

この確認で、この例が書き出し後もベクター形状であると分かります。元XML、グループ、CSS規則、他のファイルのすべての機能が保持される証明ではありません。元マスターを保持し、納品用コピーを受け取り側のアプリで比較します。

コールバックの例とpromise APIを混ぜない

古い例はfabric.loadSVGFromString(svg, callback)を呼び、コールバックへオブジェクト配列全体が渡ると想定することがよくあります。現在のAPIの第2引数はその役割ではなく、オブジェクトごとのreviverです。コレクション全体は解析結果をawaitして取得します。

Fabricの6.0移行ガイドは、名前付きインポートとpromiseベースAPIへの変更を説明しています。古いコードを適用する前にインストール済みバージョンを確認します。インポート文だけの置き換えでは、読み込みロジックは誤ったままです。

同一オリジンのSVGファイルでは、同等の出発点は次のとおりです。

import { loadSVGFromURL } from 'fabric';
const parsed = await loadSVGFromURL('/artwork.svg');
canvas.add(...parsed.objects.filter(Boolean));
canvas.requestRenderAll();

loadSVGFromURLドキュメントは、SVGを取得し、同一オリジンポリシーに従う必要があると説明しています。リモートホストが自分のオリジンを許可しない場合、グループ化コードを変えても通信失敗は直りません。まず自分のアプリから配信するファイルを試し、失敗リクエストとリモートサーバーのCORS設定を調べます。

見た目と編集可能性を別々に確認する

描画成功が答えるのは、何かが表示されたかという1つの問題です。エディターには追加の確認が必要です。

  1. 変えたい領域を選択し、他の部品が一緒に変わらないか確認します。
  2. 例のように、オブジェクトのsetメソッドで色を変え、描画を要求します。
  3. 結果を書き出し、単独で開きます。FabricのCanvas.toSVG APIは、canvas状態からSVGマークアップを生成します。
  4. ストローク、クリッピング、グラデーション、文字、入れ子の変換など、実際の画像が依存するものを確認します。上の単純素材はこれらをテストしていません。
  5. 手順が完成したと判断する前に、自分のエディターで保存、再読み込みし、意図した編集を繰り返します。

読み込みで画像オブジェクトができたなら、元データに埋め込みラスターがないか調べます。SVG内のラスター内容のガイドは、SVGの入れ物がピクセルに編集可能なパスを与えない理由を示します。使えるベクター形状がすでにあれば、再トレースせず読み込みとオブジェクトの整理を修正します。

ラスター画像に前段階の変換が必要な場合

平面的なPNGやJPGしかなく、ユーザーが色領域を編集する必要があるなら、PerfectVectorの画像をベクターに変換する手順でSVG候補を準備できます。元画像と比較し、パスを確認して、必要な領域が独立して編集可能か調べてからFabric.jsへ読み込みます。

トレースは、シェード、支柱、台座などのアプリ固有の意味を割り当てません。この素材の名前は意図して付けました。変換形状を確認した後、自分の部品対応と編集規則を追加します。再利用アイコン一式には、PNGからSVGへのUIキット手順が、元データ準備と一貫性確認を扱っています。

写真として使うものはラスターのままにします。必要な形がすでにある適した元SVGは保持します。ベクター化の概要は、そもそもトレースが必要か判断する助けになります。

よくある質問

Fabric.jsはSVGを編集可能なパスとして読み込みますか? 対応するSVG内容をFabricオブジェクトへ解析できます。編集可能性は元の構造に依存します。パス、形状グループ、埋め込みラスターは同じ編集方法を提供しません。返されたオブジェクトを調べ、必要な変更を試します。

古いloadSVGFromStringの例が失敗するのはなぜですか? 古いチュートリアルは第2引数を完了コールバックと想定する場合があります。現在のAPIはpromiseを返し、その引数をオブジェクトごとのreviverに使います。インストール済みバージョンにコードを合わせ、解析結果をawaitします。

toSVGは元ファイルを正確に保持しますか? 同じ元文書を期待しないでください。Fabricのcanvas状態からSVGを生成します。元マスターを保持し、書き出しの見た目と、受け取り工程に必要な構造を検証します。

PNGを読み込むと個々の領域を編集できますか? PNGの配置は別々のベクター領域へのトレースではありません。必要なら、適したベクター形状を先に準備し、結果の部品を確認して、アプリ内の役割を定義します。

参考資料

  1. Fabric.js — loadSVGFromString — Promiseの戻り値と元要素・オブジェクトのreviver契約。
  2. Fabric.js — Fabric.js 6.0への更新 — 名前付きインポートとコールバックからpromiseへの移行。
  3. Fabric.js — loadSVGFromURL — URLの解析と同一オリジンの取得制限。
  4. Fabric.js — Canvas API — Canvas状態からSVGマークアップへの書き出し。

不足しているのがラスターからの編集可能な形状なら、PerfectVectorでSVG候補を準備します。まず形を確認し、Fabric.jsエディターで意図した部品の色を変え、正しく書き出せることを検証します。

ブログのその他の記事

編集しやすい
よりきれいなSVGから始めましょう