まず重大な注意:
- npm i svg.js と npm i @svgdotjs/svg.js では違うものがインストールされる。
Plumb > npm list +-- @svgdotjs/svg.js@3.1.2 +-- @types/jest@28.1.4 +-- @types/node@18.0.3 `-- svg.js@2.7.1
- svg.js は開発が止まっているかも知れない。@svgdotjs/svg.js が現行バージョン。
- jsPlumb も同じことがあって、jsplumb と @jsplumb/browser-ui があって、jsplumb は開発止まっていた。
graphics.js も悪くないのだが、d.ts ファイルが存在しないようなので svg.js にした。d.tsファイルは以下に貼ってある。
その他色々:
- <script src="https://cdn.jsdelivr.net/npm/@svgdotjs/svg.js@3.0/dist/svg.min.js"></script> でブラウザに読み込める。最近は楽だなー。
- ブラウザでは、SVGオブジェクト(the global SVG object)が作られて、その名前空間の下に諸々が配置される。
- ESM方式であれば、import { SVG } from '@svgdotjs/svg.js' でSVGオブジェクトが作れる。
- 名前 'SVG' は、名前空間名であると同時に関数名なので呼び出せる。
- SVG() 呼び出しで an SVG document を作れる。要素ではなくて文書ノードが返る。一応、XMLのハイブリッド文書アーキテクチャに沿っている。SVGは別ボキャブラリだから透過的なDOMツリーではない。
- var svgDoc = SVG(); svgDoc.addTo('body') で、HTMLツリーのbodyの所にSVG文書ノードを接ぎ木する。おそらく last sibling として入るのだろう。
- var svgContainer = svgDoc.addTo('body'); svgContainer.size(300, 300) で描画領域サイズが設定される。
- 結局、HTMLツリーに接ぎ木〈マウント〉されたSVG文書のコンテナ要素を得るには、var svgContainer = SVG().addTo('body').size(300, 300)
- 上記のことをタグで記述すれば、<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300"></svg>
- svgContainer を draw という呼び名で呼ぶのがコンベンションらしいので従う。
- drawのメソッド呼び出し draw.rect(100, 100) などでdraw配下の図形オブジェクトを生成できる。
- var rect = draw.rect(100, 100).attr({ fill: '#f06' }) のようなメソッドチェーン記法も使える。
- 上記のことをタグで記述すれれば、<svg> のなかに <rect width="100" height="100" fill="#f06"></rect> を挿入したことになる。
- var draw = SVG().addTo('#someElementId').size('100%', '100%') のような呼び出しも出来る。
- DOMツリー構築完了後の初期化処理は SVG.on(document, 'DOMContentLoaded', ()=> {var draw = SVG().addTo('body')} ) だが、bodyの最後にスクリプトを直書きすれば関係ない。
- jQueryの悪しき伝統にしたがって、SVG() 関数は、引数が文字列だとノード検索関数になる。邪悪多相性と呼んでおこう。下に仕様あり。
- 邪悪多相性のディスパッチは:
- 引数なし ファクトリメソッド
- セレクター文字列 または HTMLテキスト文字列 (パーズして挙動が変わる)
- DOM要素ノード型オブジェクト
- その他の型
- The main SVG.js initializer function とは、関数 SVG() のこと。
- SVG() 呼び出しの結果、プログラムには SVG.Svg オブジェクト(クラスのインスタンス)が返される。これはDOM要素ノードではなくて、SVG.js としてのオブジェクト。
- APIドックの編成がちょっと変わっている。ひょっとしてjQueryの影響かも知れないが、クラスやインターフェイスのような構造より、何をしたいか/どんな操作がしたいかにより組織化されている。「構造よりは要求」という方針。
邪悪多相性の仕様:
function SVG(): Svg; function SVG(selector: QuerySelector): Element; function SVG<T>(el: T): SVGTypeMapping<T> function SVG(domElement: HTMLElement): Element;
クラスSvgの宣言は:
class Svg extends Container { constructor(svgElement?: SVGSVGElement); constructor(id: string); node: SVGSVGElement; namespace(): this; defs(): Defs; remove(): this; isRoot(): boolean; } class Container extends Element { circle(size?: NumberAlias): Circle; circle(size: number, unit: number): Circle; clip(): ClipPath; ellipse(width?: number, height?: number): Ellipse; flatten(parent: Dom, depth?: number): this; foreignObject(width: number, height: number) : ForeignObject gradient(type: string, block?: (stop: Gradient) => void): Gradient; group(): G; image(): Image; image(href?: string, callback?: (e: Event) => void): Image; line(points?: PointArrayAlias): Line; line(x1: number, y1: number, x2: number, y2: number): Line; link(url: string): A; marker(width?: number, height?: number, block?: (marker: Marker) => void): Marker mask(): Mask; nested(): Svg; path(): Path; path(d: PathArrayAlias): Path; pattern(width?: number, height?: number, block?: (pattern: Pattern) => void): Pattern plain(text: string): Text; polygon(points?: PointArrayAlias): Polygon; polyline(points?: PointArrayAlias): Polyline; rect(width?: number, height?: number): Rect; style(): Style; text(block: (tspan: Tspan) => void): Text; text(text: string): Text; ungroup(parent: Dom, depth?: number): this; use(element: Element | string, file?: string): Use; viewbox(): Box; viewbox(viewbox: ViewBoxLike | string): this; viewbox(x: number, y: number, width: number, height: number): this; textPath(text: string | Text, path: string | Path): TextPath symbol(): Symbol zoom(): number zoom(level: NumberAlias, point?: Point): this; }
コンテナは、図形の容れ物という意味だけでなくてメソッドのコンテナ(APIプロバイダ)にもなっているオブジェクトのこと。