feat: 形状编辑支持多个控制点(#228)

This commit is contained in:
zxc 2024-07-21 22:55:55 +08:00
parent dad2295f04
commit f2b25336f4
11 changed files with 861 additions and 698 deletions

View File

@ -6,8 +6,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="基于 Vue3.x + TypeScript 的在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能实现在线PPT的编辑、演示。支持导出PPT文件。" /> <meta name="description" content="PPTist基于 Vue3.x + TypeScript 的在线演示文稿(幻灯片)应用,还原了大部分 Office PowerPoint 常用功能实现在线PPT的编辑、演示。支持导出PPT文件。" />
<meta name="keywords" content="ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,Vue3,TypeScript" /> <meta name="keywords" content="pptist,ppt,powerpoint,office powerpoint,在线ppt,幻灯片,演示文稿,ppt在线制作,Vue3,TypeScript" />
<title>PPTist - 在线演示文稿</title> <title>PPTist - 在线演示文稿</title>
<style> <style>

1203
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@
"number-precision": "^1.6.0", "number-precision": "^1.6.0",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"pptxgenjs": "^3.12.0", "pptxgenjs": "^3.12.0",
"pptxtojson": "^0.1.4", "pptxtojson": "^0.1.7",
"prosemirror-commands": "^1.5.2", "prosemirror-commands": "^1.5.2",
"prosemirror-dropcursor": "^1.8.1", "prosemirror-dropcursor": "^1.8.1",
"prosemirror-gapcursor": "^1.3.2", "prosemirror-gapcursor": "^1.3.2",
@ -44,7 +44,7 @@
"svg-pathdata": "^6.0.3", "svg-pathdata": "^6.0.3",
"tinycolor2": "^1.6.0", "tinycolor2": "^1.6.0",
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",
"vue": "^3.3.11", "vue": "^3.4.33",
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
@ -67,7 +67,7 @@
"npm-run-all2": "^6.1.1", "npm-run-all2": "^6.1.1",
"sass": "^1.69.6", "sass": "^1.69.6",
"typescript": "~5.3.0", "typescript": "~5.3.0",
"vite": "^5.0.10", "vite": "^5.3.4",
"vue-tsc": "^1.8.25" "vue-tsc": "^2.0.26"
} }
} }

View File

