mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
fix: 组合元素执行均匀分布异常(#49)
This commit is contained in:
parent
9ac7757647
commit
bdd58ccc5b
@ -4,9 +4,33 @@ import { PPTElement, Slide } from '@/types/slides'
|
||||
import { getElementRange, getElementListRange, getRectRotatedOffset } from '@/utils/element'
|
||||
import useHistorySnapshot from './useHistorySnapshot'
|
||||
|
||||
interface SortedElementData {
|
||||
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 () => {
|
||||
@ -17,44 +41,95 @@ export default () => {
|
||||
|
||||
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))
|
||||
|
||||
// 将选中的元素按位置(从左到右)排序
|
||||
copyOfActiveElementList.sort((elementA, elementB) => {
|
||||
const { minX: elAMinX } = getElementRange(elementA)
|
||||
const { minX: elBMinX } = getElementRange(elementB)
|
||||
return elAMinX - elBMinX
|
||||
})
|
||||
// 分别获取普通元素和组合元素集合,并记录下每一项的范围
|
||||
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 element of activeElementList.value) {
|
||||
const { minX: elMinX, maxX: elMaxX } = getElementRange(element)
|
||||
totalWidth += (elMaxX - elMinX)
|
||||
for (const item of list) {
|
||||
const width = item.max - item.min
|
||||
totalWidth += width
|
||||
}
|
||||
const span = ((maxX - minX) - totalWidth) / (activeElementList.value.length - 1)
|
||||
const span = ((maxX - minX) - totalWidth) / (list.length - 1)
|
||||
|
||||
// 将所选元素按位置顺序依次计算目标位置
|
||||
// 注意pos并非元素目标left值,而是目标位置范围最小值(元素旋转后的left值 ≠ 范围最小值)
|
||||
const sortedElementData: SortedElementData[] = []
|
||||
for (const element of copyOfActiveElementList) {
|
||||
if (!sortedElementData.length) {
|
||||
const { minX: firstElMinX } = getElementRange(element)
|
||||
sortedElementData.push({ el: element, pos: firstElMinX })
|
||||
continue
|
||||
// 按位置顺序依次计算每一个元素的目标位置
|
||||
// 第一项中的元素即为起点,无序计算
|
||||
// 从第二项开始,每一项的位置应该为:上一项位置 + 上一项宽度 + 间隔
|
||||
// 注意此处计算的位置(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 })
|
||||
}
|
||||
}
|
||||
|
||||
const lastItemElement = sortedElementData[sortedElementData.length - 1].el
|
||||
const lastItemPos = sortedElementData[sortedElementData.length - 1].pos
|
||||
const { minX: lastElementMinX, maxX: lastElementMaxX } = getElementRange(lastItemElement)
|
||||
const lastElementWidth = lastElementMaxX - lastElementMinX
|
||||
|
||||
sortedElementData.push({ el: element, pos: lastItemPos + lastElementWidth + span })
|
||||
}
|
||||
|
||||
// 根据目标位置计算元素最终目标left值
|
||||
@ -89,33 +164,69 @@ export default () => {
|
||||
const copyOfActiveElementList: PPTElement[] = JSON.parse(JSON.stringify(activeElementList.value))
|
||||
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
|
||||
|
||||
copyOfActiveElementList.sort((elementA, elementB) => {
|
||||
const { minY: elAMinY } = getElementRange(elementA)
|
||||
const { minY: elBMinY } = getElementRange(elementB)
|
||||
return elAMinY - elBMinY
|
||||
})
|
||||
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 element of activeElementList.value) {
|
||||
const { minY: elMinY, maxY: elMaxY } = getElementRange(element)
|
||||
totalHeight += (elMaxY - elMinY)
|
||||
for (const item of list) {
|
||||
const height = item.max - item.min
|
||||
totalHeight += height
|
||||
}
|
||||
const span = ((maxY - minY) - totalHeight) / (activeElementList.value.length - 1)
|
||||
const span = ((maxY - minY) - totalHeight) / (list.length - 1)
|
||||
|
||||
const sortedElementData: SortedElementData[] = []
|
||||
for (const element of copyOfActiveElementList) {
|
||||
if (!sortedElementData.length) {
|
||||
const { minY: firstElMinY } = getElementRange(element)
|
||||
sortedElementData.push({ el: element, pos: firstElMinY })
|
||||
continue
|
||||
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 })
|
||||
}
|
||||
}
|
||||
|
||||
const lastItemElement = sortedElementData[sortedElementData.length - 1].el
|
||||
const lastItemPos = sortedElementData[sortedElementData.length - 1].pos
|
||||
const { minY: lastElementMinY, maxY: lastElementMaxY } = getElementRange(lastItemElement)
|
||||
const lastElementHeight = lastElementMaxY - lastElementMinY
|
||||
|
||||
sortedElementData.push({ el: element, pos: lastItemPos + lastElementHeight + span })
|
||||
}
|
||||
|
||||
for (const element of newElementList) {
|
||||
@ -143,6 +254,7 @@ export default () => {
|
||||
}
|
||||
|
||||
return {
|
||||
displayItemCount,
|
||||
uniformHorizontalDisplay,
|
||||
uniformVerticalDisplay,
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
<Button style="flex: 1;" @click="alignElement('bottom')"><IconAlignBottom /></Button>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
<ButtonGroup class="row" v-if="activeElementList.length > 2">
|
||||
<ButtonGroup class="row" v-if="displayItemCount > 2">
|
||||
<Button style="flex: 1;" @click="uniformHorizontalDisplay()">水平均匀分布</Button>
|
||||
<Button style="flex: 1;" @click="uniformVerticalDisplay()">垂直均匀分布</Button>
|
||||
</ButtonGroup>
|
||||
@ -55,7 +55,7 @@ export default defineComponent({
|
||||
const { combineElements, uncombineElements } = useCombineElement()
|
||||
const { alignActiveElement } = useAlignActiveElement()
|
||||
const { alignElementToCanvas } = useAlignElementToCanvas()
|
||||
const { uniformHorizontalDisplay, uniformVerticalDisplay } = useUniformDisplayElement()
|
||||
const { displayItemCount, uniformHorizontalDisplay, uniformVerticalDisplay } = useUniformDisplayElement()
|
||||
|
||||
// 判断当前多选的几个元素是否可以组合
|
||||
const canCombine = computed(() => {
|
||||
@ -75,7 +75,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
return {
|
||||
activeElementList,
|
||||
displayItemCount,
|
||||
canCombine,
|
||||
combineElements,
|
||||
uncombineElements,
|
||||
|
Loading…
x
Reference in New Issue
Block a user