mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 多元素自动水平/垂直均匀分布
This commit is contained in:
parent
7c7ec3d52d
commit
5be272f77e
158
src/hooks/useUniformDisplayElement.ts
Normal file
158
src/hooks/useUniformDisplayElement.ts
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import { computed } from 'vue'
|
||||||
|
import { MutationTypes, useStore } from '@/store'
|
||||||
|
import { PPTElement, Slide } from '@/types/slides'
|
||||||
|
import { getElementRange, getElementListRange, getRectRotatedRange } from '@/utils/element'
|
||||||
|
import useHistorySnapshot from './useHistorySnapshot'
|
||||||
|
|
||||||
|
interface SortedElementData {
|
||||||
|
el: PPTElement;
|
||||||
|
pos: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const store = useStore()
|
||||||
|
const activeElementIdList = computed(() => store.state.activeElementIdList)
|
||||||
|
const activeElementList = computed<PPTElement[]>(() => store.getters.activeElementList)
|
||||||
|
const currentSlide = computed<Slide>(() => store.getters.currentSlide)
|
||||||
|
|
||||||
|
const { addHistorySnapshot } = useHistorySnapshot()
|
||||||
|
|
||||||
|
// 水平均匀排列
|
||||||
|
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
|
||||||
|
})
|
||||||
|
|
||||||
|
let totaiWidth = 0
|
||||||
|
for (const element of activeElementList.value) {
|
||||||
|
const { minX: elMinX, maxX: elMaxX } = getElementRange(element)
|
||||||
|
totaiWidth += (elMaxX - elMinX)
|
||||||
|
}
|
||||||
|
const span = ((maxX - minX) - totaiWidth) / (activeElementList.value.length - 1)
|
||||||
|
|
||||||
|
const sortedElementData: SortedElementData[] = []
|
||||||
|
for (const element of copyOfActiveElementList) {
|
||||||
|
if (!sortedElementData.length) {
|
||||||
|
const { minX: firstElMinX } = getElementRange(element)
|
||||||
|
sortedElementData.push({ el: element, pos: firstElMinX })
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
|
||||||
|
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 { xRange: originXRange } = getRectRotatedRange({
|
||||||
|
left: element.left,
|
||||||
|
top: element.top,
|
||||||
|
width: element.width,
|
||||||
|
height: element.height,
|
||||||
|
rotate: 0,
|
||||||
|
})
|
||||||
|
const { xRange: rotatedXRange } = getRectRotatedRange({
|
||||||
|
left: element.left,
|
||||||
|
top: element.top,
|
||||||
|
width: element.width,
|
||||||
|
height: element.height,
|
||||||
|
rotate: element.rotate,
|
||||||
|
})
|
||||||
|
const offsetX = rotatedXRange[0] - originXRange[0]
|
||||||
|
element.left = sortedItem.pos - offsetX
|
||||||
|
}
|
||||||
|
else element.left = sortedItem.pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store.commit(MutationTypes.UPDATE_SLIDE, { 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))
|
||||||
|
|
||||||
|
copyOfActiveElementList.sort((elementA, elementB) => {
|
||||||
|
const { minY: elAMinY } = getElementRange(elementA)
|
||||||
|
const { minY: elBMinY } = getElementRange(elementB)
|
||||||
|
return elAMinY - elBMinY
|
||||||
|
})
|
||||||
|
|
||||||
|
let totaiHeight = 0
|
||||||
|
for (const element of activeElementList.value) {
|
||||||
|
const { minY: elMinY, maxY: elMaxY } = getElementRange(element)
|
||||||
|
totaiHeight += (elMaxY - elMinY)
|
||||||
|
}
|
||||||
|
const span = ((maxY - minY) - totaiHeight) / (activeElementList.value.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 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) {
|
||||||
|
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 { yRange: originYRange } = getRectRotatedRange({
|
||||||
|
left: element.left,
|
||||||
|
top: element.top,
|
||||||
|
width: element.width,
|
||||||
|
height: element.height,
|
||||||
|
rotate: 0,
|
||||||
|
})
|
||||||
|
const { yRange: rotatedYRange } = getRectRotatedRange({
|
||||||
|
left: element.left,
|
||||||
|
top: element.top,
|
||||||
|
width: element.width,
|
||||||
|
height: element.height,
|
||||||
|
rotate: element.rotate,
|
||||||
|
})
|
||||||
|
const offsetY = rotatedYRange[0] - originYRange[0]
|
||||||
|
element.top = sortedItem.pos - offsetY
|
||||||
|
}
|
||||||
|
else element.top = sortedItem.pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
|
||||||
|
addHistorySnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
uniformHorizontalDisplay,
|
||||||
|
uniformVerticalDisplay,
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,10 @@
|
|||||||
<Button style="flex: 1;" @click="alignActiveElement('bottom')"><IconAlignBottom /></Button>
|
<Button style="flex: 1;" @click="alignActiveElement('bottom')"><IconAlignBottom /></Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
<ButtonGroup class="row" v-if="activeElementList.length > 2">
|
||||||
|
<Button style="flex: 1;" @click="uniformHorizontalDisplay()">水平均匀分布</Button>
|
||||||
|
<Button style="flex: 1;" @click="uniformVerticalDisplay()">垂直均匀分布</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
@ -38,6 +42,7 @@ import { useStore } from '@/store'
|
|||||||
import { PPTElement } from '@/types/slides'
|
import { PPTElement } from '@/types/slides'
|
||||||
import useCombineElement from '@/hooks/useCombineElement'
|
import useCombineElement from '@/hooks/useCombineElement'
|
||||||
import useAlignActiveElement from '@/hooks/useAlignActiveElement'
|
import useAlignActiveElement from '@/hooks/useAlignActiveElement'
|
||||||
|
import useUniformDisplayElement from '@/hooks/useUniformDisplayElement'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'multi-position-panel',
|
name: 'multi-position-panel',
|
||||||
@ -47,6 +52,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const { combineElements, uncombineElements } = useCombineElement()
|
const { combineElements, uncombineElements } = useCombineElement()
|
||||||
const { alignActiveElement } = useAlignActiveElement()
|
const { alignActiveElement } = useAlignActiveElement()
|
||||||
|
const { uniformHorizontalDisplay, uniformVerticalDisplay } = useUniformDisplayElement()
|
||||||
|
|
||||||
// 判断当前多选的几个元素是否可以组合
|
// 判断当前多选的几个元素是否可以组合
|
||||||
const canCombine = computed(() => {
|
const canCombine = computed(() => {
|
||||||
@ -58,10 +64,13 @@ export default defineComponent({
|
|||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
activeElementList,
|
||||||
canCombine,
|
canCombine,
|
||||||
combineElements,
|
combineElements,
|
||||||
uncombineElements,
|
uncombineElements,
|
||||||
alignActiveElement,
|
alignActiveElement,
|
||||||
|
uniformHorizontalDisplay,
|
||||||
|
uniformVerticalDisplay,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user