Browse Source

图形 兼容

黄琪勇 3 months ago
parent
commit
f2966818af

+ 10 - 14
src/views/Editor/CanvasTool/ShapeItemThumbnail.vue

@@ -1,23 +1,19 @@
 <template>
   <div class="shape-item-thumbnail">
     <div class="shape-content">
-      <svg 
-        overflow="visible" 
-        width="18"
-        height="18"
-      >
-        <g 
-          :transform="`scale(${18 / shape.viewBox[0]}, ${18 / shape.viewBox[1]}) translate(0,0) matrix(1,0,0,1,0,0)`"
+      <svg overflow="visible" width="18" height="18">
+        <g
+          :transform="`scale(${18 / (shape.viewBox[0] || shape.viewBox)}, ${18 / (shape.viewBox[1] || shape.viewBox)}) translate(0,0) matrix(1,0,0,1,0,0)`"
         >
-          <path 
+          <path
             class="shape-path"
-            :class="{ 'outlined': shape.outlined }"
-            vector-effect="non-scaling-stroke" 
-            stroke-linecap="butt" 
+            :class="{ outlined: shape.outlined }"
+            vector-effect="non-scaling-stroke"
+            stroke-linecap="butt"
             stroke-miterlimit="8"
             :fill="shape.outlined ? '#999' : 'transparent'"
             :stroke="shape.outlined ? 'transparent' : '#999'"
-            stroke-width="2" 
+            stroke-width="2"
             :d="shape.path"
           ></path>
         </g>
@@ -27,7 +23,7 @@
 </template>
 
 <script lang="ts" setup>
-import type { ShapePoolItem } from '@/configs/shapes'
+import type { ShapePoolItem } from "@/configs/shapes"
 
 defineProps<{
   shape: ShapePoolItem
@@ -59,4 +55,4 @@ defineProps<{
     overflow: visible;
   }
 }
-</style>
+</style>

+ 11 - 14
src/views/components/element/LatexElement/BaseLatexElement.vue

@@ -1,30 +1,27 @@
 <template>
-  <div 
+  <div
     class="base-element-latex"
     :style="{
       top: elementInfo.top + 'px',
       left: elementInfo.left + 'px',
       width: elementInfo.width + 'px',
-      height: elementInfo.height + 'px',
+      height: elementInfo.height + 'px'
     }"
   >
-    <div
-      class="rotate-wrapper"
-      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
-    >
+    <div class="rotate-wrapper" :style="{ transform: `rotate(${elementInfo.rotate}deg)` }">
       <div class="element-content">
-        <svg 
-          overflow="visible" 
+        <svg
+          overflow="visible"
           :width="elementInfo.width"
           :height="elementInfo.height"
-          :stroke="elementInfo.color" 
-          :stroke-width="elementInfo.strokeWidth" 
-          fill="none" 
+          :stroke="elementInfo.color"
+          :stroke-width="elementInfo.strokeWidth"
+          fill="none"
           stroke-linecap="round"
           stroke-linejoin="round"
         >
-          <g 
-            :transform="`scale(${elementInfo.width / elementInfo.viewBox[0]}, ${elementInfo.height / elementInfo.viewBox[1]}) translate(0,0) matrix(1,0,0,1,0,0)`"
+          <g
+            :transform="`scale(${elementInfo.width / (elementInfo.viewBox[0] || elementInfo.viewBox)}, ${elementInfo.height / (elementInfo.viewBox[1] || elementInfo.viewBox)}) translate(0,0) matrix(1,0,0,1,0,0)`"
           >
             <path :d="elementInfo.path"></path>
           </g>
@@ -35,7 +32,7 @@
 </template>
 
 <script lang="ts" setup>
