mirror of https://gitee.com/godoos/godoos.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
8.6 KiB
261 lines
8.6 KiB
import { computed } from 'vue'
|
|
import { storeToRefs } from 'pinia'
|
|
import { useMainStore, useSlidesStore } from '@/store'
|
|
import type { PPTElement } from '@/types/slides'
|
|
import { getElementRange, getElementListRange, getRectRotatedOffset } from '@/utils/element'
|
|
import useHistorySnapshot from './useHistorySnapshot'
|
|
|
|
interface ElementItem {
|
|
min: number
|
|
max: number
|
|
el: PPTElement
|
|
}
|
|
|
|
interface GroupItem {
|
|
groupId: string
|
|
els: PPTElement[]
|
|
}
|
|
|
|
interface GroupElementsItem {
|
|
min: number
|
|
max: number
|
|
els: PPTElement[]
|
|
}
|
|
|
|
type Item = ElementItem | GroupElementsItem
|
|
|
|
interface ElementWithPos {
|
|
pos: number
|
|
el: PPTElement
|
|
}
|
|
|
|
interface LastPos {
|
|
min: number
|
|
max: number
|
|
}
|
|
|
|
export default () => {
|
|
const slidesStore = useSlidesStore()
|
|
const { activeElementIdList, activeElementList } = storeToRefs(useMainStore())
|
|
const { currentSlide } = storeToRefs(slidesStore)
|
|
|
|
const { addHistorySnapshot } = useHistorySnapshot()
|
|
|
|
const displayItemCount = computed(() => {
|
|
let count = 0
|
|
const groupIdList: string[] = []
|
|
for (const el of activeElementList.value) {
|
|
if (!el.groupId) count += 1
|
|
else if (!groupIdList.includes(el.groupId)) {
|
|
groupIdList.push(el.groupId)
|
|
count += 1
|
|
}
|
|
}
|
|
return count
|
|
})
|
|
// 水平均匀排列
|
|
const uniformHorizontalDisplay = () => {
|
|
const { minX, maxX } = getElementListRange(activeElementList.value)
|
|
const copyOfActiveElementList: PPTElement[] = JSON.parse(JSON.stringify(activeElementList.value))
|
|
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
|
|
|
// 分别获取普通元素和组合元素集合,并记录下每一项的范围
|
|
const singleElemetList: ElementItem[] = []
|
|
let groupList: GroupItem[] = []
|
|
for (const el of copyOfActiveElementList) {
|
|
if (!el.groupId) {
|
|
const { minX, maxX } = getElementRange(el)
|
|
singleElemetList.push({ min: minX, max: maxX, el })
|
|
}
|
|
else {
|
|
const groupEl = groupList.find(item => item.groupId === el.groupId)
|
|
if (!groupEl) groupList.push({ groupId: el.groupId, els: [el] })
|
|
else {
|
|
groupList = groupList.map(item => item.groupId === el.groupId ? { ...item, els: [...item.els, el] } : item)
|
|
}
|
|
}
|
|
}
|
|
const formatedGroupList: GroupElementsItem[] = []
|
|
for (const groupItem of groupList) {
|
|
const { minX, maxX } = getElementListRange(groupItem.els)
|
|
formatedGroupList.push({ min: minX, max: maxX, els: groupItem.els })
|
|
}
|
|
|
|
// 将普通元素和组合元素集合组合在一起,然后将每一项按位置(从左到右)排序
|
|
const list: Item[] = [...singleElemetList, ...formatedGroupList]
|
|
list.sort((itemA, itemB) => itemA.min - itemB.min)
|
|
|
|
// 计算元素均匀分布所需要的间隔:
|
|
// (所选元素整体范围 - 所有所选元素宽度和) / (所选元素数 - 1)
|
|
let totalWidth = 0
|
|
for (const item of list) {
|
|
const width = item.max - item.min
|
|
totalWidth += width
|
|
}
|
|
const span = ((maxX - minX) - totalWidth) / (list.length - 1)
|
|
|
|
// 按位置顺序依次计算每一个元素的目标位置
|
|
// 第一项中的元素即为起点,无需计算
|
|
// 从第二项开始,每一项的位置应该为:上一项位置 + 上一项宽度 + 间隔
|
|
// 注意此处计算的位置(pos)并非元素最终的left值,而是目标位置范围最小值(元素旋转后的left值 ≠ 范围最小值)
|
|
const sortedElementData: ElementWithPos[] = []
|
|
|
|
const firstItem = list[0]
|
|
let lastPos: LastPos = { min: firstItem.min, max: firstItem.max }
|
|
|
|
if ('el' in firstItem) {
|
|
sortedElementData.push({ pos: firstItem.min, el: firstItem.el })
|
|
}
|
|
else {
|
|
for (const el of firstItem.els) {
|
|
const { minX: pos } = getElementRange(el)
|
|
sortedElementData.push({ pos, el })
|
|
}
|
|
}
|
|
|
|
for (let i = 1; i < list.length; i++) {
|
|
const item = list[i]
|
|
const lastWidth = lastPos.max - lastPos.min
|
|
const currentPos = lastPos.min + lastWidth + span
|
|
const currentWidth = item.max - item.min
|
|
lastPos = { min: currentPos, max: currentPos + currentWidth }
|
|
|
|
if ('el' in item) {
|
|
sortedElementData.push({ pos: currentPos, el: item.el })
|
|
}
|
|
else {
|
|
for (const el of item.els) {
|
|
const { minX } = getElementRange(el)
|
|
const offset = minX - item.min
|
|
sortedElementData.push({ pos: currentPos + offset, el })
|
|
}
|
|
}
|
|
}
|
|
|
|
// 根据目标位置计算元素最终目标left值
|
|
// 对于旋转后的元素,需要计算旋转前后left的偏移来做校正
|
|
for (const element of newElementList) {
|
|
if (!activeElementIdList.value.includes(element.id)) continue
|
|
|
|
for (const sortedItem of sortedElementData) {
|
|
if (sortedItem.el.id === element.id) {
|
|
if ('rotate' in element && element.rotate) {
|
|
const { offsetX } = getRectRotatedOffset({
|
|
left: element.left,
|
|
top: element.top,
|
|
width: element.width,
|
|
height: element.height,
|
|
rotate: element.rotate,
|
|
})
|
|
element.left = sortedItem.pos - offsetX
|
|
}
|
|
else element.left = sortedItem.pos
|
|
}
|
|
}
|
|
}
|
|
|
|
slidesStore.updateSlide({ elements: newElementList })
|
|
addHistorySnapshot()
|
|
}
|
|
|
|
// 垂直均匀排列(逻辑类似水平均匀排列方法)
|
|
const uniformVerticalDisplay = () => {
|
|
const { minY, maxY } = getElementListRange(activeElementList.value)
|
|
const copyOfActiveElementList: PPTElement[] = JSON.parse(JSON.stringify(activeElementList.value))
|
|
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
|
|
|
const singleElemetList: ElementItem[] = []
|
|
let groupList: GroupItem[] = []
|
|
for (const el of copyOfActiveElementList) {
|
|
if (!el.groupId) {
|
|
const { minY, maxY } = getElementRange(el)
|
|
singleElemetList.push({ min: minY, max: maxY, el })
|
|
}
|
|
else {
|
|
const groupEl = groupList.find(item => item.groupId === el.groupId)
|
|
if (!groupEl) groupList.push({ groupId: el.groupId, els: [el] })
|
|
else {
|
|
groupList = groupList.map(item => item.groupId === el.groupId ? { ...item, els: [...item.els, el] } : item)
|
|
}
|
|
}
|
|
}
|
|
const formatedGroupList: GroupElementsItem[] = []
|
|
for (const groupItem of groupList) {
|
|
const { minY, maxY } = getElementListRange(groupItem.els)
|
|
formatedGroupList.push({ min: minY, max: maxY, els: groupItem.els })
|
|
}
|
|
|
|
const list: Item[] = [...singleElemetList, ...formatedGroupList]
|
|
list.sort((itemA, itemB) => itemA.min - itemB.min)
|
|
|
|
let totalHeight = 0
|
|
for (const item of list) {
|
|
const height = item.max - item.min
|
|
totalHeight += height
|
|
}
|
|
const span = ((maxY - minY) - totalHeight) / (list.length - 1)
|
|
|
|
const sortedElementData: ElementWithPos[] = []
|
|
|
|
const firstItem = list[0]
|
|
let lastPos: LastPos = { min: firstItem.min, max: firstItem.max }
|
|
|
|
if ('el' in firstItem) {
|
|
sortedElementData.push({ pos: firstItem.min, el: firstItem.el })
|
|
}
|
|
else {
|
|
for (const el of firstItem.els) {
|
|
const { minY: pos } = getElementRange(el)
|
|
sortedElementData.push({ pos, el })
|
|
}
|
|
}
|
|
|
|
for (let i = 1; i < list.length; i++) {
|
|
const item = list[i]
|
|
const lastHeight = lastPos.max - lastPos.min
|
|
const currentPos = lastPos.min + lastHeight + span
|
|
const currentHeight = item.max - item.min
|
|
lastPos = { min: currentPos, max: currentPos + currentHeight }
|
|
|
|
if ('el' in item) {
|
|
sortedElementData.push({ pos: currentPos, el: item.el })
|
|
}
|
|
else {
|
|
for (const el of item.els) {
|
|
const { minY } = getElementRange(el)
|
|
const offset = minY - item.min
|
|
sortedElementData.push({ pos: currentPos + offset, el })
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const element of newElementList) {
|
|
if (!activeElementIdList.value.includes(element.id)) continue
|
|
|
|
for (const sortedItem of sortedElementData) {
|
|
if (sortedItem.el.id === element.id) {
|
|
if ('rotate' in element && element.rotate) {
|
|
const { offsetY } = getRectRotatedOffset({
|
|
left: element.left,
|
|
top: element.top,
|
|
width: element.width,
|
|
height: element.height,
|
|
rotate: element.rotate,
|
|
})
|
|
element.top = sortedItem.pos - offsetY
|
|
}
|
|
else element.top = sortedItem.pos
|
|
}
|
|
}
|
|
}
|
|
|
|
slidesStore.updateSlide({ elements: newElementList })
|
|
addHistorySnapshot()
|
|
}
|
|
|
|
return {
|
|
displayItemCount,
|
|
uniformHorizontalDisplay,
|
|
uniformVerticalDisplay,
|
|
}
|
|
}
|