index.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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. />
  56. </div>
  57. </div>
  58. </template>
  59. <script lang="ts">
  60. import { computed, defineComponent, Ref, ref, watch, watchEffect } from 'vue'
  61. import { useStore } from 'vuex'
  62. import { State, MutationTypes } from '@/store'
  63. import { ContextmenuItem } from '@/components/Contextmenu/types'
  64. import { PPTElement, Slide } from '@/types/slides'
  65. import { AlignmentLineProps } from '@/types/edit'
  66. import useViewportSize from './hooks/useViewportSize'
  67. import useMouseSelection from './hooks/useMouseSelection'
  68. import useDropImageElement from './hooks/useDropImageElement'
  69. import useRotateElement from './hooks/useRotateElement'
  70. import useScaleElement from './hooks/useScaleElement'
  71. import useSelectElement from './hooks/useSelectElement'
  72. import useDragElement from './hooks/useDragElement'
  73. import useDeleteElement from '@/hooks/useDeleteElement'
  74. import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
  75. import useSelectAllElement from '@/hooks/useSelectAllElement'
  76. import EditableElement from '@/views/_common/_element/EditableElement.vue'
  77. import MouseSelection from './MouseSelection.vue'
  78. import SlideBackground from './SlideBackground.vue'
  79. import MultiSelectOperate from './MultiSelectOperate.vue'
  80. import AlignmentLine from './AlignmentLine.vue'
  81. export default defineComponent({
  82. name: 'editor-canvas',
  83. components: {
  84. EditableElement,
  85. MouseSelection,
  86. SlideBackground,
  87. MultiSelectOperate,
  88. AlignmentLine,
  89. },
  90. setup() {
  91. const store = useStore<State>()
  92. const activeElementIdList = computed(() => store.state.activeElementIdList)
  93. const handleElementId = computed(() => store.state.handleElementId)
  94. const editorAreaFocus = computed(() => store.state.editorAreaFocus)
  95. const ctrlOrShiftKeyActive: Ref<boolean> = computed(() => store.getters.ctrlOrShiftKeyActive)
  96. const viewportRef = ref<HTMLElement | null>(null)
  97. const isShowGridLines = ref(false)
  98. const alignmentLines = ref<AlignmentLineProps[]>([])
  99. const activeGroupElementId = ref('')
  100. watch(handleElementId, () => activeGroupElementId.value = '')
  101. const currentSlide: Ref<Slide> = computed(() => store.getters.currentSlide)
  102. const elementList = ref<PPTElement[]>([])
  103. const setLocalElementList = () => {
  104. elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : []
  105. }
  106. watchEffect(setLocalElementList)
  107. useDropImageElement(viewportRef)
  108. const canvasRef = ref<HTMLElement | null>(null)
  109. const { canvasScale, viewportStyles } = useViewportSize(canvasRef)
  110. const { mouseSelectionState, updateMouseSelection } = useMouseSelection(elementList, viewportRef, canvasScale)
  111. const { dragElement } = useDragElement(elementList, activeGroupElementId, canvasScale, alignmentLines)
  112. const { selectElement } = useSelectElement(elementList, activeGroupElementId, dragElement)
  113. const { scaleElement, scaleMultiElement } = useScaleElement(elementList, canvasScale, activeGroupElementId, alignmentLines)
  114. const { rotateElement } = useRotateElement(elementList, viewportRef, canvasScale)
  115. const { selectAllElement } = useSelectAllElement()
  116. const { deleteAllElements } = useDeleteElement()
  117. const { pasteElement } = useCopyAndPasteElement()
  118. const handleClickBlankArea = (e: MouseEvent) => {
  119. store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
  120. if(!ctrlOrShiftKeyActive.value) updateMouseSelection(e)
  121. if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
  122. }
  123. const removeEditorAreaFocus = () => {
  124. if(editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, false)
  125. }
  126. const contextmenus = (): ContextmenuItem[] => {
  127. return [
  128. {
  129. text: '全选',
  130. subText: 'Ctrl + A',
  131. handler: selectAllElement,
  132. },
  133. {
  134. text: '粘贴',
  135. subText: 'Ctrl + V',
  136. handler: pasteElement,
  137. },
  138. {
  139. text: '清空本页',
  140. handler: deleteAllElements,
  141. },
  142. ]
  143. }
  144. return {
  145. elementList,
  146. activeElementIdList,
  147. handleElementId,
  148. activeGroupElementId,
  149. canvasRef,
  150. viewportRef,
  151. viewportStyles,
  152. canvasScale,
  153. mouseSelectionState,
  154. handleClickBlankArea,
  155. removeEditorAreaFocus,
  156. currentSlide,
  157. isShowGridLines,
  158. alignmentLines,
  159. selectElement,
  160. rotateElement,
  161. scaleElement,
  162. scaleMultiElement,
  163. contextmenus,
  164. }
  165. },
  166. })
  167. </script>
  168. <style lang="scss" scoped>
  169. .canvas {
  170. height: 100%;
  171. user-select: none;
  172. overflow: hidden;
  173. background-color: #f9f9f9;
  174. position: relative;
  175. }
  176. .viewport {
  177. position: absolute;
  178. transform-origin: 0 0;
  179. background-color: #fff;
  180. box-shadow: 0 0 20px 0 rgba(0, 0, 0, .1);
  181. }
  182. </style>