update
This commit is contained in:
parent
2d245dd88b
commit
7f301cadfd
|
@ -1,5 +1,4 @@
|
|||
export enum KEYCODE {
|
||||
S = 83,
|
||||
C = 67,
|
||||
X = 88,
|
||||
Z = 90,
|
||||
|
|
|
@ -9,7 +9,6 @@ export enum MutationTypes {
|
|||
SET_EDITORAREA_FOCUS = 'setEditorAreaFocus',
|
||||
SET_DISABLE_HOTKEYS_STATE = 'setDisableHotkeysState',
|
||||
SET_AVAILABLE_FONTS = 'setAvailableFonts',
|
||||
SET_SAVE_STATE = 'setSaveState',
|
||||
|
||||
// slides
|
||||
SET_SLIDES = 'setSlides',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { MutationTypes } from './constants'
|
||||
import { State, SaveState } from './state'
|
||||
import { State } from './state'
|
||||
import { Slide, PPTElement } from '@/types/slides'
|
||||
import { FONT_NAMES } from '@/configs/fontName'
|
||||
import { isSupportFontFamily } from '@/utils/index'
|
||||
|
@ -36,7 +36,6 @@ export type Mutations = {
|
|||
[MutationTypes.SET_EDITORAREA_FOCUS](state: State, isFocus: boolean): void;
|
||||
[MutationTypes.SET_DISABLE_HOTKEYS_STATE](state: State, disable: boolean): void;
|
||||
[MutationTypes.SET_AVAILABLE_FONTS](state: State): void;
|
||||
[MutationTypes.SET_SAVE_STATE](state: State, saveState: SaveState ): void;
|
||||
[MutationTypes.SET_SLIDES](state: State, slides: Slide[]): void;
|
||||
[MutationTypes.ADD_SLIDES](state: State, data: AddSlidesData): void;
|
||||
[MutationTypes.SET_SLIDE](state: State, data: SetSlideData): void;
|
||||
|
@ -91,10 +90,6 @@ export const mutations: Mutations = {
|
|||
state.availableFonts = FONT_NAMES.filter(font => isSupportFontFamily(font.en))
|
||||
},
|
||||
|
||||
[MutationTypes.SET_SAVE_STATE](state, saveState) {
|
||||
state.saveState = saveState
|
||||
},
|
||||
|
||||
// slides
|
||||
|
||||
[MutationTypes.SET_SLIDES](state, slides) {
|
||||
|
|
|
@ -2,8 +2,6 @@ import { Slide } from '@/types/slides'
|
|||
import { slides } from '@/mocks/index'
|
||||
import { FontName } from '@/configs/fontName'
|
||||
|
||||
export type SaveState = 'complete' | 'pending'
|
||||
|
||||
export type State = {
|
||||
activeElementIdList: string[];
|
||||
handleElementId: string;
|
||||
|
@ -13,7 +11,6 @@ export type State = {
|
|||
editorAreaFocus: boolean;
|
||||
disableHotkeys: boolean;
|
||||
availableFonts: FontName[];
|
||||
saveState: SaveState;
|
||||
slides: Slide[];
|
||||
slideIndex: number;
|
||||
cursor: number;
|
||||
|
@ -29,7 +26,6 @@ export const state: State = {
|
|||
editorAreaFocus: false,
|
||||
disableHotkeys: false,
|
||||
availableFonts: [],
|
||||
saveState: 'complete',
|
||||
slides: slides,
|
||||
slideIndex: 0,
|
||||
cursor: -1,
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
export type ElementOrderCommand = 'up' | 'down' | 'top' | 'bottom'
|
||||
|
||||
export enum ElementOrderCommands {
|
||||
UP = 'up',
|
||||
DOWN = 'down',
|
||||
TOP = 'top',
|
||||
BOTTOM = 'bottom',
|
||||
}
|
||||
|
||||
export type ElementAlignCommand = 'top'| 'bottom' | 'left' | 'right' | 'vertical' | 'horizontal'
|
||||
|
||||
export enum ElementAlignCommands {
|
||||
TOP = 'top',
|
||||
BOTTOM = 'bottom',
|
||||
LEFT = 'left',
|
||||
RIGHT = 'right',
|
||||
VERTICAL = 'vertical',
|
||||
HORIZONTAL = 'horizontal',
|
||||
}
|
||||
|
||||
export type ElementScaleHandler = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
|
||||
|
||||
export type ElementLockCommand = 'lock' | 'unlock'
|
||||
|
||||
export enum ElementLockCommands {
|
||||
LOCK = 'lock',
|
||||
UNLOCK = 'unlock',
|
||||
}
|
|
@ -19,7 +19,6 @@ export interface PPTElementBorderProps {
|
|||
|
||||
export interface PPTTextElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
|
||||
type: 'text';
|
||||
textType: string;
|
||||
content: string;
|
||||
rotate?: number;
|
||||
fill?: string;
|
||||
|
@ -28,20 +27,18 @@ export interface PPTTextElement extends PPTElementBaseProps, PPTElementSizeProps
|
|||
segmentSpacing?: number;
|
||||
letterSpacing?: number;
|
||||
shadow?: string;
|
||||
padding?: number;
|
||||
}
|
||||
|
||||
interface ImageClip {
|
||||
range: [[number, number], [number, number]];
|
||||
shape: string;
|
||||
}
|
||||
export interface PPTImageElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
|
||||
type: 'image';
|
||||
lockRatio: boolean;
|
||||
imgUrl: string;
|
||||
rotate?: number;
|
||||
filter?: string;
|
||||
clip?: ImageClip;
|
||||
clip?: {
|
||||
range: [[number, number], [number, number]];
|
||||
shape: string;
|
||||
};
|
||||
flip?: string;
|
||||
shadow?: string;
|
||||
}
|
||||
|
@ -58,15 +55,6 @@ export interface PPTShapeElement extends PPTElementBaseProps, PPTElementSizeProp
|
|||
textAlign?: string;
|
||||
}
|
||||
|
||||
export interface PPTIconElement extends PPTElementBaseProps, PPTElementSizeProps {
|
||||
type: 'icon';
|
||||
color: string;
|
||||
lockRatio: boolean;
|
||||
svgCode: string;
|
||||
rotate?: number;
|
||||
shadow?: string;
|
||||
}
|
||||
|
||||
export interface PPTLineElement extends PPTElementBaseProps {
|
||||
type: 'line';
|
||||
start: [number, number];
|
||||
|
@ -78,23 +66,11 @@ export interface PPTLineElement extends PPTElementBaseProps {
|
|||
lineType: string;
|
||||
}
|
||||
|
||||
export interface BarChartSeries {
|
||||
name: string;
|
||||
data: number[];
|
||||
}
|
||||
export interface BarChartData {
|
||||
axisData: string[];
|
||||
series: BarChartSeries[];
|
||||
}
|
||||
export interface PieChartData {
|
||||
name: string;
|
||||
value: number
|
||||
}
|
||||
export interface PPTChartElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
|
||||
type: 'chart';
|
||||
chartType: string;
|
||||
theme: string;
|
||||
data: PieChartData[] | BarChartData;
|
||||
data: Object;
|
||||
}
|
||||
|
||||
export interface TableCell {
|
||||
|
@ -111,19 +87,13 @@ export interface PPTTableElement extends PPTElementBaseProps, PPTElementSizeProp
|
|||
colSizes: number[];
|
||||
data: TableCell[][];
|
||||
}
|
||||
export interface PPTIframeElement extends PPTElementBaseProps, PPTElementSizeProps, PPTElementBorderProps {
|
||||
type: 'iframe';
|
||||
src: string;
|
||||
}
|
||||
|
||||
export type PPTElement = PPTTextElement |
|
||||
PPTImageElement |
|
||||
PPTShapeElement |
|
||||
PPTIconElement |
|
||||
PPTLineElement |
|
||||
PPTChartElement |
|
||||
PPTTableElement |
|
||||
PPTIframeElement
|
||||
PPTTableElement
|
||||
|
||||
export interface PPTAnimation {
|
||||
elId: string;
|
||||
|
|
|
@ -169,6 +169,17 @@ export const copyText = (text: string) => {
|
|||
})
|
||||
}
|
||||
|
||||
// 读取剪贴板
|
||||
export const readClipboard = () => {
|
||||
if(navigator.clipboard) {
|
||||
navigator.clipboard.readText().then(text => {
|
||||
if(!text) return { err: '剪贴板为空或者不包含文本' }
|
||||
return { text }
|
||||
})
|
||||
}
|
||||
return { err: '浏览器不支持或禁止访问剪贴板' }
|
||||
}
|
||||
|
||||
// 加密函数
|
||||
export const encrypt = (msg: string) => {
|
||||
return CryptoJS.AES.encrypt(msg, CRYPTO_KEY).toString()
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import { PPTElement } from '@/types/slides'
|
||||
import { ElementAlignCommand, ElementAlignCommands } from '@/types/edit'
|
||||
import { getElementListRange } from './elementRange'
|
||||
import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas'
|
||||
|
||||
// 将元素对齐到屏幕
|
||||
export const alignElement = (elementList: PPTElement[], activeElementList: PPTElement[], activeElementIdList: string[], command: ElementAlignCommand) => {
|
||||
const viewportWidth = VIEWPORT_SIZE
|
||||
const viewportHeight = VIEWPORT_SIZE * VIEWPORT_ASPECT_RATIO
|
||||
const { minX, maxX, minY, maxY } = getElementListRange(activeElementList)
|
||||
|
||||
const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
|
||||
for(const element of copyOfElementList) {
|
||||
if(!activeElementIdList.includes(element.elId)) continue
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return copyOfElementList
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import { createRandomCode } from '@/utils/index'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
|
||||
// 组合元素(为当前所有激活元素添加一个相同的groupId)
|
||||
export const combineElements = (elementList: PPTElement[], activeElementList: PPTElement[], activeElementIdList: string[]) => {
|
||||
if(!activeElementList.length) return null
|
||||
|
||||
let copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
|
||||
const groupId = createRandomCode()
|
||||
|
||||
const combineElementList: PPTElement[] = []
|
||||
for(const element of copyOfElementList) {
|
||||
if(activeElementIdList.includes(element.elId)) {
|
||||
element.groupId = groupId
|
||||
combineElementList.push(element)
|
||||
}
|
||||
}
|
||||
|
||||
// 注意,组合元素的层级应该是连续的,所以需要获取该组元素中最顶层的元素,将组内其他成员从原位置移动到最顶层的元素的下面
|
||||
const combineElementMaxIndex = copyOfElementList.findIndex(_element => _element.elId === combineElementList[combineElementList.length - 1].elId)
|
||||
const combineElementIdList = combineElementList.map(_element => _element.elId)
|
||||
copyOfElementList = copyOfElementList.filter(_element => !combineElementIdList.includes(_element.elId))
|
||||
|
||||
const insertIndex = combineElementMaxIndex - combineElementList.length + 1
|
||||
copyOfElementList.splice(insertIndex, 0, ...combineElementList)
|
||||
|
||||
return copyOfElementList
|
||||
}
|
||||
|
||||
// 取消组合元素(移除所有被激活元素的groupId)
|
||||
export const uncombineElements = (elementList: PPTElement[], activeElementList: PPTElement[], activeElementIdList: string[]) => {
|
||||
if(!activeElementList.length) return null
|
||||
const hasElementInGroup = activeElementList.some(item => item.groupId)
|
||||
if(!hasElementInGroup) return null
|
||||
|
||||
const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
|
||||
for(const element of copyOfElementList) {
|
||||
if(activeElementIdList.includes(element.elId) && element.groupId) delete element.groupId
|
||||
}
|
||||
return copyOfElementList
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import { PPTElement } from '@/types/slides'
|
||||
import { ElementLockCommand, ElementLockCommands } from '@/types/edit'
|
||||
|
||||
const lock = (copyOfElementList: PPTElement[], handleElement: PPTElement, activeElementIdList: string[]) => {
|
||||
for(const element of copyOfElementList) {
|
||||
if(activeElementIdList.includes(handleElement.elId)) element.isLock = true
|
||||
}
|
||||
return copyOfElementList
|
||||
}
|
||||
|
||||
const unlock = (copyOfElementList: PPTElement[], handleElement: PPTElement) => {
|
||||
if(handleElement.groupId) {
|
||||
for(const element of copyOfElementList) {
|
||||
if(element.groupId === handleElement.groupId) element.isLock = false
|
||||
}
|
||||
return copyOfElementList
|
||||
}
|
||||
|
||||
for(const element of copyOfElementList) {
|
||||
if(element.elId === handleElement.elId) {
|
||||
element.isLock = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return copyOfElementList
|
||||
}
|
||||
|
||||
// 锁定&解锁 元素
|
||||
export const lockElement = (elementList: PPTElement[], handleElement: PPTElement, activeElementIdList: string[], command: ElementLockCommand) => {
|
||||
const copyOfElementList: PPTElement[] = JSON.parse(JSON.stringify(elementList))
|
||||
|
||||
if(command === ElementLockCommands.LOCK) return lock(copyOfElementList, handleElement, activeElementIdList)
|
||||
return unlock(copyOfElementList, handleElement)
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
import { PPTElement } from '@/types/slides'
|
||||
import { ElementOrderCommand, ElementOrderCommands } from '@/types/edit'
|
||||
|
||||
// 获取组合元素层级范围(组合成员中的最大层级和最小层级)
|
||||
const getCombineElementIndexRange = (elementList: PPTElement[], combineElementList: PPTElement[]) => {
|
||||
const minIndex = elementList.findIndex(_element => _element.elId === combineElementList[0].elId)
|
||||
const maxIndex = elementList.findIndex(_element => _element.elId === combineElementList[combineElementList.length - 1].elId)
|
||||
return { minIndex, maxIndex }
|
||||
}
|
||||
|
||||
// 上移一层,返回移动后新的元素列表(下移一层逻辑类似)
|
||||
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)
|
||||
|
||||
// 无法移动(已经处在顶层)
|
||||
if(maxIndex === elementList.length - 1) return null
|
||||
|
||||
// 该组合元素上一层的元素,以下简称为【元素next】
|
||||
const nextElement = copyOfElementList[maxIndex + 1]
|
||||
|
||||
// 从元素列表中移除该组合元素全部成员
|
||||
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)
|
||||
}
|
||||
|
||||
// 如果【元素next】是单独的元素(非组合成员),将上面移除的组合元素全部成员添加到【元素next】上方
|
||||
else copyOfElementList.splice(minIndex + 1, 0, ...movedElementList)
|
||||
}
|
||||
|
||||
// 被操作的元素是单独的元素(非组合成员)
|
||||
else {
|
||||
|
||||
// 元素在元素列表中的层级
|
||||
const elementIndex = elementList.findIndex(item => item.elId === element.elId)
|
||||
|
||||
// 无法移动(已经处在顶层)
|
||||
if(elementIndex === elementList.length - 1) return null
|
||||
|
||||
// 上一层的元素,以下简称为【元素next】
|
||||
const nextElement = copyOfElementList[elementIndex + 1]
|
||||
|
||||
// 从元素列表中移除被操作的元素
|
||||
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)
|
||||
}
|
||||
|
||||
// 如果【元素next】是单独的元素(非组合成员),将上面移除的元素添加到【元素next】上方
|
||||
else copyOfElementList.splice(elementIndex + 1, 0, movedElement)
|
||||
}
|
||||
|
||||
return copyOfElementList
|
||||
}
|
||||
|
||||
// 下移一层
|
||||
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)
|
||||
if(prevElement.groupId) {
|
||||
const prevCombineElementList = copyOfElementList.filter(_element => _element.groupId === prevElement.groupId)
|
||||
copyOfElementList.splice(minIndex - prevCombineElementList.length, 0, ...movedElementList)
|
||||
}
|
||||
else copyOfElementList.splice(minIndex - 1, 0, ...movedElementList)
|
||||
}
|
||||
|
||||
else {
|
||||
const elementIndex = elementList.findIndex(item => item.elId === element.elId)
|
||||
if(elementIndex === 0) return null
|
||||
const prevElement = copyOfElementList[elementIndex - 1]
|
||||
const movedElement = copyOfElementList.splice(elementIndex, 1)[0]
|
||||
if(prevElement.groupId) {
|
||||
const combineElementList = copyOfElementList.filter(_element => _element.groupId === prevElement.groupId)
|
||||
copyOfElementList.splice(elementIndex - combineElementList.length, 0, movedElement)
|
||||
}
|
||||
else copyOfElementList.splice(elementIndex - 1, 0, movedElement)
|
||||
}
|
||||
|
||||
return copyOfElementList
|
||||
}
|
||||
|
||||
// 置顶层,返回移动后新的元素列表(置底层逻辑类似)
|
||||
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)
|
||||
|
||||
// 无法移动(已经处在顶层)
|
||||
if(maxIndex === elementList.length - 1) return null
|
||||
|
||||
// 从元素列表中移除该组合元素全部成员,然后添加到元素列表最上方
|
||||
const movedElementList = copyOfElementList.splice(minIndex, combineElementList.length)
|
||||
copyOfElementList.push(...movedElementList)
|
||||
}
|
||||
|
||||
// 被操作的元素是单独的元素(非组合成员)
|
||||
else {
|
||||
|
||||
// 元素在元素列表中的层级
|
||||
const elementIndex = elementList.findIndex(item => item.elId === element.elId)
|
||||
|
||||
// 无法移动(已经处在顶层)
|
||||
if(elementIndex === elementList.length - 1) return null
|
||||
|
||||
// 从元素列表中移除该元素,然后添加到元素列表最上方
|
||||
copyOfElementList.splice(elementIndex, 1)
|
||||
copyOfElementList.push(element)
|
||||
}
|
||||
|
||||
return copyOfElementList
|
||||
}
|
||||
|
||||
// 置底层
|
||||
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)
|
||||
copyOfElementList.unshift(...movedElementList)
|
||||
}
|
||||
|
||||
else {
|
||||
const elementIndex = elementList.findIndex(item => item.elId === element.elId)
|
||||
if(elementIndex === 0) return null
|
||||
copyOfElementList.splice(elementIndex, 1)
|
||||
copyOfElementList.unshift(element)
|
||||
}
|
||||
|
||||
return copyOfElementList
|
||||
}
|
||||
|
||||
export const setElementOrder = (elementList: PPTElement[], element: PPTElement, command: ElementOrderCommand) => {
|
||||
let newElementList = null
|
||||
|
||||
if(command === ElementOrderCommands.UP) newElementList = moveUpElement(elementList, element)
|
||||
else if(command === ElementOrderCommands.DOWN) newElementList = moveDownElement(elementList, element)
|
||||
else if(command === ElementOrderCommands.TOP) newElementList = moveTopElement(elementList, element)
|
||||
else if(command === ElementOrderCommands.BOTTOM) newElementList = moveBottomElement(elementList, element)
|
||||
|
||||
return newElementList
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { PPTElement, PPTTextElement, PPTImageElement, PPTShapeElement, PPTIconElement } from '@/types/slides'
|
||||
import { PPTElement, PPTTextElement, PPTImageElement, PPTShapeElement } from '@/types/slides'
|
||||
|
||||
// 获取矩形旋转后在画布中的位置范围
|
||||
export const getRectRotatedRange = (element: PPTTextElement | PPTImageElement | PPTShapeElement | PPTIconElement) => {
|
||||
export const getRectRotatedRange = (element: PPTTextElement | PPTImageElement | PPTShapeElement) => {
|
||||
const { left, top, width, height, rotate = 0 } = element
|
||||
|
||||
const radius = Math.sqrt( Math.pow(width, 2) + Math.pow(height, 2) ) / 2
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PPTTextElement, PPTImageElement, PPTShapeElement, PPTIconElement } from '@/types/slides'
|
||||
import { PPTTextElement, PPTImageElement, PPTShapeElement } from '@/types/slides'
|
||||
import { OPERATE_KEYS } from '@/configs/element'
|
||||
|
||||
// 给定一个坐标,计算该坐标到(0, 0)点连线的弧度值
|
||||
|
@ -11,7 +11,7 @@ export const getAngleFromCoordinate = (x: number, y: number) => {
|
|||
}
|
||||
|
||||
// 计算元素被旋转一定角度后,八个操作点的新坐标
|
||||
export const getRotateElementPoints = (element: PPTTextElement | PPTImageElement | PPTShapeElement | PPTIconElement, angle: number) => {
|
||||
export const getRotateElementPoints = (element: PPTTextElement | PPTImageElement | PPTShapeElement, angle: number) => {
|
||||
const { left, top, width, height } = element
|
||||
|
||||
const radius = Math.sqrt( Math.pow(width, 2) + Math.pow(height, 2) ) / 2
|
||||
|
|
|
@ -45,9 +45,6 @@ export default defineComponent({
|
|||
const thumbnailsFocus = computed(() => store.state.thumbnailsFocus)
|
||||
const disableHotkeys = computed(() => store.state.disableHotkeys)
|
||||
|
||||
const save = () => {
|
||||
message.success('save')
|
||||
}
|
||||
const copy = () => {
|
||||
message.success('copy')
|
||||
}
|
||||
|
@ -89,11 +86,7 @@ export default defineComponent({
|
|||
if(shiftKey && !shiftKeyDown.value) shiftKeyDown.value = true
|
||||
|
||||
if(!editorAreaFocus.value && !thumbnailsFocus.value) return
|
||||
|
||||
if(ctrlKey && keyCode === KEYCODE.S) {
|
||||
e.preventDefault()
|
||||
save()
|
||||
}
|
||||
|
||||
if(ctrlKey && keyCode === KEYCODE.C) {
|
||||
e.preventDefault()
|
||||
copy()
|
||||
|
|
|
@ -23,6 +23,16 @@
|
|||
import { computed, defineComponent, PropType } from 'vue'
|
||||
import { PPTElement } from '@/types/slides'
|
||||
|
||||
import {
|
||||
ElementOrderCommand,
|
||||
ElementOrderCommands,
|
||||
ElementAlignCommand,
|
||||
ElementAlignCommands,
|
||||
ElementScaleHandler,
|
||||
ElementLockCommand,
|
||||
ElementLockCommands,
|
||||
} from '@/types/edit'
|
||||
|
||||
import ImageElement from './ImageElement.index.vue'
|
||||
import TextElement from './TextElement.index.vue'
|
||||
|
||||
|
@ -62,11 +72,11 @@ export default defineComponent({
|
|||
required: true,
|
||||
},
|
||||
scaleElement: {
|
||||
type: Function as PropType<(e: MouseEvent, element: PPTElement, direction: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9) => void>,
|
||||
type: Function as PropType<(e: MouseEvent, element: PPTElement, command: ElementScaleHandler) => void>,
|
||||
required: true,
|
||||
},
|
||||
updateZIndex: {
|
||||
type: Function as PropType<(element: PPTElement, operation: 'up' | 'down' | 'top' | 'bottom') => void>,
|
||||
orderElement: {
|
||||
type: Function as PropType<(element: PPTElement, command: ElementOrderCommand) => void>,
|
||||
required: true,
|
||||
},
|
||||
combineElements: {
|
||||
|
@ -78,7 +88,7 @@ export default defineComponent({
|
|||
required: true,
|
||||
},
|
||||
alignElement: {
|
||||
type: Function as PropType<(direction: 'top' | 'verticalCenter' | 'bottom' | 'left' | 'horizontalCenter' | 'right') => void>,
|
||||
type: Function as PropType<(command: ElementAlignCommand) => void>,
|
||||
required: true,
|
||||
},
|
||||
deleteElement: {
|
||||
|
@ -86,7 +96,7 @@ export default defineComponent({
|
|||
required: true,
|
||||
},
|
||||
lockElement: {
|
||||
type: Function as PropType<(element: PPTElement, handle: 'lock' | 'unlock') => void>,
|
||||
type: Function as PropType<(element: PPTElement, command: ElementLockCommand) => void>,
|
||||
required: true,
|
||||
},
|
||||
copyElement: {
|
||||
|
@ -112,7 +122,7 @@ export default defineComponent({
|
|||
return [{
|
||||
text: '解锁',
|
||||
icon: 'icon-unlock',
|
||||
action: () => props.lockElement(props.elementInfo, 'unlock'),
|
||||
action: () => props.lockElement(props.elementInfo, ElementLockCommands.UNLOCK),
|
||||
}]
|
||||
}
|
||||
|
||||
|
@ -135,29 +145,29 @@ export default defineComponent({
|
|||
icon: 'icon-top-layer',
|
||||
disable: props.isMultiSelect && !props.elementInfo.groupId,
|
||||
children: [
|
||||
{ text: '置顶层', action: () => props.updateZIndex(props.elementInfo, 'top') },
|
||||
{ text: '置底层', action: () => props.updateZIndex(props.elementInfo, 'bottom') },
|
||||
{ text: '置顶层', action: () => props.orderElement(props.elementInfo, ElementOrderCommands.TOP) },
|
||||
{ text: '置底层', action: () => props.orderElement(props.elementInfo, ElementOrderCommands.BOTTOM) },
|
||||
{ divider: true },
|
||||
{ text: '上移一层', action: () => props.updateZIndex(props.elementInfo, 'up') },
|
||||
{ text: '下移一层', action: () => props.updateZIndex(props.elementInfo, 'down') },
|
||||
{ text: '上移一层', action: () => props.orderElement(props.elementInfo, ElementOrderCommands.UP) },
|
||||
{ text: '下移一层', action: () => props.orderElement(props.elementInfo, ElementOrderCommands.DOWN) },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '水平对齐',
|
||||
icon: 'icon-align-left',
|
||||
children: [
|
||||
{ text: '水平居中', action: () => props.alignElement('horizontalCenter') },
|
||||
{ text: '左对齐', action: () => props.alignElement('left') },
|
||||
{ text: '右对齐', action: () => props.alignElement('right') },
|
||||
{ text: '水平居中', action: () => props.alignElement(ElementAlignCommands.HORIZONTAL) },
|
||||
{ text: '左对齐', action: () => props.alignElement(ElementAlignCommands.LEFT) },
|
||||
{ text: '右对齐', action: () => props.alignElement(ElementAlignCommands.RIGHT) },
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '垂直对齐',
|
||||
icon: 'icon-align-bottom',
|
||||
children: [
|
||||
{ text: '垂直居中', action: () => props.alignElement('verticalCenter') },
|
||||
{ text: '上对齐', action: () => props.alignElement('top') },
|
||||
{ text: '下对齐', action: () => props.alignElement('bottom') },
|
||||
{ text: '垂直居中', action: () => props.alignElement(ElementAlignCommands.VERTICAL) },
|
||||
{ text: '上对齐', action: () => props.alignElement(ElementAlignCommands.TOP) },
|
||||
{ text: '下对齐', action: () => props.alignElement(ElementAlignCommands.BOTTOM) },
|
||||
],
|
||||
},
|
||||
{ divider: true },
|
||||
|
@ -172,7 +182,7 @@ export default defineComponent({
|
|||
text: '锁定',
|
||||
subText: 'Ctrl + L',
|
||||
icon: 'icon-lock',
|
||||
action: () => props.lockElement(props.elementInfo, 'lock'),
|
||||
action: () => props.lockElement(props.elementInfo, ElementLockCommands.LOCK),
|
||||
},
|
||||
{
|
||||
text: '删除',
|
||||
|
|
Loading…
Reference in New Issue