From 8fb6e53342f99f7cdf1c1cd571ac6613f73e0cb9 Mon Sep 17 00:00:00 2001
From: pipipi-pikachu <1171051090@qq.com>
Date: Sun, 27 Dec 2020 19:34:21 +0800
Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=BA=BF=E6=9D=A1=E5=85=83?=
=?UTF-8?q?=E7=B4=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/hooks/useCreateElement.ts | 3 +-
src/mocks/index.ts | 12 ++
src/types/edit.ts | 9 +-
src/types/slides.ts | 52 ++++--
src/views/Editor/Canvas/EditableElement.vue | 2 +
.../Canvas/Operate/LineElementOperate.vue | 77 ++++++++
.../Editor/Canvas/Operate/ResizeHandler.vue | 2 +-
src/views/Editor/Canvas/Operate/index.vue | 9 +-
.../Editor/Canvas/hooks/useDragLineElement.ts | 169 ++++++++++++++++++
src/views/Editor/Canvas/index.vue | 4 +
src/views/Screen/ScreenElement.vue | 4 +
.../ThumbnailSlide/ThumbnailElement.vue | 2 +
.../element/LineElement/BaseLineElement.vue | 118 ++++++++++++
.../element/LineElement/LinePointMarker.vue | 66 +++++++
.../components/element/LineElement/index.vue | 142 +++++++++++++++
15 files changed, 651 insertions(+), 20 deletions(-)
create mode 100644 src/views/Editor/Canvas/Operate/LineElementOperate.vue
create mode 100644 src/views/Editor/Canvas/hooks/useDragLineElement.ts
create mode 100644 src/views/components/element/LineElement/BaseLineElement.vue
create mode 100644 src/views/components/element/LineElement/LinePointMarker.vue
create mode 100644 src/views/components/element/LineElement/index.vue
diff --git a/src/hooks/useCreateElement.ts b/src/hooks/useCreateElement.ts
index 58ecdc5c..8cf648c4 100644
--- a/src/hooks/useCreateElement.ts
+++ b/src/hooks/useCreateElement.ts
@@ -124,7 +124,7 @@ export default () => {
})
}
- const createLineElement = (position: LineElementPosition, points: [string, string], lineType: string) => {
+ const createLineElement = (position: LineElementPosition, points: [string, string]) => {
const { left, top, start, end } = position
createElement({
...DEFAULT_LINE,
@@ -135,7 +135,6 @@ export default () => {
start,
end,
points,
- lineType,
})
}
diff --git a/src/mocks/index.ts b/src/mocks/index.ts
index 4461eb83..306bb751 100644
--- a/src/mocks/index.ts
+++ b/src/mocks/index.ts
@@ -88,6 +88,18 @@ export const slides: Slide[] = [
lock: false,
content: '
๐ ๐ ๐ถ ๐ ๐ โญ โก ๐ฅ ๐ ๐ก ๐ฐ ๐ ๐ ๐ฅ ๐
๐ ๐ ๐ ๐ ๐ง โ ๐ข โ โฐ ๐ ๐งฉ ๐ต ๐ ๐ ๐ โณ ๐ ๐ ๐ฌ ๐
๐ ๐ ๐ ๐ ๐ฑ ๐ป ๐พ ๐ ๐ ๐ก ๐ข๐ง ๐ ๐งญ ๐ฐ ๐ณ ๐
',
},
+ {
+ id: 'xsfdas',
+ type: 'line',
+ width: 2,
+ left: 100,
+ top: 400,
+ end: [0, 0],
+ start: [300, 120],
+ style: 'solid',
+ color: '#888',
+ points: ['', 'arrow'],
+ },
{
id: 'xxx7',
type: 'shape',
diff --git a/src/types/edit.ts b/src/types/edit.ts
index 46f09068..90197ec1 100644
--- a/src/types/edit.ts
+++ b/src/types/edit.ts
@@ -27,7 +27,7 @@ export enum OperateBorderLines {
R = 'right',
}
-export type OperateResizeHandler = 'left-top' | 'top' | 'right-top' | 'left' | 'right' | 'left-bottom' | 'bottom' | 'right-bottom'
+export type OperateResizeHandler = '' | 'left-top' | 'top' | 'right-top' | 'left' | 'right' | 'left-bottom' | 'bottom' | 'right-bottom'
export enum OperateResizeHandlers {
LEFT_TOP = 'left-top',
@@ -40,6 +40,13 @@ export enum OperateResizeHandlers {
RIGHT_BOTTOM = 'right-bottom',
}
+export type OperateLineHandler = 'start' | 'end'
+
+export enum OperateLineHandlers {
+ START = 'start',
+ END = 'end,'
+}
+
export interface AlignmentLineAxis {
x: number;
y: number;
diff --git a/src/types/slides.ts b/src/types/slides.ts
index 206f3c0d..470c0c6a 100644
--- a/src/types/slides.ts
+++ b/src/types/slides.ts
@@ -14,22 +14,19 @@ export enum ElementTypes {
TABLE = 'table',
}
-export interface PPTElementBaseProps {
- id: string;
- left: number;
- top: number;
- lock?: boolean;
- groupId?: string;
-}
-
export interface PPTElementOutline {
style?: 'dashed' | 'solid';
width?: number;
color?: string;
}
-export interface PPTTextElement extends PPTElementBaseProps {
+export interface PPTTextElement {
type: 'text';
+ id: string;
+ left: number;
+ top: number;
+ lock?: boolean;
+ groupId?: string;
width: number;
height: number;
content: string;
@@ -49,8 +46,13 @@ export interface ImageElementFilters {
'hue-rotate': string;
'opacity': string;
}
-export interface PPTImageElement extends PPTElementBaseProps {
+export interface PPTImageElement {
type: 'image';
+ id: string;
+ left: number;
+ top: number;
+ lock?: boolean;
+ groupId?: string;
width: number;
height: number;
fixedRatio: boolean;
@@ -66,8 +68,13 @@ export interface PPTImageElement extends PPTElementBaseProps {
shadow?: PPTElementShadow;
}
-export interface PPTShapeElement extends PPTElementBaseProps {
+export interface PPTShapeElement {
type: 'shape';
+ id: string;
+ left: number;
+ top: number;
+ lock?: boolean;
+ groupId?: string;
width: number;
height: number;
viewBox: number;
@@ -80,19 +87,29 @@ export interface PPTShapeElement extends PPTElementBaseProps {
shadow?: PPTElementShadow;
}
-export interface PPTLineElement extends PPTElementBaseProps {
+export interface PPTLineElement {
type: 'line';
+ id: string;
+ left: number;
+ top: number;
+ lock?: boolean;
+ groupId?: string;
start: [number, number];
end: [number, number];
width: number;
style: string;
color: string;
points: [string, string];
- lineType: string;
+ shadow?: PPTElementShadow;
}
-export interface PPTChartElement extends PPTElementBaseProps {
+export interface PPTChartElement {
type: 'chart';
+ id: string;
+ left: number;
+ top: number;
+ lock?: boolean;
+ groupId?: string;
width: number;
height: number;
chartType: string;
@@ -107,8 +124,13 @@ export interface TableElementCell {
content: string;
bgColor: string;
}
-export interface PPTTableElement extends PPTElementBaseProps {
+export interface PPTTableElement {
type: 'table';
+ id: string;
+ left: number;
+ top: number;
+ lock?: boolean;
+ groupId?: string;
width: number;
height: number;
borderTheme?: string;
diff --git a/src/views/Editor/Canvas/EditableElement.vue b/src/views/Editor/Canvas/EditableElement.vue
index a18ee526..0e09ada2 100644
--- a/src/views/Editor/Canvas/EditableElement.vue
+++ b/src/views/Editor/Canvas/EditableElement.vue
@@ -31,6 +31,7 @@ import { ElementOrderCommands, ElementAlignCommands } from '@/types/edit'
import ImageElement from '@/views/components/element/ImageElement/index.vue'
import TextElement from '@/views/components/element/TextElement/index.vue'
import ShapeElement from '@/views/components/element/ShapeElement/index.vue'
+import LineElement from '@/views/components/element/LineElement/index.vue'
export default defineComponent({
name: 'editable-element',
@@ -58,6 +59,7 @@ export default defineComponent({
'image': ImageElement,
'text': TextElement,
'shape': ShapeElement,
+ 'line': LineElement,
}
return elementTypeMap[props.elementInfo.type] || null
})
diff --git a/src/views/Editor/Canvas/Operate/LineElementOperate.vue b/src/views/Editor/Canvas/Operate/LineElementOperate.vue
new file mode 100644
index 00000000..d477c307
--- /dev/null
+++ b/src/views/Editor/Canvas/Operate/LineElementOperate.vue
@@ -0,0 +1,77 @@
+
+
+
+ dragLineElement($event, elementInfo, point.handler)"
+ />
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/Editor/Canvas/Operate/ResizeHandler.vue b/src/views/Editor/Canvas/Operate/ResizeHandler.vue
index b3b9f99e..d665df34 100644
--- a/src/views/Editor/Canvas/Operate/ResizeHandler.vue
+++ b/src/views/Editor/Canvas/Operate/ResizeHandler.vue
@@ -11,7 +11,7 @@ export default {
props: {
type: {
type: String as PropType,
- required: true,
+ default: '',
},
},
}
diff --git a/src/views/Editor/Canvas/Operate/index.vue b/src/views/Editor/Canvas/Operate/index.vue
index 5bef9e08..9179763d 100644
--- a/src/views/Editor/Canvas/Operate/index.vue
+++ b/src/views/Editor/Canvas/Operate/index.vue
@@ -17,6 +17,7 @@
:isMultiSelect="isMultiSelect"
:rotateElement="rotateElement"
:scaleElement="scaleElement"
+ :dragLineElement="dragLineElement"
>
void>,
required: true,
},
+ dragLineElement: {
+ type: Function as PropType<(e: MouseEvent, element: PPTElement, command: OperateLineHandler) => void>,
+ required: true,
+ },
},
setup(props) {
const store = useStore
()
@@ -82,6 +88,7 @@ export default defineComponent({
'image': ImageElementOperate,
'text': TextElementOperate,
'shape': ShapeElementOperate,
+ 'line': LineElementOperate,
}
return elementTypeMap[props.elementInfo.type] || null
})
diff --git a/src/views/Editor/Canvas/hooks/useDragLineElement.ts b/src/views/Editor/Canvas/hooks/useDragLineElement.ts
new file mode 100644
index 00000000..7d4bb51f
--- /dev/null
+++ b/src/views/Editor/Canvas/hooks/useDragLineElement.ts
@@ -0,0 +1,169 @@
+import { Ref, computed } from 'vue'
+import { useStore } from 'vuex'
+import { State, MutationTypes } from '@/store'
+import { PPTElement, PPTLineElement } from '@/types/slides'
+import { OperateLineHandler, OperateLineHandlers } from '@/types/edit'
+import useHistorySnapshot from '@/hooks/useHistorySnapshot'
+
+interface AdsorptionPoint {
+ x: number;
+ y: number;
+}
+
+export default (elementList: Ref) => {
+ const store = useStore()
+ const canvasScale = computed(() => store.state.canvasScale)
+
+ const { addHistorySnapshot } = useHistorySnapshot()
+
+ const dragLineElement = (e: MouseEvent, element: PPTLineElement, command: OperateLineHandler) => {
+ let isMouseDown = true
+
+ const sorptionRange = 10
+
+ const startPageX = e.pageX
+ const startPageY = e.pageY
+
+ const adsorptionPoints: AdsorptionPoint[] = []
+
+ // ่ทๅๅ
จ้จ้็บฟๆกไธๆชๆ่ฝฌๅ
็ด ็8ไธช็นไฝไธบๅธ้็น
+ for(let i = 0; i < elementList.value.length; i++) {
+ const _element = elementList.value[i]
+ if(_element.type === 'line' || ('rotate' in _element && _element.rotate)) continue
+
+ const left = _element.left
+ const top = _element.top
+ const width = _element.width
+ const height = _element.height
+
+ const right = left + width
+ const bottom = top + height
+ const centerX = top + height / 2
+ const centerY = left + width / 2
+
+ const topPoint = { x: centerY, y: top }
+ const bottomPoint = { x: centerY, y: bottom }
+ const leftPoint = { x: left, y: centerX }
+ const rightPoint = { x: right, y: centerX }
+
+ const leftTopPoint = { x: left, y: top }
+ const rightTopPoint = { x: right, y: top }
+ const leftBottomPoint = { x: left, y: bottom }
+ const rightBottomPoint = { x: right, y: bottom }
+
+ adsorptionPoints.push(
+ topPoint,
+ bottomPoint,
+ leftPoint,
+ rightPoint,
+ leftTopPoint,
+ rightTopPoint,
+ leftBottomPoint,
+ rightBottomPoint,
+ )
+ }
+
+ document.onmousemove = e => {
+ if(!isMouseDown) return
+
+ const currentPageX = e.pageX
+ const currentPageY = e.pageY
+
+ // ้ผ ๆ ๆไธๅ็งปๅจ็่ท็ฆป
+ const moveX = (currentPageX - startPageX) / canvasScale.value
+ const moveY = (currentPageY - startPageY) / canvasScale.value
+
+ // ็บฟๆกไธคไธช็ซฏ็น๏ผ่ตท็นๅ็ป็น๏ผๅบไบ็ผ่พๅบๅ็ไฝ็ฝฎ
+ let startX = element.left + element.start[0]
+ let startY = element.top + element.start[1]
+ let endX = element.left + element.end[0]
+ let endY = element.top + element.end[1]
+
+ // ๆ นๆฎๆๆฝ็็น๏ผ้ๆฉไฟฎๆน่ตท็นๆ็ป็น็ไฝ็ฝฎ
+ // ไธค็นๅจๆฐดๅนณๅๅ็ดๆนๅไธๆๅฏน้ฝๅธ้
+ // ้ ่ฟๅ
ถไปๅ
็ด ็ๅธ้็นๆๅฏน้ฝๅธ้
+ if(command === OperateLineHandlers.START) {
+ startX = startX + moveX
+ startY = startY + moveY
+
+ if(Math.abs(startX - endX) < sorptionRange) startX = endX
+ if(Math.abs(startY - endY) < sorptionRange) startY = endY
+
+ for(const adsorptionPoint of adsorptionPoints) {
+ const { x, y } = adsorptionPoint
+ if(Math.abs(x - startX) < sorptionRange && Math.abs(y - startY) < sorptionRange) {
+ startX = x
+ startY = y
+ break
+ }
+ }
+ }
+ else {
+ endX = endX + moveX
+ endY = endY + moveY
+
+ if(Math.abs(startX - endX) < sorptionRange) endX = startX
+ if(Math.abs(startY - endY) < sorptionRange) endY = startY
+
+ for(const adsorptionPoint of adsorptionPoints) {
+ const { x, y } = adsorptionPoint
+ if(Math.abs(x - endX) < sorptionRange && Math.abs(y - endY) < sorptionRange) {
+ endX = x
+ endY = y
+ break
+ }
+ }
+ }
+
+ // ่ฎก็ฎไธคไธช็ซฏ็นๅบไบ่ช่บซๅ
็ด ไฝ็ฝฎ็ๅๆ
+ const minX = Math.min(startX, endX)
+ const minY = Math.min(startY, endY)
+ const maxX = Math.max(startX, endX)
+ const maxY = Math.max(startY, endY)
+
+ const start: [number, number] = [0, 0]
+ const end: [number, number] = [maxX - minX, maxY - minY]
+ if(startX > endX) {
+ start[0] = maxX - minX
+ end[0] = 0
+ }
+ if(startY > endY) {
+ start[1] = maxY - minY
+ end[1] = 0
+ }
+
+ // ไฟฎๆน็บฟๆก็ไฝ็ฝฎๅไธค็น็ๅๆ
+ elementList.value = elementList.value.map(el => {
+ if(el.id === element.id) {
+ return {
+ ...el,
+ left: minX,
+ top: minY,
+ start: start,
+ end: end,
+ }
+ }
+ return el
+ })
+ }
+
+ document.onmouseup = e => {
+ isMouseDown = false
+ document.onmousemove = null
+ document.onmouseup = null
+
+ const currentPageX = e.pageX
+ const currentPageY = e.pageY
+
+ // ๅฏนๆฏๅๅง้ผ ๆ ไฝ็ฝฎ๏ผๆฒกๆๅฎ้
็ไฝ็งปไธๆดๆฐๆฐๆฎ
+ if(startPageX === currentPageX && startPageY === currentPageY) return
+
+ store.commit(MutationTypes.UPDATE_SLIDE, { elements: elementList.value })
+ addHistorySnapshot()
+ }
+ }
+
+ return {
+ dragLineElement,
+ }
+}
\ No newline at end of file
diff --git a/src/views/Editor/Canvas/index.vue b/src/views/Editor/Canvas/index.vue
index 67eedfb0..72625561 100644
--- a/src/views/Editor/Canvas/index.vue
+++ b/src/views/Editor/Canvas/index.vue
@@ -39,6 +39,7 @@
:isMultiSelect="activeElementIdList.length > 1"
:rotateElement="rotateElement"
:scaleElement="scaleElement"
+ :dragLineElement="dragLineElement"
/>
@@ -85,6 +86,7 @@ import useRotateElement from './hooks/useRotateElement'
import useScaleElement from './hooks/useScaleElement'
import useSelectElement from './hooks/useSelectElement'
import useDragElement from './hooks/useDragElement'
+import useDragLineElement from './hooks/useDragLineElement'
import useDeleteElement from '@/hooks/useDeleteElement'
import useCopyAndPasteElement from '@/hooks/useCopyAndPasteElement'
@@ -140,6 +142,7 @@ export default defineComponent({
const { mouseSelectionState, updateMouseSelection } = useMouseSelection(elementList, viewportRef)
const { dragElement } = useDragElement(elementList, activeGroupElementId, alignmentLines)
+ const { dragLineElement } = useDragLineElement(elementList)
const { selectElement } = useSelectElement(elementList, activeGroupElementId, dragElement)
const { scaleElement, scaleMultiElement } = useScaleElement(elementList, activeGroupElementId, alignmentLines)
const { rotateElement } = useRotateElement(elementList, viewportRef)
@@ -218,6 +221,7 @@ export default defineComponent({
selectElement,
rotateElement,
scaleElement,
+ dragLineElement,
scaleMultiElement,
mousewheelScaleCanvas,
contextmenus,
diff --git a/src/views/Screen/ScreenElement.vue b/src/views/Screen/ScreenElement.vue
index 2b1c315a..700ccaf9 100644
--- a/src/views/Screen/ScreenElement.vue
+++ b/src/views/Screen/ScreenElement.vue
@@ -21,6 +21,8 @@ import { PPTElement, Slide } from '@/types/slides'
import BaseImageElement from '@/views/components/element/ImageElement/BaseImageElement.vue'
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
+import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
+import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
export default defineComponent({
name: 'screen-element',
@@ -43,6 +45,8 @@ export default defineComponent({
const elementTypeMap = {
'image': BaseImageElement,
'text': BaseTextElement,
+ 'shape': BaseShapeElement,
+ 'line': BaseLineElement,
}
return elementTypeMap[props.elementInfo.type] || null
})
diff --git a/src/views/components/ThumbnailSlide/ThumbnailElement.vue b/src/views/components/ThumbnailSlide/ThumbnailElement.vue
index a0fe516c..4c345ded 100644
--- a/src/views/components/ThumbnailSlide/ThumbnailElement.vue
+++ b/src/views/components/ThumbnailSlide/ThumbnailElement.vue
@@ -17,6 +17,7 @@ import { PPTElement } from '@/types/slides'
import BaseImageElement from '@/views/components/element/ImageElement/BaseImageElement.vue'
import BaseTextElement from '@/views/components/element/TextElement/BaseTextElement.vue'
import BaseShapeElement from '@/views/components/element/ShapeElement/BaseShapeElement.vue'
+import BaseLineElement from '@/views/components/element/LineElement/BaseLineElement.vue'
export default defineComponent({
name: 'base-element',
@@ -36,6 +37,7 @@ export default defineComponent({
'image': BaseImageElement,
'text': BaseTextElement,
'shape': BaseShapeElement,
+ 'line': BaseLineElement,
}
return elementTypeMap[props.elementInfo.type] || null
})
diff --git a/src/views/components/element/LineElement/BaseLineElement.vue b/src/views/components/element/LineElement/BaseLineElement.vue
new file mode 100644
index 00000000..1d75e260
--- /dev/null
+++ b/src/views/components/element/LineElement/BaseLineElement.vue
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
diff --git a/src/views/components/element/LineElement/LinePointMarker.vue b/src/views/components/element/LineElement/LinePointMarker.vue
new file mode 100644
index 00000000..1a74fea9
--- /dev/null
+++ b/src/views/components/element/LineElement/LinePointMarker.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/components/element/LineElement/index.vue b/src/views/components/element/LineElement/index.vue
new file mode 100644
index 00000000..cd44eaae
--- /dev/null
+++ b/src/views/components/element/LineElement/index.vue
@@ -0,0 +1,142 @@
+
+ handleSelectElement($event)"
+ >
+
+
+
+
+
+
+