import { Group } from "konva/lib/Group"; import { Path } from "konva/lib/shapes/Path"; import { Rect } from "konva/lib/shapes/Rect"; export type SVGPath = { fill?: string; stroke?: string; strokeWidth?: number; data: string; }; export type SVGProps = { size: number[]; realWidth: number; offset: number[]; paths: SVGPath[]; }; export type SVGPathProps = { realWidth?: number; offset?: number[]; fixed?: boolean; }; export type PathsShapeAct = ReturnType; export const pathsToShapeAct = ( svg: SVGProps, props: SVGPathProps = { fixed: true }, test = false ) => { const size = svg.size; const realSize = props.realWidth || svg.realWidth; const scale = realSize / size[0]; const realBound = size.map((p) => p * scale); const offset = (props.offset || svg.offset).map((v) => v * scale); const paths = svg.paths.map( (path, ndx) => new Path({ data: path.data, id: `path-${ndx}`, name: `path`, strokeScaleEnabled: !!props.fixed, scale: { x: scale, y: scale }, }) ); const common = () => { paths.forEach((path, ndx) => { const attrib = svg.paths[ndx]; attrib.fill && path.fill(attrib.fill); attrib.stroke && path.stroke(attrib.stroke); attrib.strokeWidth && path.strokeWidth(attrib.strokeWidth); }); }; const rect = new Rect({ x: offset[0], y: offset[1], name: "rect", width: realBound[0], height: realBound[1], fill: `rgba(0, 0, 0, ${test ? 0.3 : 0})`, }); const offsetGroup = new Group(); offsetGroup.add(...paths, rect); offsetGroup.x(-realBound[0] / 2); offsetGroup.y(-realBound[1] / 2); common(); return { shape: offsetGroup, common, setStroke(color: string) { paths.forEach((path) => { path.stroke(color); }); }, setFill(color: string) { paths.forEach((path) => { path.fill(color); }); }, }; }; const temp = document.createElement("div"); export const analysisSvgContent = (svgContent: string) => { temp.innerHTML = svgContent; const svg = temp.querySelector("svg"); const viewBoxStr = svg.getAttribute("viewBox"); const realWidth = svg.width.baseVal.value; const offset = [0, 0]; const size = [svg.width.baseVal.value, svg.height.baseVal.value]; if (viewBoxStr) { const viewBox = viewBoxStr.split(" ").map(Number); offset[0] = viewBox[0]; offset[1] = viewBox[1]; size[0] = viewBox[2] - viewBox[0]; size[1] = viewBox[3] - viewBox[1]; } const paths = Array.from(svg.querySelectorAll("path")); const pathDatas = paths.map((path) => { const fill = path.getAttribute("fill") || "#000"; const data = path.getAttribute("d"); const stroke = path.getAttribute("stroke") || "#000"; const strokeWidth = path.getAttribute("stroke-width"); return { fill, data, stroke, strokeWidth: strokeWidth && Number(strokeWidth), }; }); return { size, realWidth, offset, paths: pathDatas, }; }; const cache: { [key in string]: Promise | string } = {}; export const loadSvgContent = (url: string) => { if (url in cache) { if (typeof cache[url] === "string") { return Promise.resolve(cache[url]); } else { return cache[url]; } } else { return (cache[url] = fetch(url, { method: "GET" }) .then((res) => res.text()) .then((data) => { cache[url] = data; return data; })); } }; const SVGTypes: { [key in string]: SVGProps } = {}; export const getSVGProps = async (url: string) => { if (!(url in SVGTypes)) { const svgContent = await loadSvgContent(url); SVGTypes[url] = analysisSvgContent(svgContent); } return SVGTypes[url]; };