BaseShapeElement.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <template>
  2. <div
  3. class="base-element-shape"
  4. :style="{
  5. top: elementInfo.top + 'px',
  6. left: elementInfo.left + 'px',
  7. width: elementInfo.width + 'px',
  8. height: elementInfo.height + 'px',
  9. }"
  10. >
  11. <div
  12. class="rotate-wrapper"
  13. :style="{ transform: `rotate(${elementInfo.rotate}deg)` }"
  14. >
  15. <div
  16. class="element-content"
  17. :style="{
  18. opacity: elementInfo.opacity,
  19. filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
  20. transform: flipStyle,
  21. color: text.defaultColor,
  22. fontFamily: text.defaultFontName,
  23. }"
  24. >
  25. <svg
  26. overflow="visible"
  27. :width="elementInfo.width"
  28. :height="elementInfo.height"
  29. >
  30. <defs>
  31. <PatternDefs
  32. v-if="elementInfo.pattern"
  33. :id="`base-pattern-${elementInfo.id}`"
  34. :src="elementInfo.pattern"
  35. />
  36. <GradientDefs
  37. v-else-if="elementInfo.gradient"
  38. :id="`base-gradient-${elementInfo.id}`"
  39. :type="elementInfo.gradient.type"
  40. :colors="elementInfo.gradient.colors"
  41. :rotate="elementInfo.gradient.rotate"
  42. />
  43. </defs>
  44. <g
  45. :transform="`scale(${elementInfo.width / elementInfo.viewBox[0]}, ${elementInfo.height / elementInfo.viewBox[1]}) translate(0,0) matrix(1,0,0,1,0,0)`"
  46. >
  47. <path
  48. vector-effect="non-scaling-stroke"
  49. stroke-linecap="butt"
  50. stroke-miterlimit="8"
  51. :d="elementInfo.path"
  52. :fill="fill"
  53. :stroke="outlineColor"
  54. :stroke-width="outlineWidth"
  55. :stroke-dasharray="strokeDashArray"
  56. ></path>
  57. </g>
  58. </svg>
  59. <div class="shape-text" :style="text.style" :class="[text.align, { 'editable': editable || text.content }]">
  60. <div class="ProseMirror-static" v-html="text.content"></div>
  61. </div>
  62. </div>
  63. </div>
  64. </div>
  65. </template>
  66. <script lang="ts" setup>
  67. import { computed } from 'vue'
  68. import { storeToRefs } from 'pinia'
  69. import type { PPTShapeElement, ShapeText } from '@/types/slides'
  70. import { useSlidesStore } from '@/store'
  71. import useElementOutline from '@/views/components/element/hooks/useElementOutline'
  72. import useElementShadow from '@/views/components/element/hooks/useElementShadow'
  73. import useElementFlip from '@/views/components/element/hooks/useElementFlip'
  74. import useElementFill from '@/views/components/element/hooks/useElementFill'
  75. import GradientDefs from './GradientDefs.vue'
  76. import PatternDefs from './PatternDefs.vue'
  77. const props = defineProps<{
  78. elementInfo: PPTShapeElement
  79. }>()
  80. const { theme } = storeToRefs(useSlidesStore())
  81. const element = computed(() => props.elementInfo)
  82. const { fill } = useElementFill(element, 'base')
  83. const outline = computed(() => props.elementInfo.outline)
  84. const { outlineWidth, outlineColor, strokeDashArray } = useElementOutline(outline)
  85. const shadow = computed(() => props.elementInfo.shadow)
  86. const { shadowStyle } = useElementShadow(shadow)
  87. const flipH = computed(() => props.elementInfo.flipH)
  88. const flipV = computed(() => props.elementInfo.flipV)
  89. const { flipStyle } = useElementFlip(flipH, flipV)
  90. const text = computed<ShapeText>(() => {
  91. const defaultText: ShapeText = {
  92. content: '',
  93. align: 'middle',
  94. defaultFontName: theme.value.fontName,
  95. defaultColor: theme.value.fontColor,
  96. }
  97. if (!props.elementInfo.text) return defaultText
  98. return props.elementInfo.text
  99. })
  100. </script>
  101. <style lang="scss" scoped>
  102. .base-element-shape {
  103. position: absolute;
  104. }
  105. .rotate-wrapper {
  106. width: 100%;
  107. height: 100%;
  108. }
  109. .element-content {
  110. width: 100%;
  111. height: 100%;
  112. position: relative;
  113. line-height: 1.5;
  114. svg {
  115. transform-origin: 0 0;
  116. overflow: visible;
  117. display: block;
  118. }
  119. }
  120. .shape-text {
  121. width:100%;
  122. height:100%;
  123. position: absolute;
  124. top: 0;
  125. bottom: 0;
  126. left: 0;
  127. right: 0;
  128. display: flex;
  129. flex-direction: column;
  130. padding: 5px;
  131. word-break: break-word;
  132. pointer-events: none;
  133. white-space: break-spaces;
  134. &.editable {
  135. pointer-events: all;
  136. }
  137. &.top {
  138. justify-content: flex-start;
  139. }
  140. &.middle {
  141. justify-content: center;
  142. left: 50%;
  143. top: 50%;
  144. -webkit-transform: translate(-50%,-50%);
  145. transform: translate(-50%,-50%);
  146. }
  147. &.bottom {
  148. justify-content: flex-end;
  149. }
  150. }
  151. </style>