From 9fdbf35fa13939bdf885bb7ae5a22a3db445374a Mon Sep 17 00:00:00 2001 From: pipipi-pikachu <1171051090@qq.com> Date: Sun, 3 Jan 2021 17:26:02 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=80=89=E6=8B=A9=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E6=8F=92=E5=85=A5=E5=85=83=E7=B4=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/FileInput.vue | 4 +- src/configs/element.ts | 8 -- src/configs/lines.ts | 17 +-- src/configs/shapes.ts | 127 ++++++++++++++++++ src/hooks/useCreateElement.ts | 14 +- src/store/constants.ts | 2 +- src/store/index.ts | 5 +- src/store/mutations.ts | 5 +- src/types/edit.ts | 18 ++- src/types/slides.ts | 2 + .../Editor/Canvas/ElementCreateSelection.vue | 19 +-- .../hooks/useInsertFromCreateSelection.ts | 92 +++++++++++++ src/views/Editor/Canvas/index.vue | 17 ++- src/views/Editor/CanvasTool/LinePool.vue | 106 +++++++++++++++ src/views/Editor/CanvasTool/ShapePool.vue | 88 ++++++++++++ src/views/Editor/CanvasTool/index.vue | 49 ++++++- 16 files changed, 520 insertions(+), 53 deletions(-) create mode 100644 src/configs/shapes.ts create mode 100644 src/views/Editor/Canvas/hooks/useInsertFromCreateSelection.ts create mode 100644 src/views/Editor/CanvasTool/LinePool.vue create mode 100644 src/views/Editor/CanvasTool/ShapePool.vue diff --git a/src/components/FileInput.vue b/src/components/FileInput.vue index 3e72d61..f0eb92c 100644 --- a/src/components/FileInput.vue +++ b/src/components/FileInput.vue @@ -2,7 +2,7 @@
\ No newline at end of file diff --git a/src/configs/element.ts b/src/configs/element.ts index 266582a..4bb01e6 100644 --- a/src/configs/element.ts +++ b/src/configs/element.ts @@ -1,13 +1,6 @@ const DEFAULT_COLOR = '#41464b' export const DEFAULT_TEXT = { - left: 0, - top: 0, - width: 300, - height: 0, - opacity: 1, - lineHeight: 1.5, - segmentSpacing: 5, content: '请输入内容', } @@ -24,7 +17,6 @@ export const DEFAULT_SHAPE = { export const DEFAULT_LINE = { style: 'solid', - points: ['', ''], width: 4, color: DEFAULT_COLOR, } diff --git a/src/configs/lines.ts b/src/configs/lines.ts index 5ae86f7..fb6eff0 100644 --- a/src/configs/lines.ts +++ b/src/configs/lines.ts @@ -1,12 +1,13 @@ -export const LINES = [ +export interface LinePoolItem { + path: string; + style: string; + points: [string, string]; +} + +export const LINE_LIST = [ { path: 'M0,0 L20,20', style: 'solid', points: ['', ''] }, - { path: 'M0,0 L20,20', style: 'solid', points: ['', 'arrow'] }, - { path: 'M0,0 L20,20', style: 'solid', points: ['arrow', 'arrow'] }, - { path: 'M0,0 L20,20', style: 'solid', points: ['', 'cusp'] }, - { path: 'M0,0 L20,20', style: 'solid', points: ['cusp', 'cusp'] }, - { path: 'M0,0 L20,20', style: 'solid', points: ['', 'dot'] }, - { path: 'M0,0 L20,20', style: 'solid', points: ['dot', 'dot'] }, { path: 'M0,0 L20,20', style: 'dashed', points: ['', ''] }, + { path: 'M0,0 L20,20', style: 'solid', points: ['', 'arrow'] }, { path: 'M0,0 L20,20', style: 'dashed', points: ['', 'arrow'] }, - { path: 'M0,0 L20,20', style: 'dashed', points: ['arrow', 'arrow'] }, + { path: 'M0,0 L20,20', style: 'solid', points: ['', 'dot'] }, ] \ No newline at end of file diff --git a/src/configs/shapes.ts b/src/configs/shapes.ts new file mode 100644 index 0000000..b2ad4be --- /dev/null +++ b/src/configs/shapes.ts @@ -0,0 +1,127 @@ +export interface ShapePoolItem { + viewBox: number; + path: string; +} + +export const SHAPE_LIST = [ + { + viewBox: 200, + path: 'M 0 0 L 200 0 L 200 200 L 0 200 Z' + }, + { + viewBox: 200, + path: 'M 0 200 L 0 0 L 150 0 L 200 50 L 200 200 L 0 200' + }, + { + viewBox: 200, + path: 'M 0 150 L 0 0 L 150 0 L 200 50 L 200 200 L 50 200 L 0 150' + }, + { + viewBox: 200, + path: 'M 20 0 L 180 0 Q 200 0 200 20 L 200 180 Q 200 200 180 200 L 20 200 Q 0 200 0 180 L 0 20 Q 0 0 20 0 ' + }, + { + viewBox: 200, + path: 'M 0 0 L 140 0 Q 200 0 200 60 L 200 200 L 60 200 Q 0 200 0 140 L 0 0 Z' + }, + { + viewBox: 200, + path: 'M 0 0 L 140 0 Q 200 0 200 60 L 200 200 L 0 200 L 0 0 Z' + }, + { + viewBox: 200, + path: 'M 100 0 A 50 50 0 1 1 100 200 A 50 50 0 1 1 100 0 Z' + }, + { + viewBox: 200, + path: 'M 200 0 Q 0 0 0 200 L 200 200 L 200 0' + }, + { + viewBox: 200, + path: 'M 100 0 A 100 100 0 1 1 0 100 L 100 100 L 100 0 Z' + }, + { + viewBox: 200, + path: 'M 100 0 L 0 200 L 200 200 L 100 0 Z' + }, + { + viewBox: 200, + path: 'M 0 0 L 0 200 L 200 200 Z' + }, + { + viewBox: 200, + path: 'M 50 0 L 200 0 L 150 200 L 0 200 L 50 0 Z' + }, + { + viewBox: 200, + path: 'M 100 0 L 0 100 L 100 200 L 200 100 L 100 0 Z' + }, + { + viewBox: 200, + path: 'M 50 0 L 150 0 L 200 200 L 0 200 L 50 0 Z' + }, + { + viewBox: 200, + path: 'M 100 0 L 0 90 L 50 200 L 150 200 L 200 90 L 100 0 Z' + }, + { + viewBox: 200, + path: 'M 100 0 L 0 60 L 0 140 L 100 200 L 200 140 L 200 60 L 100 0 Z' + }, + { + viewBox: 200, + path: 'M 60 0 L 140 0 L 200 60 L 200 140 L 140 200 L 60 200 L 0 140 L 0 60 L 60 0 Z' + }, + { + viewBox: 200, + path: 'M 100 0 A 100 100 0 1 1 0 100 L 0 0 L 100 0 Z' + }, + { + viewBox: 200, + path: 'M 100 0 A 50 50 0 1 0 200 120 A 100 100 0 1 1 100 0' + }, + { + viewBox: 200, + path: 'M 100 0 L 122 70 L 196 70 L 136 114 L 158 182 L 100 140 L 42 182 L 64 114 L 4 70 L 78 70 Z' + }, + { + viewBox: 200, + path: 'M 100 0 L 0 100 L 50 100 L 50 200 L 150 200 L 150 100 L 200 100 L 100 0 Z' + }, + { + viewBox: 200, + path: 'M 100 200 L 200 100 L 150 100 L 150 0 L 50 0 L 50 100 L 0 100 L 100 200 Z' + }, + { + viewBox: 200, + path: 'M 0 100 L 100 0 L 100 50 L 200 50 L 200 150 L 100 150 L 100 200 L 0 100 Z' + }, + { + viewBox: 200, + path: 'M 200 100 L 100 0 L 100 50 L 0 50 L 0 150 L 100 150 L 100 200 L 200 100 Z' + }, + { + viewBox: 200, + path: 'M 0 0 L 120 0 L 200 100 L 120 200 L 0 200 L 80 100 L 0 0 Z' + }, + { + viewBox: 200, + path: 'M 80 0 L 200 0 L 120 100 L 200 200 L 80 200 L 0 100 L 80 0 Z' + }, + { + viewBox: 200, + path: 'M 0 0 L 140 0 L 200 100 L 140 200 L 0 200 L 0 100 L 0 0 Z' + }, + { + viewBox: 200, + path: 'M 60 0 L 200 0 L 200 100 L 200 200 L 60 200 L 0 100 L 60 0 Z' + }, + { + viewBox: 200, + path: 'M 70 0 L 70 70 L 0 70 L 0 130 L 70 130 L 70 200 L 130 200 L 130 130 L 200 130 L 200 70 L 130 70 L 130 0 L 70 0 Z' + }, + { + viewBox: 200, + path: 'M 40 0 L 0 40 L 60 100 L 0 160 L 40 200 L 100 140 L 160 200 L 200 160 L 140 100 L 200 40 L 160 0 L 100 60 L 40 0 Z' + }, +] \ No newline at end of file diff --git a/src/hooks/useCreateElement.ts b/src/hooks/useCreateElement.ts index 8cf648c..788aefe 100644 --- a/src/hooks/useCreateElement.ts +++ b/src/hooks/useCreateElement.ts @@ -4,6 +4,8 @@ import { createRandomCode } from '@/utils/common' import { getImageSize } from '@/utils/image' import { VIEWPORT_SIZE, VIEWPORT_ASPECT_RATIO } from '@/configs/canvas' import { PPTElement, TableElementCell } from '@/types/slides' +import { ShapePoolItem } from '@/configs/shapes' +import { LinePoolItem } from '@/configs/lines' import { DEFAULT_IMAGE, DEFAULT_TEXT, @@ -33,6 +35,8 @@ 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]) @@ -109,7 +113,7 @@ export default () => { }) } - const createShapeElement = (position: CommonElementPosition, path: string, viewBox: number) => { + const createShapeElement = (position: CommonElementPosition, data: ShapePoolItem) => { const { left, top, width, height } = position createElement({ ...DEFAULT_SHAPE, @@ -119,12 +123,12 @@ export default () => { top, width, height, - viewBox, - path, + viewBox: data.viewBox, + path: data.path, }) } - const createLineElement = (position: LineElementPosition, points: [string, string]) => { + const createLineElement = (position: LineElementPosition, data: LinePoolItem) => { const { left, top, start, end } = position createElement({ ...DEFAULT_LINE, @@ -134,7 +138,7 @@ export default () => { top, start, end, - points, + points: data.points, }) } diff --git a/src/store/constants.ts b/src/store/constants.ts index 644d508..d8b6796 100644 --- a/src/store/constants.ts +++ b/src/store/constants.ts @@ -9,7 +9,7 @@ export enum MutationTypes { SET_EDITORAREA_FOCUS = 'setEditorAreaFocus', SET_DISABLE_HOTKEYS_STATE = 'setDisableHotkeysState', SET_GRID_LINES_STATE = 'setGridLinesState', - SET_CREATING_ELEMENT_TYPE = 'setCreatingElementType', + SET_CREATING_ELEMENT = 'setCreatingElement', SET_AVAILABLE_FONTS = 'setAvailableFonts', SET_TOOLBAR_STATE = 'setToolbarState', diff --git a/src/store/index.ts b/src/store/index.ts index b5586f1..4a5372f 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -5,6 +5,7 @@ import { mutations } from './mutations' import { MutationTypes, ActionTypes } from './constants' import { Slide } from '@/types/slides' +import { CreatingElement } from '@/types/edit' import { ToolbarState } from '@/types/toolbar' import { slides } from '@/mocks/index' import { FontName } from '@/configs/fontName' @@ -20,7 +21,7 @@ export interface State { editorAreaFocus: boolean; disableHotkeys: boolean; showGridLines: boolean; - creatingElementType: string; + creatingElement: CreatingElement | null; availableFonts: FontName[]; toolbarState: ToolbarState; slides: Slide[]; @@ -41,7 +42,7 @@ const state: State = { editorAreaFocus: false, disableHotkeys: false, showGridLines: false, - creatingElementType: '', + creatingElement: null, availableFonts: [], toolbarState: 'slideStyle', slides: slides, diff --git a/src/store/mutations.ts b/src/store/mutations.ts index 5de1174..7ced1de 100644 --- a/src/store/mutations.ts +++ b/src/store/mutations.ts @@ -2,6 +2,7 @@ import { MutationTree } from 'vuex' import { MutationTypes } from './constants' import { State } from './index' import { Slide, PPTElement } from '@/types/slides' +import { CreatingElement } from '@/types/edit' import { FONT_NAMES } from '@/configs/fontName' import { isSupportFontFamily } from '@/utils/fontFamily' @@ -49,8 +50,8 @@ export const mutations: MutationTree = { state.showGridLines = show }, - [MutationTypes.SET_CREATING_ELEMENT_TYPE](state, type: string) { - state.creatingElementType = type + [MutationTypes.SET_CREATING_ELEMENT](state, element: CreatingElement | null) { + state.creatingElement = element }, [MutationTypes.SET_AVAILABLE_FONTS](state) { diff --git a/src/types/edit.ts b/src/types/edit.ts index 096aaca..29c074f 100644 --- a/src/types/edit.ts +++ b/src/types/edit.ts @@ -1,3 +1,6 @@ +import { ShapePoolItem } from '@/configs/shapes' +import { LinePoolItem } from '@/configs/lines' + export type ElementOrderCommand = 'up' | 'down' | 'top' | 'bottom' export enum ElementOrderCommands { @@ -85,4 +88,17 @@ export interface ImageClipedEmitData { export interface CreateElementSelectionData { start: [number, number]; end: [number, number]; -} \ No newline at end of file +} + +export interface CreatingTextElement { + type: 'text'; +} +export interface CreatingShapeElement { + type: 'shape'; + data: ShapePoolItem; +} +export interface CreatingLineElement { + type: 'line'; + data: LinePoolItem; +} +export type CreatingElement = CreatingTextElement | CreatingShapeElement | CreatingLineElement \ No newline at end of file diff --git a/src/types/slides.ts b/src/types/slides.ts index 470c0c6..76ed095 100644 --- a/src/types/slides.ts +++ b/src/types/slides.ts @@ -33,6 +33,8 @@ export interface PPTTextElement { rotate?: number; outline?: PPTElementOutline; fill?: string; + lineHeight?: number; + wordSpace?: number; opacity?: number; shadow?: PPTElementShadow; } diff --git a/src/views/Editor/Canvas/ElementCreateSelection.vue b/src/views/Editor/Canvas/ElementCreateSelection.vue index ed882ec..def3b4b 100644 --- a/src/views/Editor/Canvas/ElementCreateSelection.vue +++ b/src/views/Editor/Canvas/ElementCreateSelection.vue @@ -4,11 +4,11 @@ ref="selectionRef" @mousedown.stop="$event => createSelection($event)" > -
+
() const ctrlOrShiftKeyActive: Ref = computed(() => store.getters.ctrlOrShiftKeyActive) - const elementType = computed(() => store.state.creatingElementType) + const creatingElement = computed(() => store.state.creatingElement) const start = ref<[number, number] | null>(null) const end = ref<[number, number] | null>(null) @@ -67,7 +67,7 @@ export default defineComponent({ start.value = [startPageX, startPageY] document.onmousemove = e => { - if(!isMouseDown) return + if(!creatingElement.value || !isMouseDown) return let currentPageX = e.pageX let currentPageY = e.pageY @@ -79,7 +79,7 @@ export default defineComponent({ const absX = Math.abs(moveX) const absY = Math.abs(moveY) - if(elementType.value === 'shape') { + if(creatingElement.value.type === 'shape') { // moveX和moveY一正一负 const isOpposite = (moveY > 0 && moveX < 0) || (moveY < 0 && moveX > 0) @@ -91,7 +91,7 @@ export default defineComponent({ } } - else if(elementType.value === 'line') { + else if(creatingElement.value.type === 'line') { if(absX > absY) currentPageY = startPageY else currentPageX = startPageX } @@ -115,13 +115,14 @@ export default defineComponent({ start: start.value, end: end.value, }) - store.commit(MutationTypes.SET_CREATING_ELEMENT_TYPE, '') } + else store.commit(MutationTypes.SET_CREATING_ELEMENT, null) } } const lineData = computed(() => { - if(!start.value || !end.value || elementType.value !== 'line') return null + if(!start.value || !end.value) return null + if(!creatingElement.value || creatingElement.value.type !== 'line') return null const [_startX, _startY] = start.value const [_endX, _endY] = end.value @@ -176,7 +177,7 @@ export default defineComponent({ selectionRef, start, end, - elementType, + creatingElement, createSelection, lineData, position, diff --git a/src/views/Editor/Canvas/hooks/useInsertFromCreateSelection.ts b/src/views/Editor/Canvas/hooks/useInsertFromCreateSelection.ts new file mode 100644 index 0000000..2db29d2 --- /dev/null +++ b/src/views/Editor/Canvas/hooks/useInsertFromCreateSelection.ts @@ -0,0 +1,92 @@ +import { computed, Ref } from 'vue' +import { useStore } from 'vuex' +import { MutationTypes, State } from '@/store' +import { CreateElementSelectionData, CreatingLineElement, CreatingShapeElement } from '@/types/edit' +import useCreateElement from '@/hooks/useCreateElement' + +export default (viewportRef: Ref) => { + const store = useStore() + const canvasScale = computed(() => store.state.canvasScale) + const creatingElement = computed(() => store.state.creatingElement) + + const formatCreateSelection = (selectionData: CreateElementSelectionData) => { + const { start, end } = selectionData + + if(!viewportRef.value) return + const viewportRect = viewportRef.value.getBoundingClientRect() + + const [startX, startY] = start + const [endX, endY] = end + const minX = Math.min(startX, endX) + const maxX = Math.max(startX, endX) + const minY = Math.min(startY, endY) + const maxY = Math.max(startY, endY) + + const left = (minX - viewportRect.x) / canvasScale.value + const top = (minY - viewportRect.y) / canvasScale.value + const width = (maxX - minX) / canvasScale.value + const height = (maxY - minY) / canvasScale.value + + return { left, top, width, height } + } + + const formatCreateSelectionForLine = (selectionData: CreateElementSelectionData) => { + const { start, end } = selectionData + + if(!viewportRef.value) return + const viewportRect = viewportRef.value.getBoundingClientRect() + + const [startX, startY] = start + const [endX, endY] = end + const minX = Math.min(startX, endX) + const maxX = Math.max(startX, endX) + const minY = Math.min(startY, endY) + const maxY = Math.max(startY, endY) + + const left = (minX - viewportRect.x) / canvasScale.value + const top = (minY - viewportRect.y) / canvasScale.value + const width = (maxX - minX) / canvasScale.value + const height = (maxY - minY) / canvasScale.value + + const _start: [number, number] = [ + startX === minX ? 0 : width, + startY === minY ? 0 : height, + ] + const _end: [number, number] = [ + endX === minX ? 0 : width, + endY === minY ? 0 : height, + ] + + return { + left, + top, + start: _start, + end: _end, + } + } + + const { createTextElement, createShapeElement, createLineElement } = useCreateElement() + + const insertElementFromCreateSelection = (selectionData: CreateElementSelectionData) => { + if(!creatingElement.value) return + + const type = creatingElement.value.type + if(type === 'text') { + const position = formatCreateSelection(selectionData) + position && createTextElement(position) + } + else if(type === 'shape') { + const position = formatCreateSelection(selectionData) + position && createShapeElement(position, (creatingElement.value as CreatingShapeElement).data) + } + else if(type === 'line') { + const position = formatCreateSelectionForLine(selectionData) + position && createLineElement(position, (creatingElement.value as CreatingLineElement).data) + } + store.commit(MutationTypes.SET_CREATING_ELEMENT, null) + } + + return { + insertElementFromCreateSelection, + } +} \ No newline at end of file diff --git a/src/views/Editor/Canvas/index.vue b/src/views/Editor/Canvas/index.vue index e0eba62..44637e4 100644 --- a/src/views/Editor/Canvas/index.vue +++ b/src/views/Editor/Canvas/index.vue @@ -8,8 +8,8 @@ v-click-outside="removeEditorAreaFocus" >
store.state.creatingElementType) - const createElement = (data: CreateElementSelectionData) => { - console.log(data) - } + const creatingElement = computed(() => store.state.creatingElement) + const { insertElementFromCreateSelection } = useInsertFromCreateSelection(viewportRef) const contextmenus = (): ContextmenuItem[] => { return [ @@ -230,8 +229,8 @@ export default defineComponent({ removeEditorAreaFocus, currentSlide, isShowGridLines, - creatingElementType, - createElement, + creatingElement, + insertElementFromCreateSelection, alignmentLines, selectElement, rotateElement, diff --git a/src/views/Editor/CanvasTool/LinePool.vue b/src/views/Editor/CanvasTool/LinePool.vue new file mode 100644 index 0000000..c64de9f --- /dev/null +++ b/src/views/Editor/CanvasTool/LinePool.vue @@ -0,0 +1,106 @@ + + + + + \ No newline at end of file diff --git a/src/views/Editor/CanvasTool/ShapePool.vue b/src/views/Editor/CanvasTool/ShapePool.vue new file mode 100644 index 0000000..c8865c1 --- /dev/null +++ b/src/views/Editor/CanvasTool/ShapePool.vue @@ -0,0 +1,88 @@ + + + + + \ No newline at end of file diff --git a/src/views/Editor/CanvasTool/index.vue b/src/views/Editor/CanvasTool/index.vue index 754f49e..8cb38a6 100644 --- a/src/views/Editor/CanvasTool/index.vue +++ b/src/views/Editor/CanvasTool/index.vue @@ -6,12 +6,22 @@
- + - - + + + + + + + +
@@ -29,11 +39,16 @@ import { defineComponent, computed } from 'vue' import { useStore } from 'vuex' import { MutationTypes, State } from '@/store' import { getImageDataURL } from '@/utils/image' +import { ShapePoolItem } from '@/configs/shapes' +import { LinePoolItem } from '@/configs/lines' import useScaleCanvas from '@/hooks/useScaleCanvas' import useHistorySnapshot from '@/hooks/useHistorySnapshot' import useCreateElement from '@/hooks/useCreateElement' +import ShapePool from './ShapePool.vue' +import LinePool from './LinePool.vue' import FileInput from '@/components/FileInput.vue' +import { Popover } from 'ant-design-vue' import { UndoOutlined, RedoOutlined, @@ -50,6 +65,8 @@ import { export default defineComponent({ name: 'canvas-tool', components: { + ShapePool, + LinePool, FileInput, UndoOutlined, RedoOutlined, @@ -61,6 +78,7 @@ export default defineComponent({ PieChartOutlined, MinusOutlined, PlusOutlined, + Popover, }, setup() { const store = useStore() @@ -81,8 +99,25 @@ export default defineComponent({ getImageDataURL(imageFile).then(dataURL => createImageElement(dataURL)) } - const createElement = (type: string) => { - store.commit(MutationTypes.SET_CREATING_ELEMENT_TYPE, type) + const drawText = () => { + store.commit(MutationTypes.SET_CREATING_ELEMENT, { + type: 'text', + data: null, + }) + } + + const drawShape = (shape: ShapePoolItem) => { + store.commit(MutationTypes.SET_CREATING_ELEMENT, { + type: 'shape', + data: shape, + }) + } + + const drawLine = (line: LinePoolItem) => { + store.commit(MutationTypes.SET_CREATING_ELEMENT, { + type: 'line', + data: line, + }) } return { @@ -93,7 +128,9 @@ export default defineComponent({ redo, undo, insertImageElement, - createElement, + drawText, + drawShape, + drawLine, } }, })