-import type { PPTLatexElement } from '@/types/slides'
+import type { PPTLatexElement } from "@/types/slides"
 
 defineProps<{
   elementInfo: PPTLatexElement

+ 16 - 19
src/views/components/element/LatexElement/index.vue

@@ -1,37 +1,34 @@
 <template>
-  <div 
+  <div
     class="editable-element-latex"
-    :class="{ 'lock': elementInfo.lock }"
+    :class="{ lock: elementInfo.lock }"
     :style="{
       top: elementInfo.top + 'px',
       left: elementInfo.left + 'px',
       width: elementInfo.width + 'px',
-      height: elementInfo.height + 'px',
+      height: elementInfo.height + 'px'
     }"
   >
-    <div
-      class="rotate-wrapper"
-      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
-    >
-      <div 
-        class="element-content" 
+    <div class="rotate-wrapper" :style="{ transform: `rotate(${elementInfo.rotate}deg)` }">
+      <div
+        class="element-content"
         v-contextmenu="contextmenus"
         @mousedown="$event => handleSelectElement($event)"
         @touchstart="$event => handleSelectElement($event)"
         @dblclick="openLatexEditor()"
       >
-        <svg 
-          overflow="visible" 
+        <svg
+          overflow="visible"
           :width="elementInfo.width"
           :height="elementInfo.height"
-          :stroke="elementInfo.color" 
-          :stroke-width="elementInfo.strokeWidth" 
-          fill="none" 
+          :stroke="elementInfo.color"
+          :stroke-width="elementInfo.strokeWidth"
+          fill="none"
           stroke-linecap="round"
           stroke-linejoin="round"
         >
-          <g 
-            :transform="`scale(${elementInfo.width / elementInfo.viewBox[0]}, ${elementInfo.height / elementInfo.viewBox[1]}) translate(0,0) matrix(1,0,0,1,0,0)`"
+          <g
+            :transform="`scale(${elementInfo.width / (elementInfo.viewBox[0] || elementInfo.viewBox)}, ${elementInfo.height / (elementInfo.viewBox[1] || elementInfo.viewBox)}) translate(0,0) matrix(1,0,0,1,0,0)`"
           >
             <path :d="elementInfo.path"></path>
           </g>
@@ -42,9 +39,9 @@
 </template>
 
 <script lang="ts" setup>
-import type { PPTLatexElement } from '@/types/slides'
-import type { ContextmenuItem } from '@/components/Contextmenu/types'
-import emitter, { EmitterEvents } from '@/utils/emitter'
+import type { PPTLatexElement } from "@/types/slides"
+import type { ContextmenuItem } from "@/components/Contextmenu/types"
+import emitter, { EmitterEvents } from "@/utils/emitter"
 
 const props = defineProps<{
   elementInfo: PPTLatexElement

+ 25 - 32
src/views/components/element/ShapeElement/BaseShapeElement.vue

@@ -1,52 +1,45 @@
 <template>
-  <div 
+  <div
     class="base-element-shape"
     :style="{
       top: elementInfo.top + 'px',
       left: elementInfo.left + 'px',
       width: elementInfo.width + 'px',
-      height: elementInfo.height + 'px',
+      height: elementInfo.height + 'px'
     }"
   >
-    <div
-      class="rotate-wrapper"
-      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
-    >
-      <div 
+    <div class="rotate-wrapper" :style="{ transform: `rotate(${elementInfo.rotate}deg)` }">
+      <div
         class="element-content"
         :style="{
           opacity: elementInfo.opacity,
           filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
           transform: flipStyle,
           color: text.defaultColor,
-          fontFamily: text.defaultFontName,
+          fontFamily: text.defaultFontName
         }"
       >
-        <svg 
-          overflow="visible" 
-          :width="elementInfo.width"
-          :height="elementInfo.height"
-        >
+        <svg overflow="visible" :width="elementInfo.width" :height="elementInfo.height">
           <defs v-if="elementInfo.gradient">
             <GradientDefs
-              :id="`base-gradient-${elementInfo.id}`" 
+              :id="`base-gradient-${elementInfo.id}`"
               :type="elementInfo.gradient.type"
               :colors="elementInfo.gradient.colors"
               :rotate="elementInfo.gradient.rotate"
             />
           </defs>
