docs: 补充代码注释

This commit is contained in:
pipipi-pikachu 2021-02-09 19:49:23 +08:00
parent ce3e0eb529
commit 4b25579b45
19 changed files with 299 additions and 145 deletions

View File

@ -35,6 +35,7 @@ export default defineComponent({
},
setup(props, { emit }) {
const color = computed(() => props.value)
const gradientColor = computed(() => {
const rgbaStr = [color.value.r, color.value.g, color.value.b].join(',')
return `linear-gradient(to right, rgba(${rgbaStr}, 0) 0%, rgba(${rgbaStr}, 1) 100%)`

View File

@ -6,6 +6,7 @@
import { computed, defineComponent } from 'vue'
const checkboardCache = {}
const renderCheckboard = (white: string, grey: string, size: number) => {
const canvas = document.createElement('canvas')
canvas.width = canvas.height = size * 2

View File

@ -27,7 +27,7 @@
height: rubberSize + 'px',
}"
v-if="mouseInCanvas && model === 'eraser'"
><IconClearFormat class="icon" :size="rubberSize * 0.6" /></div>
></div>
</div>
</template>
@ -53,6 +53,7 @@ export default defineComponent({
let ctx: CanvasRenderingContext2D | null = null
const writingBoardRef = ref<HTMLElement>()
const canvasRef = ref<HTMLCanvasElement>()
let lastPos = {
x: 0,
y: 0,
@ -61,12 +62,22 @@ export default defineComponent({
let lastTime = 0
let lastLineWidth = -1
//
const mouse = reactive({
x: 0,
y: 0,
})
//
const updateMousePosition = (e: MouseEvent) => {
mouse.x = e.pageX
mouse.y = e.pageY
}
//
const mouseInCanvas = ref(false)
//
const initCanvas = () => {
if (!canvasRef.value || !writingBoardRef.value) return
@ -82,30 +93,9 @@ export default defineComponent({
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
}
onMounted(initCanvas)
const getDistance = (posX: number, posY: number) => {
const lastPosX = lastPos.x
const lastPosY = lastPos.y
return Math.sqrt((posX - lastPosX) * (posX - lastPosX) + (posY - lastPosY) * (posY - lastPosY))
}
const getLineWidth = (s: number, t: number) => {
const maxV = 10
const minV = 0.1
const maxWidth = penSize
const minWidth = 3
const v = s / t
let lineWidth
if (v <= minV) lineWidth = maxWidth
else if (v >= maxV) lineWidth = minWidth
else lineWidth = maxWidth - v / maxV * maxWidth
if (lastLineWidth === -1) return lineWidth
return lineWidth * 1 / 3 + lastLineWidth * 2 / 3
}
//
//
const draw = (posX: number, posY: number, lineWidth: number) => {
if (!ctx) return
@ -121,7 +111,7 @@ export default defineComponent({
ctx.closePath()
}
//
//
const erase = (posX: number, posY: number) => {
if (!ctx || !canvasRef.value) return
const lastPosX = lastPos.x
@ -155,47 +145,60 @@ export default defineComponent({
ctx.restore()
}
const startDraw = (posX: number, posY: number) => {
lastPos = { x: posX, y: posY }
lastTime = new Date().getTime()
}
const startMove = (posX: number, posY: number) => {
const time = new Date().getTime()
//
if (props.model === 'pen') {
const s = getDistance(posX, posY)
const t = time - lastTime
const lineWidth = getLineWidth(s, t)
draw(posX, posY, lineWidth)
lastLineWidth = lineWidth
}
//
else erase(posX, posY)
lastPos = { x: posX, y: posY }
lastTime = new Date().getTime()
}
// /
const handleMousedown = (e: MouseEvent) => {
isMouseDown = true
startDraw(e.offsetX, e.offsetY)
lastPos = { x: e.offsetX, y: e.offsetY }
lastTime = new Date().getTime()
}
const updateMousePosition = (e: MouseEvent) => {
mouse.x = e.pageX
mouse.y = e.pageY
//
const getDistance = (posX: number, posY: number) => {
const lastPosX = lastPos.x
const lastPosY = lastPos.y
return Math.sqrt((posX - lastPosX) * (posX - lastPosX) + (posY - lastPosY) * (posY - lastPosY))
}
// st
const getLineWidth = (s: number, t: number) => {
const maxV = 10
const minV = 0.1
const maxWidth = penSize
const minWidth = 3
const v = s / t
let lineWidth
if (v <= minV) lineWidth = maxWidth
else if (v >= maxV) lineWidth = minWidth
else lineWidth = maxWidth - v / maxV * maxWidth
if (lastLineWidth === -1) return lineWidth
return lineWidth * 1 / 3 + lastLineWidth * 2 / 3
}
// /
const handleMousemove = (e: MouseEvent) => {
updateMousePosition(e)
if (!isMouseDown) return
startMove(e.offsetX, e.offsetY)
const time = new Date().getTime()
if (props.model === 'pen') {
const s = getDistance(e.offsetX, e.offsetY)
const t = time - lastTime
const lineWidth = getLineWidth(s, t)
draw(e.offsetX, e.offsetY, lineWidth)
lastLineWidth = lineWidth
}
else erase(e.offsetX, e.offsetY)
lastPos = { x: e.offsetX, y: e.offsetY }
lastTime = new Date().getTime()
}
// /
const handleMouseup = () => {
if (!isMouseDown) return
isMouseDown = false
@ -207,8 +210,6 @@ export default defineComponent({
ctx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height)
}
onMounted(initCanvas)
return {
mouse,
mouseInCanvas,

View File

@ -15,6 +15,10 @@ export default () => {
const { addHistorySnapshot } = useHistorySnapshot()
/**
*
* @param command
*/
const alignElementToCanvas = (command: ElementAlignCommand) => {
const viewportWidth = VIEWPORT_SIZE
const viewportHeight = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
@ -24,33 +28,45 @@ export default () => {
for (const element of newElementList) {
if (!activeElementIdList.value.includes(element.id)) continue
// 水平垂直居中
if (command === ElementAlignCommands.CENTER) {
const offsetY = minY + (maxY - minY) / 2 - viewportHeight / 2
const offsetX = minX + (maxX - minX) / 2 - viewportWidth / 2
element.top = element.top - offsetY
element.left = element.left - offsetX
}
// 顶部对齐
if (command === ElementAlignCommands.TOP) {
const offsetY = minY - 0
element.top = element.top - offsetY
}
// 垂直居中
else if (command === ElementAlignCommands.VERTICAL) {
const offsetY = minY + (maxY - minY) / 2 - viewportHeight / 2
element.top = element.top - offsetY
}
// 底部对齐
else if (command === ElementAlignCommands.BOTTOM) {
const offsetY = maxY - viewportHeight
element.top = element.top - offsetY
}
// 左侧对齐
else if (command === ElementAlignCommands.LEFT) {
const offsetX = minX - 0
element.left = element.left - offsetX
}
// 水平居中
else if (command === ElementAlignCommands.HORIZONTAL) {
const offsetX = minX + (maxX - minX) / 2 - viewportWidth / 2
element.left = element.left - offsetX
}
// 右侧对齐
else if (command === ElementAlignCommands.RIGHT) {
const offsetX = maxX - viewportWidth
element.left = element.left - offsetX

View File

@ -9,16 +9,23 @@ export default () => {
const activeElementIdList = computed(() => store.state.activeElementIdList)
const activeElementList = computed<PPTElement[]>(() => store.getters.activeElementList)
const currentSlide = computed<Slide>(() => store.getters.currentSlide)
const handleElementId = computed(() => store.state.handleElementId)
const { addHistorySnapshot } = useHistorySnapshot()
// 组合元素为当前所有激活元素添加一个相同的groupId
/**
* ID
*/
const combineElements = () => {
if (!activeElementList.value.length) return
// 生成一个新元素列表进行后续操作
let newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
// 生成分组ID
const groupId = createRandomCode()
// 收集需要组合的元素列表并赋上唯一分组ID
const combineElementList: PPTElement[] = []
for (const element of newElementList) {
if (activeElementIdList.value.includes(element.id)) {
@ -27,19 +34,23 @@ export default () => {
}
}
// 注意,组合元素的层级应该是连续的,所以需要获取该组元素中最顶层的元素,将组内其他成员从原位置移动到最顶层的元素的下面
const combineElementMaxIndex = newElementList.findIndex(_element => _element.id === combineElementList[combineElementList.length - 1].id)
// 确保该组合内所有元素成员的层级是连续的,具体操作方法为:
// 先获取到该组合内最上层元素的层级,将本次需要组合的元素从新元素列表中移除,
// 再根据最上层元素的层级位置,将上面收集到的需要组合的元素列表一起插入到新元素列表中合适的位置
const combineElementMaxLevel = newElementList.findIndex(_element => _element.id === combineElementList[combineElementList.length - 1].id)
const combineElementIdList = combineElementList.map(_element => _element.id)
newElementList = newElementList.filter(_element => !combineElementIdList.includes(_element.id))
const insertIndex = combineElementMaxIndex - combineElementList.length + 1
newElementList.splice(insertIndex, 0, ...combineElementList)
const insertLevel = combineElementMaxLevel - combineElementList.length + 1
newElementList.splice(insertLevel, 0, ...combineElementList)
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
addHistorySnapshot()
}
// 取消组合元素移除所有被激活元素的groupId
/**
* ID
*/
const uncombineElements = () => {
if (!activeElementList.value.length) return
const hasElementInGroup = activeElementList.value.some(item => item.groupId)
@ -50,6 +61,11 @@ export default () => {
if (activeElementIdList.value.includes(element.id) && element.groupId) delete element.groupId
}
store.commit(MutationTypes.UPDATE_SLIDE, { elements: newElementList })
// 取消组合后,需要重置激活元素状态
// 默认重置为当前正在操作的元素,如果不存在则重置为空
const handleElementIdList = handleElementId.value ? [handleElementId.value] : []
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, handleElementIdList)
addHistorySnapshot()
}

View File

@ -15,6 +15,7 @@ export default () => {
const { pasteTextClipboardData } = usePasteTextClipboardData()
const { deleteElement } = useDeleteElement()
// 将选中元素数据加密后复制到剪贴板
const copyElement = () => {
if (!activeElementIdList.value.length) return
@ -28,17 +29,20 @@ export default () => {
})
}
// 将选中元素复制后删除(剪切)
const cutElement = () => {
copyElement()
deleteElement()
}
// 尝试将剪贴板元素数据解密后进行粘贴
const pasteElement = () => {
readClipboard().then(text => {
pasteTextClipboardData(text)
}).catch(err => message.warning(err))
}
// 将选中元素复制后立刻粘贴
const quickCopyElement = () => {
copyElement()
pasteElement()

View File

@ -29,12 +29,17 @@ export default () => {
const { addHistorySnapshot } = useHistorySnapshot()
// 创建(插入)一个元素并将其设置为被选中元素
const createElement = (element: PPTElement) => {
store.commit(MutationTypes.ADD_ELEMENT, element)
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [element.id])
addHistorySnapshot()
}
/**
*
* @param src
*/
const createImageElement = (src: string) => {
getImageSize(src).then(({ width, height }) => {
const scale = height / width
@ -61,6 +66,10 @@ export default () => {
})
}
/**
*
* @param chartType
*/
const createChartElement = (chartType: ChartType) => {
createElement({
type: 'chart',
@ -81,6 +90,11 @@ export default () => {
})
}
/**
*
* @param row
* @param col
*/
const createTableElement = (row: number, col: number) => {
const rowCells: TableCell[] = new Array(col).fill({ id: createRandomCode(), colspan: 1, rowspan: 1, text: '' })
const data: TableCell[][] = new Array(row).fill(rowCells)
@ -117,6 +131,11 @@ export default () => {
})
}
/**
*
* @param position
* @param content
*/
const createTextElement = (position: CommonElementPosition, content = '请输入内容') => {
const { left, top, width, height } = position
createElement({
@ -130,6 +149,11 @@ export default () => {
})
}
/**
*
* @param position
* @param data
*/
const createShapeElement = (position: CommonElementPosition, data: ShapePoolItem) => {
const { left, top, width, height } = position
createElement({
@ -146,6 +170,11 @@ export default () => {
})
}
/**
* 线
* @param position
* @param data 线
*/
const createLineElement = (position: LineElementPosition, data: LinePoolItem) => {
const { left, top, start, end } = position
createElement({

View File

@ -10,6 +10,7 @@ export default () => {
const { addHistorySnapshot } = useHistorySnapshot()
// 删除全部选中元素
const deleteElement = () => {
if (!activeElementIdList.value.length) return
const newElementList = currentSlide.value.elements.filter(el => !activeElementIdList.value.includes(el.id))
@ -18,6 +19,7 @@ export default () => {
addHistorySnapshot()
}
// 删除内面内全部元素(无论是否选中)
const deleteAllElements = () => {
if (!currentSlide.value.elements.length) return
store.commit(MutationTypes.SET_ACTIVE_ELEMENT_ID_LIST, [])

View File

@ -5,14 +5,17 @@ import { ActionTypes, useStore } from '@/store'
export default () => {
const store = useStore()
// 添加历史快照(历史记录)
const addHistorySnapshot = debounce(function() {
store.dispatch(ActionTypes.ADD_SNAPSHOT)
}, 300, { trailing: true })
// 重做
const redo = throttle(function() {
store.dispatch(ActionTypes.RE_DO)
}, 100, { leading: true, trailing: false })
// 撤销
const undo = throttle(function() {
store.dispatch(ActionTypes.UN_DO)
}, 100, { leading: true, trailing: false })

View File

@ -10,6 +10,7 @@ export default () => {
const { addHistorySnapshot } = useHistorySnapshot()
// 锁定选中的元素,并清空选中元素状态
const lockElement = () => {
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))
@ -21,6 +22,10 @@ export default () => {
addHistorySnapshot()
}
/**
* ,
* @param handleElement
*/
const unlockElement = (handleElement: PPTElement) => {
const newElementList: PPTElement[] = JSON.parse(JSON.stringify(currentSlide.value.elements))

View File

@ -11,22 +11,27 @@ export default () => {
const { addHistorySnapshot } = useHistorySnapshot()
const moveElement = (command: string) => {
/**
*
* @param command
* @param step
*/
const moveElement = (command: string, step = 1) => {
const newElementList = currentSlide.value.elements.map(el => {
if (activeElementIdList.value.includes(el.id)) {
let { left, top } = el
switch (command) {
case KEYS.LEFT:
left = left - 1
left = left - step
break
case KEYS.RIGHT:
left = left + 1
left = left + step
break
case KEYS.UP:
top = top - 1
top = top - step
break
case KEYS.DOWN:
top = top + 1
top = top + step
break
default: break
}

View File

@ -10,165 +10,190 @@ export default () => {
const { addHistorySnapshot } = useHistorySnapshot()
// 获取组合元素层级范围(组合成员中的最大层级和最小层级)
const getCombineElementIndexRange = (elementList: PPTElement[], combineElementList: PPTElement[]) => {
const minIndex = elementList.findIndex(_element => _element.id === combineElementList[0].id)
const maxIndex = elementList.findIndex(_element => _element.id === combineElementList[combineElementList.length - 1].id)
return { minIndex, maxIndex }
/**
*
* @param elementList
* @param combineElementList
*/
const getCombineElementLevelRange = (elementList: PPTElement[], combineElementList: PPTElement[]) => {
return {
minLevel: elementList.findIndex(_element => _element.id === combineElementList[0].id),
maxLevel: elementList.findIndex(_element => _element.id === combineElementList[combineElementList.length - 1].id),
}
}
// 上移一层,返回移动后新的元素列表(下移一层逻辑类似)
/**
*
* @param elementList
* @param element
*/
const moveUpElement = (elementList: PPTElement[], element: PPTElement) => {
const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
// 被操作的元素是组合元素成员
// 如果被操作的元素是组合元素成员,需要将该组合全部成员一起进行移动
if (element.groupId) {
// 获取该组合元素全部成员,以及组合元素层级范围
// 获取到该组合全部成员,以及所有成员的层级范围
const combineElementList = copyOfElementList.filter(_element => _element.groupId === element.groupId)
const { minIndex, maxIndex } = getCombineElementIndexRange(elementList, combineElementList)
const { minLevel, maxLevel } = getCombineElementLevelRange(elementList, combineElementList)
// 无法移动(已经处在顶层)
if (maxIndex === elementList.length - 1) return null
// 已经处在顶层,无法继续移动
if (maxLevel === elementList.length - 1) return
// 该组合元素上一层的元素以下简称为【元素next】
const nextElement = copyOfElementList[maxIndex + 1]
// 通过组合成员范围的最大值,获取到该组合上一层的元素,然后将该组合元素从元素列表中移除(并缓存被移除的元素列表)
// 若上层元素处在另一个组合中,则将上述被移除的组合元素插入到该上层组合上方
// 若上层元素不处于任何分组中,则将上述被移除的组合元素插入到该上层元素上方
const nextElement = copyOfElementList[maxLevel + 1]
const movedElementList = copyOfElementList.splice(minLevel, combineElementList.length)
// 从元素列表中移除该组合元素全部成员
const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
// 如果【元素next】也是组合元素成员另一个组合不是上面被移除的那一组以下简称为【组合next】
// 需要获取【组合next】全部成员的长度将上面移除的组合元素全部成员添加到【组合next】全部成员的上方
if (nextElement.groupId) {
const nextCombineElementList = copyOfElementList.filter(_element => _element.groupId === nextElement.groupId)
copyOfElementList.splice(minIndex + nextCombineElementList.length, 0, ...movedElementList)
copyOfElementList.splice(minLevel + nextCombineElementList.length, 0, ...movedElementList)
}
// 如果【元素next】是单独的元素非组合成员将上面移除的组合元素全部成员添加到【元素next】上方
else copyOfElementList.splice(minIndex + 1, 0, ...movedElementList)
else copyOfElementList.splice(minLevel + 1, 0, ...movedElementList)
}
// 被操作的元素是单独的元素(非组合成员)
// 如果被操作的元素不是组合元素成员
else {
// 元素在元素列表中的层级
const elementIndex = elementList.findIndex(item => item.id === element.id)
// 获取该元素在列表中的层级
const level = elementList.findIndex(item => item.id === element.id)
// 无法移动(已经处在顶层)
if (elementIndex === elementList.length - 1) return null
// 已经处在顶层,无法继续移动
if (level === elementList.length - 1) return
// 上一层的元素以下简称为【元素next】
const nextElement = copyOfElementList[elementIndex + 1]
// 获取到该组合上一层的元素,然后将该组合元素从元素列表中移除(并缓存被移除的元素列表)
const nextElement = copyOfElementList[level + 1]
const movedElement = copyOfElementList.splice(level, 1)[0]
// 从元素列表中移除被操作的元素
const movedElement = copyOfElementList.splice(elementIndex, 1)[0]
// 如果【元素next】是组合元素成员
// 需要获取该组合全部成员的长度,将上面移除的元素添加到该组合全部成员的上方
// 通过组合成员范围的最大值,获取到该组合上一层的元素,然后将该组合元素从元素列表中移除(并缓存被移除的元素列表)
// 若上层元素处在另一个组合中,则将上述被移除的组合元素插入到该上层组合上方
// 若上层元素不处于任何分组中,则将上述被移除的组合元素插入到该上层元素上方
if (nextElement.groupId) {
const combineElementList = copyOfElementList.filter(_element => _element.groupId === nextElement.groupId)
copyOfElementList.splice(elementIndex + combineElementList.length, 0, movedElement)
copyOfElementList.splice(level + combineElementList.length, 0, movedElement)
}
// 如果【元素next】是单独的元素非组合成员将上面移除的元素添加到【元素next】上方
else copyOfElementList.splice(elementIndex + 1, 0, movedElement)
else copyOfElementList.splice(level + 1, 0, movedElement)
}
return copyOfElementList
}
// 下移一层
/**
*
* @param elementList
* @param element
*/
const moveDownElement = (elementList: PPTElement[], element: PPTElement) => {
const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
if (element.groupId) {
const combineElementList = copyOfElementList.filter(_element => _element.groupId === element.groupId)
const { minIndex } = getCombineElementIndexRange(elementList, combineElementList)
if (minIndex === 0) return null
const prevElement = copyOfElementList[minIndex - 1]
const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
const { minLevel } = getCombineElementLevelRange(elementList, combineElementList)
if (minLevel === 0) return
const prevElement = copyOfElementList[minLevel - 1]
const movedElementList = copyOfElementList.splice(minLevel, combineElementList.length)
if (prevElement.groupId) {
const prevCombineElementList = copyOfElementList.filter(_element => _element.groupId === prevElement.groupId)
copyOfElementList.splice(minIndex - prevCombineElementList.length, 0, ...movedElementList)
copyOfElementList.splice(minLevel - prevCombineElementList.length, 0, ...movedElementList)
}
else copyOfElementList.splice(minIndex - 1, 0, ...movedElementList)
else copyOfElementList.splice(minLevel - 1, 0, ...movedElementList)
}
else {
const elementIndex = elementList.findIndex(item => item.id === element.id)
if (elementIndex === 0) return null
const prevElement = copyOfElementList[elementIndex - 1]
const movedElement = copyOfElementList.splice(elementIndex, 1)[0]
const level = elementList.findIndex(item => item.id === element.id)
if (level === 0) return
const prevElement = copyOfElementList[level - 1]
const movedElement = copyOfElementList.splice(level, 1)[0]
if (prevElement.groupId) {
const combineElementList = copyOfElementList.filter(_element => _element.groupId === prevElement.groupId)
copyOfElementList.splice(elementIndex - combineElementList.length, 0, movedElement)
copyOfElementList.splice(level - combineElementList.length, 0, movedElement)
}
else copyOfElementList.splice(elementIndex - 1, 0, movedElement)
else copyOfElementList.splice(level - 1, 0, movedElement)
}
return copyOfElementList
}
// 置顶层,返回移动后新的元素列表(置底层逻辑类似)
/**
*
* @param elementList
* @param element
*/
const moveTopElement = (elementList: PPTElement[], element: PPTElement) => {
const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
// 被操作的元素是组合元素成员
// 如果被操作的元素是组合元素成员,需要将该组合全部成员一起进行移动
if (element.groupId) {
// 获取该组合元素全部成员,以及组合元素层级范围
// 获取到该组合全部成员,以及所有成员的层级范围
const combineElementList = copyOfElementList.filter(_element => _element.groupId === element.groupId)
const { minIndex, maxIndex } = getCombineElementIndexRange(elementList, combineElementList)
const { minLevel, maxLevel } = getCombineElementLevelRange(elementList, combineElementList)
// 无法移动(已经处在顶层)
if (maxIndex === elementList.length - 1) return null
// 已经处在顶层,无法继续移动
if (maxLevel === elementList.length - 1) return null
// 从元素列表中移除该组合元素全部成员,然后添加到元素列表最上方
const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
// 将该组合元素从元素列表中移除,然后将被移除的元素添加到元素列表顶部
const movedElementList = copyOfElementList.splice(minLevel, combineElementList.length)
copyOfElementList.push(...movedElementList)
}
// 被操作的元素是单独的元素(非组合成员)
// 如果被操作的元素不是组合元素成员
else {
// 元素在元素列表中的层级
const elementIndex = elementList.findIndex(item => item.id === element.id)
// 获取该元素在列表中的层级
const level = elementList.findIndex(item => item.id === element.id)
// 无法移动(已经处在顶层)
if (elementIndex === elementList.length - 1) return null
// 已经处在顶层,无法继续移动
if (level === elementList.length - 1) return null
// 从元素列表中移除该元素,然后添加到元素列表最上方
copyOfElementList.splice(elementIndex, 1)
// 将该组合元素从元素列表中移除,然后将被移除的元素添加到元素列表底部
copyOfElementList.splice(level, 1)
copyOfElementList.push(element)
}
return copyOfElementList
}
// 置底层
/**
*
* @param elementList
* @param element
*/
const moveBottomElement = (elementList: PPTElement[], element: PPTElement) => {
const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
if (element.groupId) {
const combineElementList = copyOfElementList.filter(_element => _element.groupId === element.groupId)
const { minIndex } = getCombineElementIndexRange(elementList, combineElementList)
if (minIndex === 0) return null
const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
const { minLevel } = getCombineElementLevelRange(elementList, combineElementList)
if (minLevel === 0) return
const movedElementList = copyOfElementList.splice(minLevel, combineElementList.length)
copyOfElementList.unshift(...movedElementList)
}
else {
const elementIndex = elementList.findIndex(item => item.id === element.id)
if (elementIndex === 0) return null
copyOfElementList.splice(elementIndex, 1)
const level = elementList.findIndex(item => item.id === element.id)
if (level === 0) return
copyOfElementList.splice(level, 1)
copyOfElementList.unshift(element)
}
return copyOfElementList
}
/**
*
* @param element
* @param command
*/
const orderElement = (element: PPTElement, command: ElementOrderCommand) => {
let newElementList = null
let newElementList
if (command === ElementOrderCommands.UP) newElementList = moveUpElement(currentSlide.value.elements, element)
else if (command === ElementOrderCommands.DOWN) newElementList = moveDownElement(currentSlide.value.elements, element)

View File

@ -18,6 +18,10 @@ export default () => {
const { addHistorySnapshot } = useHistorySnapshot()
const { createTextElement } = useCreateElement()
/**
*
* @param elements
*/
const pasteElement = (elements: PPTElement[]) => {
const groupIdMap = {}
const elIdMap = {}
@ -47,6 +51,10 @@ export default () => {
addHistorySnapshot()
}
/**
*
* @param slide
*/
const pasteSlide = (slide: Slide) => {
store.commit(MutationTypes.ADD_SLIDE, {
...slide,
@ -55,6 +63,10 @@ export default () => {
addHistorySnapshot()
}
/**
*
* @param text
*/
const pasteText = (text: string) => {
createTextElement({
left: 0,
@ -64,6 +76,11 @@ export default () => {
}, text)
}
/**
*
* @param text
* @param options onlySlide -- onlyElements --
*/
const pasteTextClipboardData = (text: string, options?: PasteTextClipboardDataOptions) => {
const onlySlide = options?.onlySlide || false
const onlyElements = options?.onlyElements || false
@ -76,7 +93,7 @@ export default () => {
clipboardData = text
}
// 粘贴自定义元素或页面
// 元素或页面
if (typeof clipboardData === 'object') {
const { type, data } = clipboardData
@ -84,7 +101,7 @@ export default () => {
else if (type === 'slide' && !onlyElements) pasteSlide(data)
}
// 粘贴普通文本
// 普通文本
else if (!onlyElements && !onlySlide) pasteText(clipboardData)
}

View File

@ -5,6 +5,10 @@ export default () => {
const store = useStore()
const canvasPercentage = computed(() => store.state.canvasPercentage)
/**
*
* @param command
*/
const scaleCanvas = (command: '+' | '-') => {
let percentage = canvasPercentage.value
const step = 5
@ -16,6 +20,10 @@ export default () => {
store.commit(MutationTypes.SET_CANVAS_PERCENTAGE, percentage)
}
/**
*
* @param percentage 0.8
*/
const setCanvasPercentage = (percentage: number) => {
store.commit(MutationTypes.SET_CANVAS_PERCENTAGE, percentage)
}

View File

@ -4,16 +4,19 @@ import { enterFullscreen, exitFullscreen, isFullscreen } from '@/utils/fullscree
export default () => {
const store = useStore()
// 进入放映状态(从当前页开始)
const enterScreening = () => {
enterFullscreen()
store.commit(MutationTypes.SET_SCREENING, true)
}
// 进入放映状态(从第一页开始)
const enterScreeningFromStart = () => {
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, 0)
enterScreening()
}
// 退出放映状态
const exitScreening = () => {
store.commit(MutationTypes.SET_SCREENING, false)
if (isFullscreen()) exitFullscreen()

View File

@ -6,6 +6,7 @@ export default () => {
const store = useStore()
const currentSlide = computed<Slide>(() => store.getters.currentSlide)
// 将当前页面全部元素设置为被选择状态
const selectAllElement = () => {
const unlockedElements = currentSlide.value.elements.filter(el => !el.lock)
const newActiveElementIdList = unlockedElements.map(el => el.id)

View File

@ -1,6 +1,7 @@
import { Ref, computed } from 'vue'
import { SlideBackground } from '@/types/slides'
// 将页面背景数据转换为css样式
export default (background: Ref<SlideBackground | undefined>) => {
const backgroundStyle = computed(() => {
if (!background.value) return { backgroundColor: '#fff' }
@ -15,7 +16,11 @@ export default (background: Ref<SlideBackground | undefined>) => {
gradientType,
} = background.value
// 纯色背景
if (type === 'solid') return { backgroundColor: color }
// 背景图模式
// 包括:背景图、背景大小,是否重复
else if (type === 'image') {
if (!image) return { backgroundColor: '#fff' }
if (imageSize === 'repeat') {
@ -31,6 +36,8 @@ export default (background: Ref<SlideBackground | undefined>) => {
backgroundSize: imageSize || 'cover',
}
}
// 渐变色背景
else if (type === 'gradient') {
const rotate = gradientRotate || 0
const color1 = gradientColor ? gradientColor[0] : '#fff'

View File

@ -19,6 +19,10 @@ export default () => {
const { pasteTextClipboardData } = usePasteTextClipboardData()
const { addHistorySnapshot } = useHistorySnapshot()
/**
*
* @param command
*/
const updateSlideIndex = (command: string) => {
let targetIndex = 0
if (command === KEYS.UP && slideIndex.value > 0) {
@ -30,6 +34,7 @@ export default () => {
store.commit(MutationTypes.UPDATE_SLIDE_INDEX, targetIndex)
}
// 将当前页面数据加密后复制到剪贴板
const copySlide = () => {
const text = encrypt(JSON.stringify({
type: 'slide',
@ -41,12 +46,14 @@ export default () => {
})
}
// 尝试将剪贴板页面数据解密后添加到下一页(粘贴)
const pasteSlide = () => {
readClipboard().then(text => {
pasteTextClipboardData(text, { onlySlide: true })
}).catch(err => message.warning(err))
}
// 创建一页空白页并添加到下一页
const createSlide = () => {
const emptySlide = {
id: createRandomCode(8),
@ -60,6 +67,7 @@ export default () => {
addHistorySnapshot()
}
// 将当前页复制一份到下一页
const copyAndPasteSlide = () => {
store.commit(MutationTypes.ADD_SLIDE, {
...currentSlide.value,
@ -68,6 +76,7 @@ export default () => {
addHistorySnapshot()
}
// 删除当前页
const deleteSlide = () => {
if (slidesLength.value === 1) return message.warning('无法继续删除')
@ -75,6 +84,7 @@ export default () => {
addHistorySnapshot()
}
// 将当前页复制后删除(剪切)
const cutSlide = () => {
copySlide()
deleteSlide()

View File

@ -1,3 +1,5 @@
// https://iconpark.bytedance.com/official
import { App } from 'vue'
import {
PlayOne,
@ -24,7 +26,6 @@ import {
BackgroundColor,
Group,
Ungroup,
ClearFormat,
Back,
Next,
Fullwidth,
@ -160,7 +161,6 @@ export default {
app.component('IconSearch', Search)
app.component('IconPpt', Ppt)
app.component('IconHelpcenter', Helpcenter)
app.component('IconClearFormat', ClearFormat)
app.component('IconGithub', Github)
app.component('IconWrite', Write)
app.component('IconEffects', Effects)