icon.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { Transform } from "konva/lib/Util";
  2. import {
  3. BaseItem,
  4. generateSnapInfos,
  5. getBaseItem,
  6. getRectSnapPoints,
  7. } from "../util.ts";
  8. import { getMouseColors } from "@/utils/colors.ts";
  9. import { InteractiveFix, InteractiveTo, MatResponseProps } from "../index.ts";
  10. import { Size } from "@/utils/math.ts";
  11. import { getSvgContent, parseSvgContent } from "@/utils/resource.ts";
  12. import { Color } from "three";
  13. export const shapeName = "图例";
  14. export const defaultStyle = {
  15. coverFill: "#000000",
  16. coverOpcatiy: 0,
  17. strokeScaleEnabled: false,
  18. width: 80,
  19. height: 80,
  20. };
  21. type ColorCounts = [string, number][];
  22. const colorsManage = (counts: ColorCounts, color: any) => {
  23. if (!color) {
  24. return;
  25. }
  26. const colorHex = new Color(color).getHexString();
  27. const item = counts.find(([c]) => c === colorHex);
  28. if (!item) {
  29. counts.push([colorHex, 1]);
  30. } else {
  31. item[1]++;
  32. }
  33. };
  34. const findColor = (counts: ColorCounts) => {
  35. let ndx = -1;
  36. let max = 0;
  37. for (let i = 0; i < counts.length; i++) {
  38. if (counts[i][1] >= max) {
  39. ndx = i;
  40. max = counts[i][1];
  41. }
  42. }
  43. if (~ndx) {
  44. return `#${counts[ndx][0]}`;
  45. }
  46. };
  47. export const getIconStyle = async (
  48. url: string,
  49. width = defaultStyle.width,
  50. height = defaultStyle.height,
  51. fixed = false
  52. ) => {
  53. const svgContent = parseSvgContent(await getSvgContent(url));
  54. if (!fixed) {
  55. if (width / height > svgContent.width / svgContent.height) {
  56. width = (svgContent.width / svgContent.height) * height;
  57. } else {
  58. height = (svgContent.height / svgContent.width) * width;
  59. }
  60. }
  61. const fillColorCounts: [string, number][] = [];
  62. const strokeColorCounts: [string, number][] = [];
  63. for (let i = 0; i < svgContent.paths.length; i++) {
  64. colorsManage(fillColorCounts, svgContent.paths[i].fill);
  65. colorsManage(strokeColorCounts, svgContent.paths[i].stroke);
  66. }
  67. const color = {
  68. fill: findColor(fillColorCounts) || null,
  69. stroke: findColor(strokeColorCounts) || null,
  70. };
  71. if (!color.fill && !color.stroke) {
  72. color.stroke = "#000000";
  73. }
  74. return {
  75. url,
  76. width,
  77. height,
  78. ...color,
  79. };
  80. };
  81. export const addMode = "dot";
  82. export const getSnapInfos = (data: IconData) => {
  83. return generateSnapInfos(getSnapPoints(data), true, false);
  84. };
  85. export const getSnapPoints = (data: IconData) => {
  86. const tf = new Transform(data.mat);
  87. const w = data.width || defaultStyle.width;
  88. const h = data.height || defaultStyle.height;
  89. const points = getRectSnapPoints(w, h);
  90. return points.map((v) => tf.point(v));
  91. };
  92. export const getMouseStyle = (data: IconData) => {
  93. const fillStatus = getMouseColors(data.coverFill || defaultStyle.coverFill);
  94. const hCoverOpcaoty = data.coverOpcatiy ? data.coverOpcatiy : 0.3;
  95. return {
  96. default: {
  97. coverFill: data.coverFill || defaultStyle.coverFill,
  98. coverOpcatiy: data.coverOpcatiy || 0,
  99. },
  100. hover: { coverFill: fillStatus.hover, coverOpcatiy: hCoverOpcaoty },
  101. select: { coverFill: fillStatus.select, coverOpcatiy: hCoverOpcaoty },
  102. focus: { coverFill: fillStatus.select, coverOpcatiy: hCoverOpcaoty },
  103. press: { coverFill: fillStatus.press, coverOpcatiy: hCoverOpcaoty },
  104. };
  105. };
  106. export type IconData = Partial<typeof defaultStyle> &
  107. BaseItem &
  108. Size & {
  109. fill?: string | null;
  110. stroke?: string | null;
  111. name?: string;
  112. strokeWidth?: number;
  113. coverFill?: string;
  114. coverStroke?: string;
  115. coverStrokeWidth?: number;
  116. mat: number[];
  117. url: string;
  118. };
  119. export const dataToConfig = (data: IconData) => {
  120. return {
  121. ...defaultStyle,
  122. ...data,
  123. };
  124. };
  125. export const interactiveToData: InteractiveTo<"icon"> = ({
  126. info,
  127. preset = {},
  128. viewTransform,
  129. ...args
  130. }) => {
  131. if (info.cur) {
  132. return interactiveFixData({
  133. ...args,
  134. viewTransform,
  135. info,
  136. data: { ...getBaseItem(), ...preset } as unknown as IconData,
  137. });
  138. }
  139. };
  140. export const interactiveFixData: InteractiveFix<"icon"> = ({
  141. data,
  142. info,
  143. }) => {
  144. const mat = new Transform().translate(info.cur!.x, info.cur!.y);
  145. data.mat = mat.m;
  146. return data;
  147. };
  148. export const matResponse = ({
  149. data,
  150. mat,
  151. increment,
  152. }: MatResponseProps<"icon">) => {
  153. data.mat = increment ? mat.copy().multiply(new Transform(data.mat)).m : mat.m;
  154. return data;
  155. };
  156. export const getPredefine = (key: keyof IconData) => {
  157. if (key === "fill" || key === "stroke") {
  158. return { canun: true };
  159. }
  160. };