- svg ファイルを react component として使えるやつ
- afterInjection から描画後の node 操作が可能
- src の svg ファイルから width, height を読み取って viewBox 指定までやってくれるので、中に svg やら html (foreign object) やら nest してやれば resize 時に全体がよき感じに縮尺される
$ npm i react-svg
import React from 'react'
import { ReactSVG } from 'react-svg'
export const Component = () => {
return <ReactSVG
src={'/some/file.svg'}
afterInjection={svgNode => {
try {
console.log(svgNode)
} catch (e) {
// 内部で throw されると握りつぶされるっぽいのでエラー出してあげてるだけ
console.error(e)
}
}}
/>
}
https://github.com/svgdotjs/svg.js
https://svgjs.dev/docs/3.0/
- 有名っぽい svg 操作 library で普通に手続き型っぽく操作する感じ
- 一旦
SVG(node)
で wrap してからうにょる感じ - svg element の native property, method 習熟してないなら便利
$ npm i @svgdotjs/svg.js
import { SVG } from '@svgdotjs/svg.js'
/**
* svg の中に矩形の座標・サイズ指定しつつ
* textarea としての div をいれる、みたいな
*/
export const insertTextarea2Svg = ({
parent,
text,
box,
styles,
ruler,
noCentered,
scrollable,
}: {
parent: SVGSVGElement
text: string
box: { x: number, y: number, width: number, height: number }
styles?: Partial<CSSStyleDeclaration>
ruler?: true
noCentered?: true
scrollable?: true
}) => {
const svg = SVG(parent)
const { x, y, width, height } = box
const foreign = svg.foreignObject(width, height).attr({ x, y })
const div = document.createElement('div')
div.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml')
div.style.fontSize = '14px'
div.style.overflow = scrollable ? 'auto' : 'visible'
div.style.height = '100%'
div.style.border = ruler ? '1px solid red' : '1px solid rgba(0,0,0,0)'
if (!noCentered) {
div.style.display = 'flex'
div.style.justifyContent = 'center'
div.style.alignItems = 'center'
}
div.innerHTML = text
Object.entries(styles || {}).forEach(([key, value]) => {
// @ts-ignore
div.style[key] = value
})
foreign.add(SVG(div))
}
https://github.com/plotly/plotly.js
https://plotly.com/javascript/
https://x.com/norach7746/status/1721013209476518177?s=20
- plotly 自体は python や R などでも使えるグラフ生成ライブラリ
- こいつはその js 版で、simple x plain な plot が生成できる
newPlot().then()
で node を拾えるので、そこから svg.js と組み合わせて svg の中にグラフを挿入する ことができるという例- 加えて react-svg の afterInjection 経由なら、viewBox も適用されてるので responsive & dynamic な graph on svg が出来る、というアイデア
window
依存で ssr ng なので csr 必須
$ npm i plotly.js-dist
$ npm i -D @types/plotly.js-dist-min
import { SVG } from '@svgdotjs/svg.js'
import { plotly } from 'libs/plotly'
import { Data as PlotlyData, Root as PlotlyRoot, Layout } from 'plotly.js-dist-min'
export const insertGraph2Svg = ({ parent, graph, box, ruler }: {
parent: SVGSVGElement
graph: {
root: PlotlyRoot
data: PlotlyData[]
layout?: Partial<Layout>
}
box: { x: number, y: number, width: number, height: number }
ruler?: true
}) => {
const svg = SVG(parent)
const { x, y, width, height } = box
plotly.newPlot(
/**
* html node しか受け付けてないのでお目当ての svg (parent) ではなく
* 適当な hidden div を root として一度そっちに吐いちゃう
*/
graph.root,
graph.data,
{ width, height, ...graph.layout },
{ staticPlot: true },
).then(dom => {
/**
* dom まるごと foreignObject で wrap して入れる感じ
*/
const plot = SVG(dom)
const foreign = svg.foreignObject(width, height).attr({ x, y })
if (ruler) {
plot.node.style.outline = '1px solid red'
}
plot.attr({ x, y })
foreign.add(SVG(plot))
})
}
import React, { useRef } from 'react'
import { ReactSVG } from 'react-svg'
export const Component = () => {
const ref = useRef(null)
return <>
<div ref={ref} style={{ display: 'none' }} />
{
ref.current &&
<ReactSVG
src={'/some/file.svg'}
afterInjection={svgNode => {
try {
insertGraph2Svg({
parent: svgNode,
box: { x: 0, y: 0, width: 300, height: 150 },
graph: {
root: ref.current,
data: [{
x: [1, 2, 3, 4, 5],
y: [1, 2, 4, 8, 16],
}],
layout: {
font: { size: 7 },
margin: { l: 30, r: 10, t: 15, b: 20, pad: 0 },
showlegend: false,
title: {
text: 'title',
yanchor: 'top',
font: { size: 7 }k
},
xaxis: {
title: { text: 'x-title', standoff: 0 },
},
yaxis: {
title: { text: 'y-title', standoff: 0 },
},
}
},
})
} catch (e) {
console.error(e)
}
}}
/>
}
</>
}