Bläddra i källkod

feat: svg各种形状支持

bill 1 vecka sedan
förälder
incheckning
d3ba14a2f0

+ 1 - 0
package.json

@@ -36,6 +36,7 @@
     "pinia": "^2.2.4",
     "sass": "^1.80.4",
     "stateshot": "^1.3.5",
+    "svg-path-commander": "^2.1.11",
     "three": "^0.169.0",
     "uuid": "^11.0.2",
     "vite-plugin-html": "^3.2.2",

+ 14 - 0
pnpm-lock.yaml

@@ -24,6 +24,7 @@ specifiers:
   sass: ^1.80.4
   sass-embedded: ^1.80.4
   stateshot: ^1.3.5
+  svg-path-commander: ^2.1.11
   three: ^0.169.0
   typescript: ~5.6.2
   uuid: ^11.0.2
@@ -58,6 +59,7 @@ dependencies:
   pinia: 2.3.1_vphgr67rkmbnynx2ixl2jpzrzi
   sass: 1.97.1
   stateshot: 1.3.5
+  svg-path-commander: 2.1.11
   three: 0.169.0
   uuid: 11.1.0
   vite-plugin-html: 3.2.2_vite@5.4.21
@@ -712,6 +714,11 @@ packages:
     engines: {node: '>=16', pnpm: '>=8'}
     dev: false
 
+  /@thednp/dommatrix/2.0.12:
+    resolution: {integrity: sha512-eOshhlSShBXLfrMQqqhA450TppJXhKriaQdN43mmniOCMn9sD60QKF1Axsj7bKl339WH058LuGFS6H84njYH5w==}
+    engines: {node: '>=20', pnpm: '>=8.6.0'}
+    dev: false
+
   /@trysound/sax/0.2.0:
     resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
     engines: {node: '>=10.13.0'}
@@ -3548,6 +3555,13 @@ packages:
       - supports-color
     dev: false
 
+  /svg-path-commander/2.1.11:
+    resolution: {integrity: sha512-wmQ6QA3Od+HOcpIzLjPlbv59+x3yd3V5W6xitUOvAHmqZpP7wVrRM2CHqEm5viHUbZu6PjzFsjbTEFtIeUxaNA==}
+    engines: {node: '>=16', pnpm: '>=8.6.0'}
+    dependencies:
+      '@thednp/dommatrix': 2.0.12
+    dev: false
+
   /svg-pathdata/6.0.3:
     resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==}
     engines: {node: '>=12.0.0'}

+ 41 - 1
src/example/constant.ts