-          <g 
-            :transform="`scale(${elementInfo.width / elementInfo.viewBox[0]}, ${elementInfo.height / elementInfo.viewBox[1]}) translate(0,0) matrix(1,0,0,1,0,0)`"
+          <g
+            :transform="`scale(${elementInfo.width / (elementInfo.viewBox[0] || elementInfo.viewBox)}, ${elementInfo.height / (elementInfo.viewBox[1] || elementInfo.viewBox)}) translate(0,0) matrix(1,0,0,1,0,0)`"
           >
-            <path 
-              vector-effect="non-scaling-stroke" 
-              stroke-linecap="butt" 
+            <path
+              vector-effect="non-scaling-stroke"
+              stroke-linecap="butt"
               stroke-miterlimit="8"
-              :d="elementInfo.path" 
+              :d="elementInfo.path"
               :fill="elementInfo.gradient ? `url(#base-gradient-${elementInfo.id})` : elementInfo.fill"
               :stroke="outlineColor"
-              :stroke-width="outlineWidth" 
-              :stroke-dasharray="strokeDashArray" 
+              :stroke-width="outlineWidth"
+              :stroke-dasharray="strokeDashArray"
             ></path>
           </g>
         </svg>
@@ -60,13 +53,13 @@
 </template>
 
 <script lang="ts" setup>
-import { computed } from 'vue'
-import type { PPTShapeElement, ShapeText } from '@/types/slides'
-import useElementOutline from '@/views/components/element/hooks/useElementOutline'
-import useElementShadow from '@/views/components/element/hooks/useElementShadow'
-import useElementFlip from '@/views/components/element/hooks/useElementFlip'
+import { computed } from "vue"
+import type { PPTShapeElement, ShapeText } from "@/types/slides"
+import useElementOutline from "@/views/components/element/hooks/useElementOutline"
+import useElementShadow from "@/views/components/element/hooks/useElementShadow"
+import useElementFlip from "@/views/components/element/hooks/useElementFlip"
 
-import GradientDefs from './GradientDefs.vue'
+import GradientDefs from "./GradientDefs.vue"
 
 const props = defineProps<{
   elementInfo: PPTShapeElement
@@ -84,10 +77,10 @@ const { flipStyle } = useElementFlip(flipH, flipV)
 
 const text = computed<ShapeText>(() => {
   const defaultText: ShapeText = {
-    content: '',
-    defaultFontName: '微软雅黑',
-    defaultColor: '#000',
-    align: 'middle',
+    content: "",
+    defaultFontName: "微软雅黑",
+    defaultColor: "#000",
+    align: "middle"
   }
   if (!props.elementInfo.text) return defaultText
 

+ 47 - 51
src/views/components/element/ShapeElement/index.vue

@@ -1,29 +1,26 @@
 <template>
-  <div 
+  <div
     class="editable-element-shape"
     :class="{
-      'lock': elementInfo.lock,
-      'format-painter': shapeFormatPainter,
+      lock: elementInfo.lock,
+      'format-painter': shapeFormatPainter
     }"
     :style="{
       top: elementInfo.top + 'px',
       left: elementInfo.left + 'px',
       width: elementInfo.width + 'px',
-      height: elementInfo.height + 'px',
+      height: elementInfo.height + 'px'
     }"
   >
-    <div
-      class="rotate-wrapper"
-      :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
-    >
-      <div 
-        class="element-content" 
+    <div class="rotate-wrapper" :style="{ transform: `rotate(${elementInfo.rotate}deg)` }">
+      <div
+        class="element-content"
         :style="{
           opacity: elementInfo.opacity,
           filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
           transform: flipStyle,
           color: text.defaultColor,
-          fontFamily: text.defaultFontName,
+          fontFamily: text.defaultFontName
         }"
         v-contextmenu="contextmenus"
         @mousedown="$event => handleSelectElement($event)"
