index.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <template>
  2. <div
  3. class="canvas"
  4. ref="canvasRef"
  5. @mousedown="$event => handleClickBlankArea($event)"
  6. v-contextmenu="contextmenus"
  7. v-click-outside="removeEditorAreaFocus"
  8. >
  9. <div
  10. class="viewport"
  11. ref="viewportRef"
  12. :style="{
  13. width: viewportStyles.width + 'px',
  14. height: viewportStyles.height + 'px',
  15. left: viewportStyles.left + 'px',
  16. top: viewportStyles.top + 'px',
  17. transform: `scale(${canvasScale})`,
  18. }"
  19. >
  20. <MouseSelection
  21. v-if="mouseSelectionState.isShow"
  22. :top="mouseSelectionState.top"
  23. :left="mouseSelectionState.left"
  24. :width="mouseSelectionState.width"
  25. :height="mouseSelectionState.height"
  26. :quadrant="mouseSelectionState.quadrant"
  27. />
  28. <SlideBackground
  29. :background="currentSlide?.background"
  30. :isShowGridLines="isShowGridLines"
  31. />
  32. <AlignmentLine
  33. v-for="(line, index) in alignmentLines" :key="index"
  34. :type="line.type" :axis="line.axis" :length="line.length"
  35. />
  36. <MultiSelectOperate
  37. v-if="activeElementIdList.length > 1"
  38. :elementList="elementList"
  39. :scaleMultiElement="scaleMultiElement"
  40. :canvasScale="canvasScale"
  41. />
  42. <EditableElement
  43. v-for="(element, index) in elementList"
  44. :key="element.elId"
  45. :canvasScale="canvasScale"
  46. :elementInfo="element"
  47. :elementIndex="index + 1"
  48. :isActive="activeElementIdList.includes(element.elId)"
  49. :isHandleEl="element.elId === handleElementId"
  50. :isActiveGroupElement="activeGroupElementId === element.elId"
  51. :isMultiSelect="activeElementIdList.length > 1"
  52. :selectElement="selectElement"
  53. :rotateElement="rotateElement"
  54. :scaleElement="scaleElement"
  55. :orderElement="orderElement"
  56. :combineElements="combineElements"
  57. :uncombineElements="uncombineElements"
  58. :alignElementToCanvas="alignElementToCanvas"
  59. :deleteElement="deleteElement"
  60. :lockElement="lockElement"
  61. :unlockElement="unlockElement"
  62. :copyElement="copyElement"
  63. :cutElement="cutElement"
  64. />
  65. </div>
  66. </div>
  67. </template>
  68. <script lang="ts">
  69. import { computed, defineComponent, Ref, ref, watch, watchEffect } from 'vue'
  70. import { useStore } from 'vuex'
  71. import { State, MutationTypes } from '@/store'
  72. import { ContextmenuItem } from '@/components/Contextmenu/types'
  73. import { PPTElement } from '@/types/slides'
  74. import { AlignmentLineProps } from './types/index'
  75. import useViewportSize from './hooks/useViewportSize'
  76. import useLockElement from './hooks/useLockElement'
  77. import useDeleteElement from './hooks/useDeleteElement'
  78. import useCombineElement from './hooks/useCombineElement'
  79. import useOrderElement from './hooks/useOrderElement'
  80. import useAlignElementToCanvas from './hooks/useAlignElementToCanvas'
  81. import useCopyAndPasteElement from './hooks/useCopyAndPasteElement'
  82. import useRotateElement from './hooks/useRotateElement'
  83. import useScaleElement from './hooks/useScaleElement'
  84. import useSelectElement from './hooks/useSelectElement'
  85. import useMoveElement from './hooks/useMoveElement'
  86. import useMouseSelection from './hooks/useMouseSelection'
  87. import useDropImageElement from './hooks/useDropImageElement'
  88. import EditableElement from '@/views/_common/_element/EditableElement.vue'
  89. import MouseSelection from './MouseSelection.vue'
  90. import SlideBackground from './SlideBackground.vue'
  91. import MultiSelectOperate from './MultiSelectOperate.vue'
  92. import AlignmentLine from './AlignmentLine.vue'
  93. export default defineComponent({
  94. name: 'editor-canvas',
  95. components: {
  96. EditableElement,
  97. MouseSelection,
  98. SlideBackground,
  99. MultiSelectOperate,
  100. AlignmentLine,
  101. },
  102. setup() {
  103. const store = useStore<State>()
  104. const activeElementIdList = computed(() => store.state.activeElementIdList)
  105. const handleElementId = computed(() => store.state.handleElementId)
  106. const editorAreaFocus = computed(() => store.state.editorAreaFocus)
  107. const ctrlOrShiftKeyActive: Ref<boolean> = computed(() => store.getters.ctrlOrShiftKeyActive)
  108. const viewportRef = ref<HTMLElement | null>(null)
  109. const isShowGridLines = ref(false)
  110. const alignmentLines = ref<AlignmentLineProps[]>([])
  111. const activeGroupElementId = ref('')
  112. watch(handleElementId, () => activeGroupElementId.value = '')
  113. const currentSlide = computed(() => store.getters.currentSlide)
  114. const elementList = ref<PPTElement[]>([])
  115. const setLocalElementList = () => {
  116. elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : []
  117. }
  118. watchEffect(setLocalElementList)
  119. useDropImageElement(viewportRef)
  120. const canvasRef = ref<HTMLElement | null>(null)
  121. const { canvasScale, viewportStyles } = useViewportSize(canvasRef)
  122. const { mouseSelectionState, updateMouseSelection } = useMouseSelection(elementList, viewportRef, canvasScale)
  123. const { moveElement } = useMoveElement(elementList, activeGroupElementId, canvasScale, alignmentLines)
  124. const { selectElement, selectAllElement } = useSelectElement(elementList, activeGroupElementId, moveElement)
  125. const { scaleElement, scaleMultiElement } = useScaleElement(elementList, canvasScale, activeGroupElementId, alignmentLines)
  126. const { rotateElement } = useRotateElement(elementList, viewportRef, canvasScale)
  127. const { orderElement } = useOrderElement(elementList)
  128. const { alignElementToCanvas } = useAlignElementToCanvas(elementList)
  129. const { combineElements, uncombineElements } = useCombineElement(elementList)
  130. const { deleteElement, deleteAllElements } = useDeleteElement(elementList)
  131. const { lockElement, unlockElement } = useLockElement(elementList)
  132. const { copyElement, cutElement, pasteElement } = useCopyAndPasteElement(deleteElement)
  133. const handleClickBlankArea = (e: MouseEvent) => {
  134. store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
  135. if(!ctrlOrShiftKeyActive.value) updateMouseSelection(e)
  136. if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
  137. }
  138. const removeEditorAreaFocus = () => {
  139. if(editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, false)
  140. }
  141. const contextmenus = (): ContextmenuItem[] => {
  142. return [
  143. {
  144. text: '全选',
  145. subText: 'Ctrl + A',
  146. handler: selectAllElement,
  147. },
  148. {
  149. text: '粘贴',
  150. subText: 'Ctrl + V',
  151. handler: pasteElement,
  152. },
  153. {
  154. text: '清空本页',
  155. handler: deleteAllElements,
  156. },
  157. ]
  158. }
  159. return {
  160. elementList,
  161. activeElementIdList,
  162. handleElementId,
  163. activeGroupElementId,
  164. canvasRef,
  165. viewportRef,
  166. viewportStyles,
  167. canvasScale,
  168. mouseSelectionState,
  169. handleClickBlankArea,
  170. removeEditorAreaFocus,
  171. currentSlide,
  172. isShowGridLines,
  173. alignmentLines,
  174. selectElement,
  175. rotateElement,
  176. scaleElement,
  177. scaleMultiElement,
  178. orderElement,
  179. combineElements,
  180. uncombineElements,
  181. alignElementToCanvas,
  182. deleteElement,
  183. lockElement,
  184. unlockElement,
  185. copyElement,
  186. cutElement,
  187. contextmenus,
  188. }
  189. },
  190. })
  191. </script>
  192. <style lang="scss" scoped>
  193. .canvas {
  194. height: 100%;
  195. user-select: none;
  196. overflow: hidden;
  197. background-color: #f9f9f9;
  198. position: relative;
  199. }
  200. .viewport {
  201. position: absolute;
  202. transform-origin: 0 0;
  203. background-color: #fff;
  204. box-shadow: 0 0 20px 0 rgba(0, 0, 0, .1);
  205. }
  206. </style>