@ -19,206 +19,226 @@ interface ShapeListItem {
children: ShapePoolItem[] children: ShapePoolItem[]
} }
export const SHAPE_PATH_FORMULAS = { export interface ShapePathFormula {
editable?: boolean
defaultValue?: number[]
range?: [number, number][]
relative?: string[]
getBaseSize?: ((width: number, height: number) => number)[]
formula: (width: number, height: number, values?: number[]) => string
}
export const SHAPE_PATH_FORMULAS: {
[key: string]: ShapePathFormula
} = {
[ShapePathFormulasKeys.ROUND_RECT]: { [ShapePathFormulasKeys.ROUND_RECT]: {
editable: true, editable: true,
defaultValue: 0.125, defaultValue: [0.125],
range: [0, 0.5], range: [[0, 0.5]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const radius = Math.min(width, height) * value const radius = Math.min(width, height) * values![0]
return `M ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height - radius} Q ${width} ${height} ${width - radius} ${height} L ${radius} ${height} Q 0 ${height} 0 ${height - radius} L 0 ${radius} Q 0 0 ${radius} 0 Z` return `M ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height - radius} Q ${width} ${height} ${width - radius} ${height} L ${radius} ${height} Q 0 ${height} 0 ${height - radius} L 0 ${radius} Q 0 0 ${radius} 0 Z`
} }
}, },
[ShapePathFormulasKeys.CUT_RECT_DIAGONAL]: { [ShapePathFormulasKeys.CUT_RECT_DIAGONAL]: {
editable: true, editable: true,
defaultValue: 0.2, defaultValue: [0.2],
range: [0, 0.9], range: [[0, 0.9]],
relative: 'right', relative: ['right'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const radius = Math.min(width, height) * value const radius = Math.min(width, height) * values![0]
return `M 0 ${height - radius} L 0 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L ${radius} ${height} Z` return `M 0 ${height - radius} L 0 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L ${radius} ${height} Z`
} }
}, },
[ShapePathFormulasKeys.CUT_RECT_SINGLE]: { [ShapePathFormulasKeys.CUT_RECT_SINGLE]: {
editable: true, editable: true,
defaultValue: 0.2, defaultValue: [0.2],
range: [0, 0.9], range: [[0, 0.9]],
relative: 'right', relative: ['right'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const radius = Math.min(width, height) * value const radius = Math.min(width, height) * values![0]
return `M 0 ${height} L 0 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} Z` return `M 0 ${height} L 0 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} Z`
} }
}, },
[ShapePathFormulasKeys.CUT_RECT_SAMESIDE]: { [ShapePathFormulasKeys.CUT_RECT_SAMESIDE]: {
editable: true, editable: true,
defaultValue: 0.2, defaultValue: [0.2],
range: [0, 0.5], range: [[0, 0.5]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const radius = Math.min(width, height) * value const radius = Math.min(width, height) * values![0]
return `M 0 ${radius} L ${radius} 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L 0 ${height} Z` return `M 0 ${radius} L ${radius} 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L 0 ${height} Z`
} }
}, },
[ShapePathFormulasKeys.ROUND_RECT_DIAGONAL]: { [ShapePathFormulasKeys.ROUND_RECT_DIAGONAL]: {
editable: true, editable: true,
defaultValue: 0.125, defaultValue: [0.125],
range: [0, 1], range: [[0, 1]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const radius = Math.min(width, height) * value const radius = Math.min(width, height) * values![0]
return `M ${radius} 0 L ${width} 0 L ${width} ${height - radius} Q ${width} ${height} ${width - radius} ${height} L 0 ${height} L 0 ${radius} Q 0 0 ${radius} 0 Z` return `M ${radius} 0 L ${width} 0 L ${width} ${height - radius} Q ${width} ${height} ${width - radius} ${height} L 0 ${height} L 0 ${radius} Q 0 0 ${radius} 0 Z`
} }
}, },
[ShapePathFormulasKeys.ROUND_RECT_SINGLE]: { [ShapePathFormulasKeys.ROUND_RECT_SINGLE]: {
editable: true, editable: true,
defaultValue: 0.125, defaultValue: [0.125],
range: [0, 1], range: [[0, 1]],
relative: 'right', relative: ['right'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const radius = Math.min(width, height) * value const radius = Math.min(width, height) * values![0]
return `M 0 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height} L 0 ${height} L 0 0 Z` return `M 0 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height} L 0 ${height} L 0 0 Z`
} }
}, },
[ShapePathFormulasKeys.ROUND_RECT_SAMESIDE]: { [ShapePathFormulasKeys.ROUND_RECT_SAMESIDE]: {
editable: true, editable: true,
defaultValue: 0.125, defaultValue: [0.125],
range: [0, 0.5], range: [[0, 0.5]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const radius = Math.min(width, height) * value const radius = Math.min(width, height) * values![0]
return `M 0 ${radius} Q 0 0 ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height} L 0 ${height} Z` return `M 0 ${radius} Q 0 0 ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height} L 0 ${height} Z`
} }
}, },
[ShapePathFormulasKeys.CUT_ROUND_RECT]: { [ShapePathFormulasKeys.CUT_ROUND_RECT]: {
editable: true, editable: true,
defaultValue: 0.125, defaultValue: [0.125],
range: [0, 0.5], range: [[0, 0.5]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const radius = Math.min(width, height) * value const radius = Math.min(width, height) * values![0]
return `M ${radius} 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L 0 ${height} L 0 ${radius} Q 0 0 ${radius} 0 Z` return `M ${radius} 0 L ${width - radius} 0 L ${width} ${radius} L ${width} ${height} L 0 ${height} L 0 ${radius} Q 0 0 ${radius} 0 Z`
} }
}, },
[ShapePathFormulasKeys.MESSAGE]: { [ShapePathFormulasKeys.MESSAGE]: {
formula: (width: number, height: number) => { editable: true,
range: [[0, 0.8], [0.1, 0.3]],
defaultValue: [0.3, 0.2],
relative: ['left_bottom', 'bottom'],
getBaseSize: [
width => width,
(width, height) => height,
],
formula: (width, height, values) => {
const point = width * values![0]
const arrowWidth = width * 0.2 const arrowWidth = width * 0.2
const arrowheight = height * 0.2 const arrowheight = height * values![1]
return `M 0 0 L ${width} 0 L ${width} ${height - arrowheight} L ${width / 2} ${height - arrowheight} L ${width / 2 - arrowWidth} ${height} L ${width / 2 - arrowWidth} ${height - arrowheight} L 0 ${height - arrowheight} Z` return `M 0 0 L ${width} 0 L ${width} ${height - arrowheight} L ${point + arrowWidth} ${height - arrowheight} L ${point} ${height} L ${point} ${height - arrowheight} L 0 ${height - arrowheight} Z`
} }
}, },
[ShapePathFormulasKeys.ROUND_MESSAGE]: { [ShapePathFormulasKeys.ROUND_MESSAGE]: {
formula: (width: number, height: number) => { formula: (width, height) => {
const radius = Math.min(width, height) * 0.125 const radius = Math.min(width, height) * 0.125
const arrowWidth = width * 0.2 const arrowWidth = Math.min(width, height) * 0.2
const arrowheight = height * 0.2 const arrowheight = Math.min(width, height) * 0.2
return `M 0 ${radius} Q 0 0 ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height - radius - arrowheight} Q ${width} ${height - arrowheight} ${width - radius} ${height - arrowheight} L ${width / 2} ${height - arrowheight} L ${width / 2 - arrowWidth} ${height} L ${width / 2 - arrowWidth} ${height - arrowheight} L ${radius} ${height - arrowheight} Q 0 ${height - arrowheight} 0 ${height - radius - arrowheight} L 0 ${radius} Z` return `M 0 ${radius} Q 0 0 ${radius} 0 L ${width - radius} 0 Q ${width} 0 ${width} ${radius} L ${width} ${height - radius - arrowheight} Q ${width} ${height - arrowheight} ${width - radius} ${height - arrowheight} L ${width / 2} ${height - arrowheight} L ${width / 2 - arrowWidth} ${height} L ${width / 2 - arrowWidth} ${height - arrowheight} L ${radius} ${height - arrowheight} Q 0 ${height - arrowheight} 0 ${height - radius - arrowheight} L 0 ${radius} Z`
} }
}, },
[ShapePathFormulasKeys.L]: { [ShapePathFormulasKeys.L]: {
editable: true, editable: true,
defaultValue: 0.25, defaultValue: [0.25],
range: [0.1, 0.9], range: [[0.1, 0.9]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const lineWidth = Math.min(width, height) * value const lineWidth = Math.min(width, height) * values![0]
return `M 0 0 L 0 ${height} L ${width} ${height} L ${width} ${height - lineWidth} L ${lineWidth} ${height - lineWidth} L ${lineWidth} 0 Z` return `M 0 0 L 0 ${height} L ${width} ${height} L ${width} ${height - lineWidth} L ${lineWidth} ${height - lineWidth} L ${lineWidth} 0 Z`
} }
}, },
[ShapePathFormulasKeys.RING_RECT]: { [ShapePathFormulasKeys.RING_RECT]: {
editable: true, editable: true,
defaultValue: 0.25, defaultValue: [0.25],
range: [0.1, 0.45], range: [[0.1, 0.45]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const lineWidth = Math.min(width, height) * value const lineWidth = Math.min(width, height) * values![0]
return `M 0 0 ${width} 0 ${width} ${height} L 0 ${height} L 0 0 Z M ${lineWidth} ${lineWidth} L ${lineWidth} ${height - lineWidth} L ${width - lineWidth} ${height - lineWidth} L ${width - lineWidth} ${lineWidth} Z` return `M 0 0 ${width} 0 ${width} ${height} L 0 ${height} L 0 0 Z M ${lineWidth} ${lineWidth} L ${lineWidth} ${height - lineWidth} L ${width - lineWidth} ${height - lineWidth} L ${width - lineWidth} ${lineWidth} Z`
} }
}, },
[ShapePathFormulasKeys.PLUS]: { [ShapePathFormulasKeys.PLUS]: {
editable: true, editable: true,
defaultValue: 0.25, defaultValue: [0.25],
range: [0.1, 0.9], range: [[0.1, 0.9]],
relative: 'center', relative: ['center'],
getBaseSize: (width: number, height: number) => Math.min(width, height), getBaseSize: [(width, height) => Math.min(width, height)],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const lineWidth = Math.min(width, height) * value const lineWidth = Math.min(width, height) * values![0]
return `M ${width / 2 - lineWidth / 2} 0 L ${width / 2 - lineWidth / 2} ${height / 2 - lineWidth / 2} L 0 ${height / 2 - lineWidth / 2} L 0 ${height / 2 + lineWidth / 2} L ${width / 2 - lineWidth / 2} ${height / 2 + lineWidth / 2} L ${width / 2 - lineWidth / 2} ${height} L ${width / 2 + lineWidth / 2} ${height} L ${width / 2 + lineWidth / 2} ${height / 2 + lineWidth / 2} L ${width} ${height / 2 + lineWidth / 2} L ${width} ${height / 2 - lineWidth / 2} L ${width / 2 + lineWidth / 2} ${height / 2 - lineWidth / 2} L ${width / 2 + lineWidth / 2} 0 Z` return `M ${width / 2 - lineWidth / 2} 0 L ${width / 2 - lineWidth / 2} ${height / 2 - lineWidth / 2} L 0 ${height / 2 - lineWidth / 2} L 0 ${height / 2 + lineWidth / 2} L ${width / 2 - lineWidth / 2} ${height / 2 + lineWidth / 2} L ${width / 2 - lineWidth / 2} ${height} L ${width / 2 + lineWidth / 2} ${height} L ${width / 2 + lineWidth / 2} ${height / 2 + lineWidth / 2} L ${width} ${height / 2 + lineWidth / 2} L ${width} ${height / 2 - lineWidth / 2} L ${width / 2 + lineWidth / 2} ${height / 2 - lineWidth / 2} L ${width / 2 + lineWidth / 2} 0 Z`
} }
}, },
[ShapePathFormulasKeys.TRIANGLE]: { [ShapePathFormulasKeys.TRIANGLE]: {
editable: true, editable: true,
defaultValue: 0.5, defaultValue: [0.5],
range: [0, 1], range: [[0, 1]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number) => width, getBaseSize: [width => width],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const vertex = width * value const vertex = width * values![0]
return `M ${vertex} 0 L 0 ${height} L ${width} ${height} Z` return `M ${vertex} 0 L 0 ${height} L ${width} ${height} Z`
} }
}, },
[ShapePathFormulasKeys.PARALLELOGRAM_LEFT]: { [ShapePathFormulasKeys.PARALLELOGRAM_LEFT]: {
editable: true, editable: true,
defaultValue: 0.25, defaultValue: [0.25],
range: [0, 0.9], range: [[0, 0.9]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number) => width, getBaseSize: [width => width],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const point = width * value const point = width * values![0]
return `M ${point} 0 L ${width} 0 L ${width - point} ${height} L 0 ${height} Z` return `M ${point} 0 L ${width} 0 L ${width - point} ${height} L 0 ${height} Z`
} }
}, },
[ShapePathFormulasKeys.PARALLELOGRAM_RIGHT]: { [ShapePathFormulasKeys.PARALLELOGRAM_RIGHT]: {
editable: true, editable: true,
defaultValue: 0.25, defaultValue: [0.25],
range: [0, 0.9], range: [[0, 0.9]],
relative: 'right', relative: ['right'],
getBaseSize: (width: number) => width, getBaseSize: [width => width],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const point = width * value const point = width * values![0]
return `M 0 0 L ${width - point} 0 L ${width} ${height} L ${point} ${height} Z` return `M 0 0 L ${width - point} 0 L ${width} ${height} L ${point} ${height} Z`
} }
}, },
[ShapePathFormulasKeys.TRAPEZOID]: { [ShapePathFormulasKeys.TRAPEZOID]: {
editable: true, editable: true,
defaultValue: 0.25, defaultValue: [0.25],
range: [0, 0.5], range: [[0, 0.5]],
relative: 'left', relative: ['left'],
getBaseSize: (width: number) => width, getBaseSize: [width => width],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const point = width * value const point = width * values![0]
return `M ${point} 0 L ${width - point} 0 L ${width} ${height} L 0 ${height} Z` return `M ${point} 0 L ${width - point} 0 L ${width} ${height} L 0 ${height} Z`
} }
}, },
[ShapePathFormulasKeys.BULLET]: { [ShapePathFormulasKeys.BULLET]: {
editable: true, editable: true,
defaultValue: 0.2, defaultValue: [0.2],
range: [0, 1], range: [[0, 1]],
relative: 'top', relative: ['top'],
getBaseSize: (width: number, height: number) => height, getBaseSize: [(width, height) => height],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const point = height * value const point = height * values![0]
return `M ${width / 2} 0 L 0 ${point} L 0 ${height} L ${width} ${height} L ${width} ${point} Z` return `M ${width / 2} 0 L 0 ${point} L 0 ${height} L ${width} ${height} L ${width} ${point} Z`
} }
}, },
[ShapePathFormulasKeys.INDICATOR]: { [ShapePathFormulasKeys.INDICATOR]: {
editable: true, editable: true,
defaultValue: 0.2, defaultValue: [0.2],
range: [0, 0.9], range: [[0, 0.9]],
relative: 'right', relative: ['right'],
getBaseSize: (width: number) => width, getBaseSize: [width => width],
formula: (width: number, height: number, value: number) => { formula: (width, height, values) => {
const point = width * value const point = width * values![0]
return `M ${width} ${height / 2} L ${width - point} 0 L 0 0 L ${point} ${height / 2} L 0 ${height} L ${width - point} ${height} Z` return `M ${width} ${height / 2} L ${width - point} 0 L 0 0 L ${point} ${height / 2} L 0 ${height} L ${width - point} ${height} Z`
} }
}, },