@@ -31,37 +28,33 @@
         @touchstart="$event => handleSelectElement($event)"
         @dblclick="startEdit()"
       >
-        <svg 
-          overflow="visible" 
-          :width="elementInfo.width"
-          :height="elementInfo.height"
-        >
+        <svg overflow="visible" :width="elementInfo.width" :height="elementInfo.height">
           <defs v-if="elementInfo.gradient">
             <GradientDefs
-              :id="`editabel-gradient-${elementInfo.id}`" 
+              :id="`editabel-gradient-${elementInfo.id}`"
               :type="elementInfo.gradient.type"
               :colors="elementInfo.gradient.colors"
               :rotate="elementInfo.gradient.rotate"
             />
           </defs>
-          <g 
-            :transform="`scale(${elementInfo.width / elementInfo.viewBox[0]}, ${elementInfo.height / elementInfo.viewBox[1]}) translate(0,0) matrix(1,0,0,1,0,0)`"
+          <g
+            :transform="`scale(${elementInfo.width / (elementInfo.viewBox[0] || elementInfo.viewBox)}, ${elementInfo.height / (elementInfo.viewBox[1] || elementInfo.viewBox)}) translate(0,0) matrix(1,0,0,1,0,0)`"
           >
-            <path 
+            <path
               class="shape-path"
-              vector-effect="non-scaling-stroke" 
-              stroke-linecap="butt" 
+              vector-effect="non-scaling-stroke"
+              stroke-linecap="butt"
               stroke-miterlimit="8"
-              :d="elementInfo.path" 
+              :d="elementInfo.path"
               :fill="elementInfo.gradient ? `url(#editabel-gradient-${elementInfo.id})` : elementInfo.fill"
               :stroke="outlineColor"
-              :stroke-width="outlineWidth" 
-              :stroke-dasharray="strokeDashArray" 
+              :stroke-width="outlineWidth"
+              :stroke-dasharray="strokeDashArray"
             ></path>
           </g>
         </svg>
 
-        <div class="shape-text" :class="[text.align, { 'editable': editable || text.content }]">
+        <div class="shape-text" :class="[text.align, { editable: editable || text.content }]">
           <ProsemirrorEditor
             ref="prosemirrorEditorRef"
             v-if="editable || text.content"
@@ -81,18 +74,18 @@
 </template>
 
 <script lang="ts" setup>
