svg.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { Group } from "konva/lib/Group";
  2. import { Path } from "konva/lib/shapes/Path";
  3. import { Rect } from "konva/lib/shapes/Rect";
  4. export type SVGPath = {
  5. fill?: string;
  6. stroke?: string;
  7. strokeWidth?: number;
  8. data: string;
  9. };
  10. export type SVGProps = {
  11. size: number[];
  12. realWidth: number;
  13. offset: number[];
  14. paths: SVGPath[];
  15. };
  16. export type SVGPathProps = {
  17. realWidth?: number;
  18. offset?: number[];
  19. fixed?: boolean;
  20. };
  21. export type PathsShapeAct = ReturnType<typeof pathsToShapeAct>;
  22. export const pathsToShapeAct = (
  23. svg: SVGProps,
  24. props: SVGPathProps = { fixed: true },
  25. test = false
  26. ) => {
  27. const size = svg.size;
  28. const realSize = props.realWidth || svg.realWidth;
  29. const scale = realSize / size[0];
  30. const realBound = size.map((p) => p * scale);
  31. const offset = (props.offset || svg.offset).map((v) => v * scale);
  32. const paths = svg.paths.map(
  33. (path, ndx) =>
  34. new Path({
  35. data: path.data,
  36. id: `path-${ndx}`,
  37. name: `path`,
  38. strokeScaleEnabled: !!props.fixed,
  39. scale: { x: scale, y: scale },
  40. })
  41. );
  42. const common = () => {
  43. paths.forEach((path, ndx) => {
  44. const attrib = svg.paths[ndx];
  45. attrib.fill && path.fill(attrib.fill);
  46. attrib.stroke && path.stroke(attrib.stroke);
  47. attrib.strokeWidth && path.strokeWidth(attrib.strokeWidth);
  48. });
  49. };
  50. const rect = new Rect({
  51. x: offset[0],
  52. y: offset[1],
  53. name: "rect",
  54. width: realBound[0],
  55. height: realBound[1],
  56. fill: `rgba(0, 0, 0, ${test ? 0.3 : 0})`,
  57. });
  58. const offsetGroup = new Group();
  59. offsetGroup.add(...paths, rect);
  60. offsetGroup.x(-realBound[0] / 2);
  61. offsetGroup.y(-realBound[1] / 2);
  62. common();
  63. return {
  64. shape: offsetGroup,
  65. common,
  66. setStroke(color: string) {
  67. paths.forEach((path) => {
  68. path.stroke(color);
  69. });
  70. },
  71. setFill(color: string) {
  72. paths.forEach((path) => {
  73. path.fill(color);
  74. });
  75. },
  76. };
  77. };
  78. const temp = document.createElement("div");
  79. export const analysisSvgContent = (svgContent: string) => {
  80. temp.innerHTML = svgContent;
  81. const svg = temp.querySelector("svg");
  82. const viewBoxStr = svg.getAttribute("viewBox");
  83. const realWidth = svg.width.baseVal.value;
  84. const offset = [0, 0];
  85. const size = [svg.width.baseVal.value, svg.height.baseVal.value];
  86. if (viewBoxStr) {
  87. const viewBox = viewBoxStr.split(" ").map(Number);
  88. offset[0] = viewBox[0];
  89. offset[1] = viewBox[1];
  90. size[0] = viewBox[2] - viewBox[0];
  91. size[1] = viewBox[3] - viewBox[1];
  92. }
  93. const paths = Array.from(svg.querySelectorAll("path"));
  94. const pathDatas = paths.map((path) => {
  95. const fill = path.getAttribute("fill") || "#000";
  96. const data = path.getAttribute("d");
  97. const stroke = path.getAttribute("stroke") || "#000";
  98. const strokeWidth = path.getAttribute("stroke-width");
  99. return {
  100. fill,
  101. data,
  102. stroke,
  103. strokeWidth: strokeWidth && Number(strokeWidth),
  104. };
  105. });
  106. return {
  107. size,
  108. realWidth,
  109. offset,
  110. paths: pathDatas,
  111. };
  112. };
  113. const cache: { [key in string]: Promise<string> | string } = {};
  114. export const loadSvgContent = (url: string) => {
  115. if (url in cache) {
  116. if (typeof cache[url] === "string") {
  117. return Promise.resolve(cache[url]);
  118. } else {
  119. return cache[url];
  120. }
  121. } else {
  122. return (cache[url] = fetch(url, { method: "GET" })
  123. .then((res) => res.text())
  124. .then((data) => {
  125. cache[url] = data;
  126. return data;
  127. }));
  128. }
  129. };
  130. const SVGTypes: { [key in string]: SVGProps } = {};
  131. export const getSVGProps = async (url: string) => {
  132. if (!(url in SVGTypes)) {
  133. const svgContent = await loadSvgContent(url);
  134. SVGTypes[url] = analysisSvgContent(svgContent);
  135. }
  136. return SVGTypes[url];
  137. };