mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
147 lines
4.8 KiB
Vue
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> |