2024-09-07 11:00:18 +08:00

147 lines
4.8 KiB
Vue

<template>
<div class="mobile-editor">
<Header :changeMode="changeMode" />
<div class="content" ref="contentRef" @touchstart="handleClickBlankArea()">
<div class="viewport-wrapper" :style="viewportStyles">
<div class="background" :style="backgroundStyle"></div>
<AlignmentLine
v-for="(line, index) in alignmentLines"
:key="index"
:type="line.type"
:axis="line.axis"
:length="line.length"
:canvasScale="canvasScale"
/>
<template v-for="element in elementList" :key="element.id">
<MobileOperate
v-if="element.type !== 'line'"
:elementInfo="element"
:isSelected="activeElementIdList.includes(element.id)"
:canvasScale="canvasScale"
:scaleElement="scaleElement"
:rotateElement="rotateElement"
/>
</template>
<div class="viewport" ref="viewportRef" :style="{ transform: `scale(${canvasScale})` }">
<MobileEditableElement
v-for="(element, index) in elementList"
:key="element.id"
:elementInfo="element"
:elementIndex="index + 1"
:selectElement="selectElement"
/>
</div>
</div>
</div>
<SlideToolbar />
<ElementToolbar v-if="handleElement" />
</div>
</template>
<script lang="ts" setup>
import { computed, onMounted, ref, watchEffect } from 'vue'
import { storeToRefs } from 'pinia'
import { useMainStore, useSlidesStore } from '@/store'
import type { PPTElement } from '@/types/slides'
import type { AlignmentLineProps } from '@/types/edit'
import type { Mode } from '@/types/mobile'
import useSlideBackgroundStyle from '@/hooks/useSlideBackgroundStyle'
import useDragElement from '@/views/Editor/Canvas/hooks/useDragElement'
import useScaleElement from '@/views/Editor/Canvas/hooks/useScaleElement'
import useRotateElement from '@/views/Editor/Canvas/hooks/useRotateElement'
import AlignmentLine from '@/views/Editor/Canvas/AlignmentLine.vue'
import MobileEditableElement from './MobileEditableElement.vue'
import MobileOperate from './MobileOperate.vue'
import SlideToolbar from './SlideToolbar.vue'
import ElementToolbar from './ElementToolbar.vue'
import Header from './Header.vue'
defineProps<{
changeMode: (mode: Mode) => void
}>()
const slidesStore = useSlidesStore()
const mainStore = useMainStore()
const { slideIndex, currentSlide, viewportRatio, viewportSize } = storeToRefs(slidesStore)
const { activeElementIdList, handleElement } = storeToRefs(mainStore)
const contentRef = ref<HTMLElement>()
const viewportRef = ref<HTMLElement>()
const alignmentLines = ref<AlignmentLineProps[]>([])
const background = computed(() => currentSlide.value.background)
const { backgroundStyle } = useSlideBackgroundStyle(background)
const canvasScale = computed(() => {
if (!contentRef.value) return 1
const contentWidth = contentRef.value.clientWidth
const contentheight = contentRef.value.clientHeight
const contentRatio = contentheight / contentWidth
if (contentRatio >= viewportRatio.value) return (contentWidth - 20) / viewportSize.value
return (contentheight - 20) / viewportRatio.value / viewportSize.value
})
onMounted(() => {
if (activeElementIdList.value.length) mainStore.setActiveElementIdList([])
if (slideIndex.value !== 0) slidesStore.updateSlideIndex(0)
})
const viewportStyles = computed(() => ({
width: viewportSize.value * canvasScale.value + 'px',
height: viewportSize.value * viewportRatio.value * canvasScale.value + 'px',
}))
const elementList = ref<PPTElement[]>([])
const setLocalElementList = () => {
elementList.value = currentSlide.value ? JSON.parse(JSON.stringify(currentSlide.value.elements)) : []
}
watchEffect(setLocalElementList)
const { dragElement } = useDragElement(elementList, alignmentLines, canvasScale)
const { scaleElement } = useScaleElement(elementList, alignmentLines, canvasScale)
const { rotateElement } = useRotateElement(elementList, viewportRef, canvasScale)
const selectElement = (e: TouchEvent, element: PPTElement, startMove = true) => {
if (!activeElementIdList.value.includes(element.id)) {
mainStore.setActiveElementIdList([element.id])
mainStore.setHandleElementId(element.id)
}
if (startMove) dragElement(e, element)
}
const handleClickBlankArea = () => {
mainStore.setActiveElementIdList([])
}
</script>
<style lang="scss" scoped>
.mobile-editor {
height: 100%;
position: relative;
background-color: $lightGray;
}
.content {
height: calc(100% - 280px);
display: flex;
justify-content: center;
align-items: center;
}
.viewport {
transform-origin: 0 0;
}
.viewport-wrapper {
position: relative;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.01), 0 0 12px 0 rgba(0, 0, 0, 0.1);
}
.background {
width: 100%;
height: 100%;
background-position: center;
position: absolute;
}
</style>