@@ -144,14 +144,38 @@ export const iconGroups: IconGroup[] = [
         name: "客餐厅",
         children: [
           { icon: "TV", name: "电视柜" },
+          { icon: "HangingTV", name: "电视-壁挂" },
+          { icon: "Cabinet", name: "柜子" },
+
+
           { icon: "CombinationSofa", name: "组合沙发" },
           { icon: "ThreeSofa", name: "三人沙发" },
           { icon: "SingleSofa", name: "单人沙发" },
-          { icon: "Chair", name: "椅子" },
+
+          { icon: "SingleSofaR", name: "组合沙发" },
+          { icon: "CurvedSofa", name: "弧形沙发2.2m" },
+          { icon: "CornerSofa", name: "转角沙发2.7m" },
+          { icon: "TwoSofa", name: "双人沙发1.8m" },
+
+
           { icon: "TeaTable", name: "茶几" },
           { icon: "Carpet", name: "地毯" },
           { icon: "Plant", name: "植物" },
+
+          { icon: "TeaTableR", name: "茶几-圆形" },
+          { icon: "TeaTableO", name: "茶几-椭圆" },
+          { icon: "SideTableR", name: "边几-圆形" },
+          { icon: "SideTableS", name: "边几-方形" },
+
+
+          { icon: "DiningTableC", name: "餐桌椅" }, 
+          { icon: "Chair", name: "椅子" },
+
+          { icon: "DiningTableRC", name: "餐桌" },
           { icon: "DiningTable", name: "餐桌" },
+          { icon: "DiningTableR", name: "餐桌-圆" },
+          { icon: "Stool", name: "条凳" },
+
         ],
       },
       {
@@ -160,6 +184,8 @@ export const iconGroups: IconGroup[] = [
           { icon: "DoubleBed", name: "双人床" },
           { icon: "SingleBed", name: "单人床" },
           { icon: "Wardrobe", name: "衣柜" },
+          { icon: "DoubleBedB", name: "双人床1.8m " },
+
           { icon: "Dresser", name: "梳妆台" },
           { icon: "BedsideCupboard", name: "床头柜" },
           { icon: "Pillow", name: "抱枕" },
@@ -173,6 +199,15 @@ export const iconGroups: IconGroup[] = [
           { icon: "Bathtub", name: "浴缸" },
           { icon: "Closestool", name: "马桶" },
           { icon: "Washstand", name: "洗漱台" },
+
+          { icon: "CupboardU", name: "通用橱柜" },
+          { icon: "WaterChannel", name: "水槽" },
+          { icon: "WaterChannelD", name: "双水槽" },
+          { icon: "FridgeS", name: "冰箱" },
+          { icon: "FridgeD", name: "冰箱-双门" },
+          { icon: "CabinetB", name: "浴室柜" },
+          { icon: "SquattingPan", name: "蹲便器" },
+          { icon: "Shower", name: "花洒" },
         ],
       },
       {
@@ -180,7 +215,12 @@ export const iconGroups: IconGroup[] = [
         children: [
           { icon: "Desk", name: "书桌" },
           { icon: "BalconyChair", name: "阳台椅" },
+          { icon: "MopPool", name: "拖把池" },
+          { icon: "WashingMachine", name: "洗衣机" },
           { icon: "Elevator", name: "电梯" },
+          { icon: "WaterFountain", name: "饮水机" },
+          { icon: "AirConditioner", name: "空调-圆形" },
+          { icon: "Tablelamp", name: "台灯" },
         ],
       },
     ],

+ 5 - 5
src/example/fuse/views/tabulation/header.vue

@@ -78,11 +78,11 @@ const actions = [
     {
       ...baseActions.initViewport,
       handler: () => {
-        if (import.meta.env.DEV) {
-          draw.viewer.setViewMat(new Transform());
-        } else {
-          initViewport(draw, 0.05);
-        }
+        // if (import.meta.env.DEV) {
+        // draw.viewer.setViewMat(new Transform());
+        // } else {
+        initViewport(draw, 0.05);
+        // }
       },
     },
   ],

+ 12 - 3
src/example/fuse/views/tabulation/index.vue

@@ -17,7 +17,7 @@
     @change-origin="(origin) => setOrigin && setOrigin(origin)"
     :cover-scale="coverSetting?.scale"
     :paperKey="tabulationData?.paperKey"
-    :pixelRatio="needScreenshot ? 4 : pixelRatio"
+    :pixelRatio="needScreenshot ? 2 : pixelRatio"
     :not-debounce="needScreenshot"
     :data="overviewData?.store"
     :scale="draw?.viewerConfig.scaleX"
@@ -274,9 +274,15 @@ const setCover = (paperKey: PaperKey, draw: Draw) => {
   const mountMenus = draw.mountFilter;
   const menusFilter = draw.menusFilter;
   draw.excludeSelection.push(cover.id);
+  const scale = ref<number>();
 
   cleanups.push(
     watch(
+      () => coverSetting.value?.scale,
+      (v) => (scale.value = v),
+      { immediate: true }
+    ),
+    watch(
       coverSetting,
       (setting) => {
         if (coverTexQue.value) {
@@ -294,11 +300,14 @@ const setCover = (paperKey: PaperKey, draw: Draw) => {
         label: "缩放比例",
         "layout-type": "row",
         get value() {
-          return coverSetting.value?.scale;
+          return scale.value;
         },
         set value(val) {
+          scale.value = Math.max(val || 0, 1);
+        },
+        onChange() {
           if (coverSetting.value) {
-            coverSetting.value.scale = Math.max(val || 0, 1);
+            coverSetting.value.scale = scale.value!;
           }
         },
         props: { min: 1 },

+ 15 - 3
src/example/fuse/views/tabulation/overview-viewport.vue

@@ -145,12 +145,20 @@ const realRect = computed(
     }
 );
 
-const viewScale = computed(() => {
+const calcViewScale = () => {
   if (!pixelPaperToDrawPixel.value) return;
   let scale = pixelPaperToDrawPixel.value < 1 ? 1 : props.scale || 1;
   scale = Math.min(Math.max(scale, 1), 10);
   scale *= props.pixelRatio || 1;
   return scale;
+};
+const viewScale = ref(calcViewScale());
+watchEffect(() => {
+  const curScale = calcViewScale();
+  viewScale.value = curScale;
+  // if (!curScale || !viewScale.value || curScale > viewScale.value) {
+  //   viewScale.value = curScale;
+  // }
 });
 
 const originSize = computed(
@@ -191,12 +199,15 @@ const updateOrigin = async () => {
         item.__strokeWidth = item.strokeWidth;
       }
       if ("__strokeWidth" in item) {
-        item.strokeWidth = (item.__strokeWidth * pixelPaperToDrawPixel.value!) / 2;
+        item.strokeWidth =
+          ((props.pixelRatio || 1) *
+            (item.__strokeWidth * pixelPaperToDrawPixel.value!)) /
+          2;
       }
     };
     d.store.items.forEach(setStyle);
     const lineItems = d.store.getTypeItems("line")[0];
-    lineItems.lines.forEach(setStyle);
+    lineItems?.lines.forEach(setStyle);
   }
 
   const size = canvasSize.value as Size;
@@ -236,6 +247,7 @@ watch([preed, viewScale, () => props.notDebounce], () => {
 watch(coverScale, () => {
   frameUpdateOrigin.stop();
   preed.value && updateOrigin();
+  nextTick(() => frameUpdateOrigin.stop());
 });
 </script>
 

+ 9 - 0
src/utils/resource.ts

@@ -1,5 +1,6 @@
 import { reactive } from "vue";
 import { Pos, Size } from "./math";
+import { shapeToPath } from 'svg-path-commander';
 
 let imageCache: Record<string, Promise<HTMLImageElement>>;
 let imageCache1: Record<string, HTMLImageElement>;
@@ -91,6 +92,14 @@ export let parseSvgContent = (svgContent: string): SVGParseResult => {
     let bottom = 0;
     let x = Number.MAX_VALUE;
     let y = Number.MAX_VALUE;
+    const $svg = helpDOM.children[0]
+    for (const $item of $svg.children) {
+      if ($item.tagName.toUpperCase() === 'PATH') {
+        continue;
+      }
+      shapeToPath($item as any, true)
+    }
+
     const svgPaths = Array.from(helpDOM.querySelectorAll("path"));
     const paths = svgPaths.map((path) => {
       const box = path.getBBox();