View File

@ -235,9 +235,9 @@ export default () => {
newElement.viewBox = [width, height] newElement.viewBox = [width, height]
const pathFormula = SHAPE_PATH_FORMULAS[data.pathFormula] const pathFormula = SHAPE_PATH_FORMULAS[data.pathFormula]
if ('editable' in pathFormula) { if ('editable' in pathFormula && pathFormula.editable) {
newElement.path = pathFormula.formula(width, height, pathFormula.defaultValue) newElement.path = pathFormula.formula(width, height, pathFormula.defaultValue!)
newElement.keypoint = pathFormula.defaultValue newElement.keypoints = pathFormula.defaultValue
} }
else newElement.path = pathFormula.formula(width, height) else newElement.path = pathFormula.formula(width, height)
} }

View File

@ -319,7 +319,7 @@ export interface ShapeText {
* viewBox viewBox path * viewBox viewBox path
* viewBox path * viewBox path
* *
* keypoint?: 关键点位置百分比 * keypoints?: 关键点位置百分比
*/ */
export interface PPTShapeElement extends PPTBaseElement { export interface PPTShapeElement extends PPTBaseElement {
type: 'shape' type: 'shape'
@ -336,7 +336,7 @@ export interface PPTShapeElement extends PPTBaseElement {
special?: boolean special?: boolean
text?: ShapeText text?: ShapeText
pathFormula?: ShapePathFormulasKeys pathFormula?: ShapePathFormulasKeys
keypoint?: number keypoints?: number[]
} }

View File

@ -24,9 +24,10 @@
/> />
<div <div
class="operate-keypoint-handler" class="operate-keypoint-handler"
v-if="elementInfo.keypoint !== undefined" v-for="(keypoint, index) in keypoints"
:style="keypointStyle" :key="index"
@mousedown.stop="$event => moveShapeKeypoint($event, elementInfo)" :style="keypoint.styles"
@mousedown.stop="$event => moveShapeKeypoint($event, elementInfo, index)"
></div> ></div>
</template> </template>
</div> </div>
@ -39,7 +40,7 @@ export default {
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue' import { computed, type CSSProperties } from 'vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { useMainStore } from '@/store' import { useMainStore } from '@/store'
import type { PPTShapeElement } from '@/types/slides' import type { PPTShapeElement } from '@/types/slides'
@ -56,7 +57,7 @@ const props = defineProps<{
handlerVisible: boolean handlerVisible: boolean
rotateElement: (e: MouseEvent, element: PPTShapeElement) => void rotateElement: (e: MouseEvent, element: PPTShapeElement) => void
scaleElement: (e: MouseEvent, element: PPTShapeElement, command: OperateResizeHandlers) => void scaleElement: (e: MouseEvent, element: PPTShapeElement, command: OperateResizeHandlers) => void
moveShapeKeypoint: (e: MouseEvent, element: PPTShapeElement) => void moveShapeKeypoint: (e: MouseEvent, element: PPTShapeElement, index: number) => void
}>() }>()
const { canvasScale } = storeToRefs(useMainStore()) const { canvasScale } = storeToRefs(useMainStore())
@ -65,19 +66,31 @@ const scaleWidth = computed(() => props.elementInfo.width * canvasScale.value)
const scaleHeight = computed(() => props.elementInfo.height * canvasScale.value) const scaleHeight = computed(() => props.elementInfo.height * canvasScale.value)
const { resizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight) const { resizeHandlers, borderLines } = useCommonOperate(scaleWidth, scaleHeight)
const keypointStyle = computed(() => { const keypoints = computed(() => {
if (!props.elementInfo.pathFormula || props.elementInfo.keypoint === undefined) return {} if (!props.elementInfo.pathFormula || props.elementInfo.keypoints === undefined) return []
const pathFormula = SHAPE_PATH_FORMULAS[props.elementInfo.pathFormula] const pathFormula = SHAPE_PATH_FORMULAS[props.elementInfo.pathFormula]
if ('editable' in pathFormula) {
const keypointPos = pathFormula.getBaseSize(props.elementInfo.width, props.elementInfo.height) * props.elementInfo.keypoint return props.elementInfo.keypoints.map((keypoint, index) => {
if (pathFormula.relative === 'left') return { left: keypointPos * canvasScale.value + 'px' } const getBaseSize = pathFormula.getBaseSize![index]
if (pathFormula.relative === 'right') return { left: (props.elementInfo.width - keypointPos) * canvasScale.value + 'px' } const relative = pathFormula.relative![index]
if (pathFormula.relative === 'center') return { left: (props.elementInfo.width - keypointPos) / 2 * canvasScale.value + 'px' } const keypointPos = getBaseSize(props.elementInfo.width, props.elementInfo.height) * keypoint
if (pathFormula.relative === 'top') return { top: keypointPos * canvasScale.value + 'px' }
if (pathFormula.relative === 'bottom') return { top: (props.elementInfo.height - keypointPos) * canvasScale.value + 'px' } let styles: CSSProperties = {}
} if (relative === 'left') styles = { left: keypointPos * canvasScale.value + 'px' }
return {} else if (relative === 'right') styles = { left: (props.elementInfo.width - keypointPos) * canvasScale.value + 'px' }
else if (relative === 'center') styles = { left: (props.elementInfo.width - keypointPos) / 2 * canvasScale.value + 'px' }
else if (relative === 'top') styles = { top: keypointPos * canvasScale.value + 'px' }
else if (relative === 'bottom') styles = { top: (props.elementInfo.height - keypointPos) * canvasScale.value + 'px' }
else if (relative === 'left_bottom') styles = { left: keypointPos * canvasScale.value + 'px', top: props.elementInfo.height * canvasScale.value + 'px' }
else if (relative === 'right_bottom') styles = { left: (props.elementInfo.width - keypointPos) * canvasScale.value + 'px', top: props.elementInfo.height * canvasScale.value + 'px' }
else if (relative === 'top_right') styles = { left: props.elementInfo.width * canvasScale.value + 'px', top: keypointPos * canvasScale.value + 'px' }
else if (relative === 'bottom_right') styles = { left: props.elementInfo.width * canvasScale.value + 'px', top: (props.elementInfo.height - keypointPos) * canvasScale.value + 'px' }
return {
keypoint,
styles,
}
})
}) })
</script> </script>

View File

@ -69,7 +69,7 @@ const props = defineProps<{
rotateElement: (e: MouseEvent, element: Exclude<PPTElement, PPTChartElement | PPTLineElement | PPTVideoElement | PPTAudioElement>) => void rotateElement: (e: MouseEvent, element: Exclude<PPTElement, PPTChartElement | PPTLineElement | PPTVideoElement | PPTAudioElement>) => void
scaleElement: (e: MouseEvent, element: Exclude<PPTElement, PPTLineElement>, command: OperateResizeHandlers) => void scaleElement: (e: MouseEvent, element: Exclude<PPTElement, PPTLineElement>, command: OperateResizeHandlers) => void
dragLineElement: (e: MouseEvent, element: PPTLineElement, command: OperateLineHandlers) => void dragLineElement: (e: MouseEvent, element: PPTLineElement, command: OperateLineHandlers) => void
moveShapeKeypoint: (e: MouseEvent, element: PPTShapeElement) => void moveShapeKeypoint: (e: MouseEvent, element: PPTShapeElement, index: number) => void
openLinkDialog: () => void openLinkDialog: () => void
}>() }>()

View File

@ -20,7 +20,7 @@ export default (
const { addHistorySnapshot } = useHistorySnapshot() const { addHistorySnapshot } = useHistorySnapshot()
const moveShapeKeypoint = (e: MouseEvent | TouchEvent, element: PPTShapeElement) => { const moveShapeKeypoint = (e: MouseEvent | TouchEvent, element: PPTShapeElement, index = 0) => {
const isTouchEvent = !(e instanceof MouseEvent) const isTouchEvent = !(e instanceof MouseEvent)
if (isTouchEvent && (!e.changedTouches || !e.changedTouches[0])) return if (isTouchEvent && (!e.changedTouches || !e.changedTouches[0])) return
@ -29,13 +29,19 @@ export default (
const startPageX = isTouchEvent ? e.changedTouches[0].pageX : e.pageX const startPageX = isTouchEvent ? e.changedTouches[0].pageX : e.pageX
const startPageY = isTouchEvent ? e.changedTouches[0].pageY : e.pageY const startPageY = isTouchEvent ? e.changedTouches[0].pageY : e.pageY
const originKeypoints = element.keypoints!
const pathFormula = SHAPE_PATH_FORMULAS[element.pathFormula!] const pathFormula = SHAPE_PATH_FORMULAS[element.pathFormula!]
let shapePathData: ShapePathData | null = null let shapePathData: ShapePathData | null = null
if ('editable' in pathFormula) { if ('editable' in pathFormula && pathFormula.editable) {
const baseSize = pathFormula.getBaseSize(element.width, element.height) const getBaseSize = pathFormula.getBaseSize![index]
const originPos = baseSize * element.keypoint! const range = pathFormula.range![index]
const [min, max] = pathFormula.range const relative = pathFormula.relative![index]
const relative = pathFormula.relative const keypoint = originKeypoints[index]
const baseSize = getBaseSize(element.width, element.height)
const originPos = baseSize * keypoint
const [min, max] = range
shapePathData = { baseSize, originPos, min, max, relative } shapePathData = { baseSize, originPos, min, max, relative }
} }
@ -55,19 +61,30 @@ export default (
let keypoint = 0 let keypoint = 0
if (relative === 'left') keypoint = (originPos + moveX) / baseSize
if (relative === 'right') keypoint = (originPos - moveX) / baseSize
if (relative === 'center') keypoint = (originPos - moveX * 2) / baseSize if (relative === 'center') keypoint = (originPos - moveX * 2) / baseSize
if (relative === 'top') keypoint = (originPos + moveY) / baseSize else if (relative === 'left') keypoint = (originPos + moveX) / baseSize
if (relative === 'bottom') keypoint = (originPos - moveY) / baseSize else if (relative === 'right') keypoint = (originPos - moveX) / baseSize
else if (relative === 'top') keypoint = (originPos + moveY) / baseSize
else if (relative === 'bottom') keypoint = (originPos - moveY) / baseSize
else if (relative === 'left_bottom') keypoint = (originPos + moveX) / baseSize
else if (relative === 'right_bottom') keypoint = (originPos - moveX) / baseSize
else if (relative === 'top_right') keypoint = (originPos + moveY) / baseSize
else if (relative === 'bottom_right') keypoint = (originPos - moveY) / baseSize
if (keypoint < min) keypoint = min if (keypoint < min) keypoint = min
if (keypoint > max) keypoint = max if (keypoint > max) keypoint = max
let keypoints: number[] = []
if (Array.isArray(originKeypoints)) {
keypoints = [...originKeypoints]
keypoints[index] = keypoint
}
else keypoints = [keypoint]
return { return {
...el, ...el,
keypoint, keypoints,
path: pathFormula.formula(shapeElement.width, shapeElement.height, keypoint), path: pathFormula.formula(shapeElement.width, shapeElement.height, keypoints),
} }
} }
return el return el

View File

@ -405,7 +405,7 @@ export default (
const pathFormula = SHAPE_PATH_FORMULAS[el.pathFormula] const pathFormula = SHAPE_PATH_FORMULAS[el.pathFormula]
let path = '' let path = ''
if ('editable' in pathFormula) path = pathFormula.formula(width, height, el.keypoint!) if ('editable' in pathFormula) path = pathFormula.formula(width, height, el.keypoints!)
else path = pathFormula.formula(width, height) else path = pathFormula.formula(width, height)
return { return {

View File

@ -224,13 +224,13 @@ const changeShape = (shape: ShapePoolItem) => {
const pathFormula = SHAPE_PATH_FORMULAS[shape.pathFormula] const pathFormula = SHAPE_PATH_FORMULAS[shape.pathFormula]
if ('editable' in pathFormula) { if ('editable' in pathFormula) {
props.path = pathFormula.formula(width, height, pathFormula.defaultValue) props.path = pathFormula.formula(width, height, pathFormula.defaultValue)
props.keypoint = pathFormula.defaultValue props.keypoints = pathFormula.defaultValue
} }
else props.path = pathFormula.formula(width, height) else props.path = pathFormula.formula(width, height)
} }
else { else {
props.pathFormula = undefined props.pathFormula = undefined
props.keypoint = undefined props.keypoints = undefined
} }
updateElement(props) updateElement(props)
} }