feat: 多元素自动水平/垂直均匀分布

This commit is contained in:
pipipi-pikachu 2021-04-13 17:25:33 +08:00
parent 7c7ec3d52d
commit 5be272f77e
2 changed files with 167 additions and 0 deletions

View 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,
}
}

View File

@ -22,6 +22,10 @@
<Button style="flex: 1;" @click="alignActiveElement('bottom')"><IconAlignBottom /></Button>
</Tooltip>
</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 />
@ -38,6 +42,7 @@ import { useStore } from '@/store'
import { PPTElement } from '@/types/slides'
import useCombineElement from '@/hooks/useCombineElement'
import useAlignActiveElement from '@/hooks/useAlignActiveElement'
import useUniformDisplayElement from '@/hooks/useUniformDisplayElement'
export default defineComponent({
name: 'multi-position-panel',
@ -47,6 +52,7 @@ export default defineComponent({
const { combineElements, uncombineElements } = useCombineElement()
const { alignActiveElement } = useAlignActiveElement()
const { uniformHorizontalDisplay, uniformVerticalDisplay } = useUniformDisplayElement()
//
const canCombine = computed(() => {
@ -58,10 +64,13 @@ export default defineComponent({
})
return {
activeElementList,
canCombine,
combineElements,
uncombineElements,
alignActiveElement,
uniformHorizontalDisplay,
uniformVerticalDisplay,
}
},
})