mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
227 lines
7.5 KiB
Vue
227 lines
7.5 KiB
Vue
<template>
|
|
<div
|
|
class="canvas"
|
|
ref="canvasRef"
|
|
@mousedown="$event => handleClickBlankArea($event)"
|
|
v-contextmenu="contextmenus"
|
|
v-click-outside="removeEditorAreaFocus"
|
|
>
|
|
<div
|
|
class="viewport"
|
|
ref="viewportRef"
|
|
:style="{
|
|
width: viewportStyles.width + 'px',
|
|
height: viewportStyles.height + 'px',
|
|
left: viewportStyles.left + 'px',
|
|
top: viewportStyles.top + 'px',
|
|
transform: `scale(${canvasScale})`,
|
|
}"
|
|
>
|
|
<MouseSelection
|
|
v-if="mouseSelectionState.isShow"
|
|
:top="mouseSelectionState.top"
|
|
:left="mouseSelectionState.left"
|
|
:width="mouseSelectionState.width"
|
|
:height="mouseSelectionState.height"
|
|
:quadrant="mouseSelectionState.quadrant"
|
|
/>
|
|
|
|
<SlideBackground
|
|
:background="currentSlide?.background"
|
|
:isShowGridLines="isShowGridLines"
|
|
/>
|
|
|
|
<AlignmentLine
|
|
v-for="(line, index) in alignmentLines" :key="index"
|
|
:type="line.type" :axis="line.axis" :length="line.length"
|
|
/>
|
|
|
|
<MultiSelectOperate
|
|
v-if="activeElementIdList.length > 1"
|
|
:elementList="elementList"
|
|
:scaleMultiElement="scaleMultiElement"
|
|
:canvasScale="canvasScale"
|
|
/>
|
|
|
|
<EditableElement
|
|
v-for="(element, index) in elementList"
|
|
:key="element.elId"
|
|
:canvasScale="canvasScale"
|
|
:elementInfo="element"
|
|
:elementIndex="index + 1"
|
|
:isActive="activeElementIdList.includes(element.elId)"
|
|
:isHandleEl="element.elId === handleElementId"
|
|
:isActiveGroupElement="activeGroupElementId === element.elId"
|
|
:isMultiSelect="activeElementIdList.length > 1"
|
|
:selectElement="selectElement"
|
|
:rotateElement="rotateElement"
|
|
:scaleElement="scaleElement"
|
|
:orderElement="orderElement"
|
|
:combineElements="combineElements"
|
|
:uncombineElements="uncombineElements"
|
|
:alignElementToCanvas="alignElementToCanvas"
|
|
:deleteElement="deleteElement"
|
|
:lockElement="lockElement"
|
|
:unlockElement="unlockElement"
|
|
:copyElement="copyElement"
|
|
:cutElement="cutElement"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { computed, defineComponent, Ref, ref, watch, watchEffect } from 'vue'
|
|
import { useStore } from 'vuex'
|
|
import { State, MutationTypes } from '@/store'
|
|
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
|
import { PPTElement } from '@/types/slides'
|
|
import { AlignmentLineProps } from './types/index'
|
|
|
|
import useViewportSize from './hooks/useViewportSize'
|
|
import useLockElement from './hooks/useLockElement'
|
|
import useDeleteElement from './hooks/useDeleteElement'
|
|
import useCombineElement from './hooks/useCombineElement'
|
|
import useOrderElement from './hooks/useOrderElement'
|
|
import useAlignElementToCanvas from './hooks/useAlignElementToCanvas'
|
|
import useCopyAndPasteElement from './hooks/useCopyAndPasteElement'
|
|
import useRotateElement from './hooks/useRotateElement'
|
|
import useScaleElement from './hooks/useScaleElement'
|
|
import useSelectElement from './hooks/useSelectElement'
|
|
import useMoveElement from './hooks/useMoveElement'
|
|
import useMouseSelection from './hooks/useMouseSelection'
|
|
import useDropImageElement from './hooks/useDropImageElement'
|
|
|
|
import EditableElement from '@/views/_common/_element/EditableElement.vue'
|
|
import MouseSelection from './MouseSelection.vue'
|
|
import SlideBackground from './SlideBackground.vue'
|
|
import MultiSelectOperate from './MultiSelectOperate.vue'
|
|
import AlignmentLine from './AlignmentLine.vue'
|
|
|
|
export default defineComponent({
|
|
name: 'editor-canvas',
|
|
components: {
|
|
EditableElement,
|
|
MouseSelection,
|
|
SlideBackground,
|
|
MultiSelectOperate,
|
|
AlignmentLine,
|
|
},
|
|
setup() {
|
|
const store = useStore<State>()
|
|
|
|
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
|
const handleElementId = computed(() => store.state.handleElementId)
|
|
const editorAreaFocus = computed(() => store.state.editorAreaFocus)
|
|
const ctrlOrShiftKeyActive: Ref<boolean> = computed(() => store.getters.ctrlOrShiftKeyActive)
|
|
|
|
const viewportRef = ref<HTMLElement | null>(null)
|
|
const isShowGridLines = ref(false)
|
|
const alignmentLines = ref<AlignmentLineProps[]>([])
|
|
|
|
const activeGroupElementId = ref('')
|
|
watch(handleElementId, () => activeGroupElementId.value = '')
|
|
|
|
const currentSlide = computed(() => store.getters.currentSlide)
|
|
const elementList = ref<PPTElement[]>([])
|
|
const setLocalElementList = () => {
|
|
elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : []
|
|
}
|
|
watchEffect(setLocalElementList)
|
|
|
|
useDropImageElement(viewportRef)
|
|
|
|
const canvasRef = ref<HTMLElement | null>(null)
|
|
const { canvasScale, viewportStyles } = useViewportSize(canvasRef)
|
|
|
|
const { mouseSelectionState, updateMouseSelection } = useMouseSelection(elementList, viewportRef, canvasScale)
|
|
|
|
const { moveElement } = useMoveElement(elementList, activeGroupElementId, canvasScale, alignmentLines)
|
|
const { selectElement, selectAllElement } = useSelectElement(elementList, activeGroupElementId, moveElement)
|
|
const { scaleElement, scaleMultiElement } = useScaleElement(elementList, canvasScale, activeGroupElementId, alignmentLines)
|
|
const { rotateElement } = useRotateElement(elementList, viewportRef, canvasScale)
|
|
const { orderElement } = useOrderElement(elementList)
|
|
const { alignElementToCanvas } = useAlignElementToCanvas(elementList)
|
|
const { combineElements, uncombineElements } = useCombineElement(elementList)
|
|
const { deleteElement, deleteAllElements } = useDeleteElement(elementList)
|
|
const { lockElement, unlockElement } = useLockElement(elementList)
|
|
const { copyElement, cutElement, pasteElement } = useCopyAndPasteElement(deleteElement)
|
|
|
|
const handleClickBlankArea = (e: MouseEvent) => {
|
|
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])
|
|
if(!ctrlOrShiftKeyActive.value) updateMouseSelection(e)
|
|
if(!editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, true)
|
|
}
|
|
|
|
const removeEditorAreaFocus = () => {
|
|
if(editorAreaFocus.value) store.commit(MutationTypes.SET_EDITORAREA_FOCUS, false)
|
|
}
|
|
|
|
const contextmenus = (): ContextmenuItem[] => {
|
|
return [
|
|
{
|
|
text: '全选',
|
|
subText: 'Ctrl + A',
|
|
handler: selectAllElement,
|
|
},
|
|
{
|
|
text: '粘贴',
|
|
subText: 'Ctrl + V',
|
|
handler: pasteElement,
|
|
},
|
|
{
|
|
text: '清空本页',
|
|
handler: deleteAllElements,
|
|
},
|
|
]
|
|
}
|
|
|
|
return {
|
|
elementList,
|
|
activeElementIdList,
|
|
handleElementId,
|
|
activeGroupElementId,
|
|
canvasRef,
|
|
viewportRef,
|
|
viewportStyles,
|
|
canvasScale,
|
|
mouseSelectionState,
|
|
handleClickBlankArea,
|
|
removeEditorAreaFocus,
|
|
currentSlide,
|
|
isShowGridLines,
|
|
alignmentLines,
|
|
selectElement,
|
|
rotateElement,
|
|
scaleElement,
|
|
scaleMultiElement,
|
|
orderElement,
|
|
combineElements,
|
|
uncombineElements,
|
|
alignElementToCanvas,
|
|
deleteElement,
|
|
lockElement,
|
|
unlockElement,
|
|
copyElement,
|
|
cutElement,
|
|
contextmenus,
|
|
}
|
|
},
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.canvas {
|
|
height: 100%;
|
|
user-select: none;
|
|
overflow: hidden;
|
|
background-color: #f9f9f9;
|
|
position: relative;
|
|
}
|
|
.viewport {
|
|
position: absolute;
|
|
transform-origin: 0 0;
|
|
background-color: #fff;
|
|
box-shadow: 0 0 20px 0 rgba(0, 0, 0, .1);
|
|
}
|
|
</style> |