-import { computed, nextTick, ref, watch } from 'vue'
-import { storeToRefs } from 'pinia'
-import { useMainStore, useSlidesStore } from '@/store'
-import type { PPTShapeElement, ShapeText } from '@/types/slides'
-import type { ContextmenuItem } from '@/components/Contextmenu/types'
-import useElementOutline from '@/views/components/element/hooks/useElementOutline'
-import useElementShadow from '@/views/components/element/hooks/useElementShadow'
-import useElementFlip from '@/views/components/element/hooks/useElementFlip'
-import useHistorySnapshot from '@/hooks/useHistorySnapshot'
-
-import GradientDefs from './GradientDefs.vue'
-import ProsemirrorEditor from '@/views/components/element/ProsemirrorEditor.vue'
+import { computed, nextTick, ref, watch } from "vue"
+import { storeToRefs } from "pinia"
+import { useMainStore, useSlidesStore } from "@/store"
+import type { PPTShapeElement, ShapeText } from "@/types/slides"
+import type { ContextmenuItem } from "@/components/Contextmenu/types"
+import useElementOutline from "@/views/components/element/hooks/useElementOutline"
+import useElementShadow from "@/views/components/element/hooks/useElementShadow"
+import useElementFlip from "@/views/components/element/hooks/useElementFlip"
+import useHistorySnapshot from "@/hooks/useHistorySnapshot"
+
+import GradientDefs from "./GradientDefs.vue"
+import ProsemirrorEditor from "@/views/components/element/ProsemirrorEditor.vue"
 
 const props = defineProps<{
   elementInfo: PPTShapeElement
@@ -118,10 +111,10 @@ const execFormatPainter = () => {
   const { keep, ...newProps } = shapeFormatPainter.value
 
   slidesStore.updateElement({
-    id: props.elementInfo.id, 
-    props: newProps,
+    id: props.elementInfo.id,
+    props: newProps
   })
-  
+
   addHistorySnapshot()
   if (!keep) mainStore.setShapeFormatPainter(null)
 }
@@ -146,10 +139,10 @@ watch(handleElementId, () => {
 
 const text = computed<ShapeText>(() => {
   const defaultText: ShapeText = {
-    content: '',
-    defaultFontName: '微软雅黑',
-    defaultColor: '#000',
-    align: 'middle',
+    content: "",
+    defaultFontName: "微软雅黑",
+    defaultColor: "#000",
+    align: "middle"
   }
   if (!props.elementInfo.text) return defaultText
 
@@ -159,19 +152,19 @@ const text = computed<ShapeText>(() => {
 const updateText = (content: string, ignore = false) => {
   const _text = { ...text.value, content }
   slidesStore.updateElement({
-    id: props.elementInfo.id, 
-    props: { text: _text },
+    id: props.elementInfo.id,
+    props: { text: _text }
   })
-  
+
   if (!ignore) addHistorySnapshot()
 }
 
 const checkEmptyText = () => {
   if (!props.elementInfo.text) return
 
-  const pureText = props.elementInfo.text.content.replace(/<[^>]+>/g, '')
+  const pureText = props.elementInfo.text.content.replace(/<[^>]+>/g, "")
   if (!pureText) {
-    slidesStore.removeElementProps({ id: props.elementInfo.id, propName: 'text' })
+    slidesStore.removeElementProps({ id: props.elementInfo.id, propName: "text" })
     addHistorySnapshot()
   }
 }
@@ -192,7 +185,10 @@ const startEdit = () => {
     cursor: default;
   }
   &.format-painter .element-content {
-    cursor: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzQiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNzUgMTMuNzY0VjEuNDIxYS4zLjMgMCAwMS40NDgtLjI2bDEwLjkxIDYuMTk3YS4zLjMgMCAwMS0uMTE2LjU1OWwtNC4xOTYuNDQyIDIuNTgyIDQuNDcyYS4zLjMgMCAwMS0uMTEuNDFsLTMuMTg0IDEuODM4YS4zLjMgMCAwMS0uNDEtLjExbC0yLjU4MS00LjQ3Mi0yLjgxIDMuNDU2YS4zLjMgMCAwMS0uNTMzLS4xODl6IiBmaWxsPSIjZmZmIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjYgMTQuNWw0LjUtNC41LTYtNmMtMiAyLTMgMi01LjUgMi41LjQgMy4yIDQuODMzIDYuNjY3IDcgOHptNC41ODgtNC40OTRhLjMuMyAwIDAwLjQyNCAwbC42OC0uNjhhMS41IDEuNSAwIDAwMC0yLjEyMUwzMC4zNCA1Ljg1MmwyLjAyNi0xLjU4MmExLjYyOSAxLjYyOSAwIDEwLTIuMjgtMi4yOTZsLTEuNjAzIDIuMDIxLTEuMzU3LTEuMzU2YTEuNSAxLjUgMCAwMC0yLjEyIDBsLS42ODEuNjhhLjMuMyAwIDAwMCAuNDI0bDYuMjYzIDYuMjYzeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0yNC41NDMgMy45NjFzLTEuMDMgMS4yMDItMi40OTQgMS44OTFjLTEuMDA2LjQ3NC0yLjE4MS41ODUtMi43MzQuNjI3LS4yLjAxNC0uMzQ0LjIwOS0uMjc3LjM5OC4yOTMuODIgMS4xMTIgMi44MDEgMi42NTggNC4zNDcgMi4xMjYgMi4xMjYgMy42NTkgMi45NjggNC4xNDIgMy4yMDIuMS4wNDguMjE1LjAzLjI5OS0uMDQxLjM4NS0uMzI2IDEuNS0xLjI3NyAyLjIxLTEuOTg2Ljg5MS0uODkgMi4xODYtMi40NDggMi4xODYtMi40NDhtLjQ4LjA1NWEuMy4zIDAgMDEtLjQyNSAwbC02LjI2My02LjI2M2EuMy4zIDAgMDEwLS40MjRsLjY4LS42OGExLjUgMS41IDAgMDEyLjEyMiAwbDEuMzU2IDEuMzU2IDEuNjA0LTIuMDIxYTEuNjI5IDEuNjI5IDAgMTEyLjI3OSAyLjI5NkwzMC4zNCA1Ljg1MmwxLjM1MyAxLjM1M2ExLjUgMS41IDAgMDEwIDIuMTIxbC0uNjguNjh6IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L3N2Zz4=) 2 5, default !important;
+    cursor:
+      url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzQiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNzUgMTMuNzY0VjEuNDIxYS4zLjMgMCAwMS40NDgtLjI2bDEwLjkxIDYuMTk3YS4zLjMgMCAwMS0uMTE2LjU1OWwtNC4xOTYuNDQyIDIuNTgyIDQuNDcyYS4zLjMgMCAwMS0uMTEuNDFsLTMuMTg0IDEuODM4YS4zLjMgMCAwMS0uNDEtLjExbC0yLjU4MS00LjQ3Mi0yLjgxIDMuNDU2YS4zLjMgMCAwMS0uNTMzLS4xODl6IiBmaWxsPSIjZmZmIiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjYgMTQuNWw0LjUtNC41LTYtNmMtMiAyLTMgMi01LjUgMi41LjQgMy4yIDQuODMzIDYuNjY3IDcgOHptNC41ODgtNC40OTRhLjMuMyAwIDAwLjQyNCAwbC42OC0uNjhhMS41IDEuNSAwIDAwMC0yLjEyMUwzMC4zNCA1Ljg1MmwyLjAyNi0xLjU4MmExLjYyOSAxLjYyOSAwIDEwLTIuMjgtMi4yOTZsLTEuNjAzIDIuMDIxLTEuMzU3LTEuMzU2YTEuNSAxLjUgMCAwMC0yLjEyIDBsLS42ODEuNjhhLjMuMyAwIDAwMCAuNDI0bDYuMjYzIDYuMjYzeiIgZmlsbD0iI2ZmZiIvPjxwYXRoIGQ9Ik0yNC41NDMgMy45NjFzLTEuMDMgMS4yMDItMi40OTQgMS44OTFjLTEuMDA2LjQ3NC0yLjE4MS41ODUtMi43MzQuNjI3LS4yLjAxNC0uMzQ0LjIwOS0uMjc3LjM5OC4yOTMuODIgMS4xMTIgMi44MDEgMi42NTggNC4zNDcgMi4xMjYgMi4xMjYgMy42NTkgMi45NjggNC4xNDIgMy4yMDIuMS4wNDguMjE1LjAzLjI5OS0uMDQxLjM4NS0uMzI2IDEuNS0xLjI3NyAyLjIxLTEuOTg2Ljg5MS0uODkgMi4xODYtMi40NDggMi4xODYtMi40NDhtLjQ4LjA1NWEuMy4zIDAgMDEtLjQyNSAwbC02LjI2My02LjI2M2EuMy4zIDAgMDEwLS40MjRsLjY4LS42OGExLjUgMS41IDAgMDEyLjEyMiAwbDEuMzU2IDEuMzU2IDEuNjA0LTIuMDIxYTEuNjI5IDEuNjI5IDAgMTEyLjI3OSAyLjI5NkwzMC4zNCA1Ljg1MmwxLjM1MyAxLjM1M2ExLjUgMS41IDAgMDEwIDIuMTIxbC0uNjguNjh6IiBzdHJva2U9IiMzMzMiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L3N2Zz4=)
+        2 5,
+      default !important;
   }
 }
 .rotate-wrapper {