feat: 支持形状格式刷

This commit is contained in:
pipipi-pikachu 2023-10-11 22:43:27 +08:00
parent 697e9a2300
commit 60e4fbd4d0
8 changed files with 83 additions and 12 deletions

View File

@ -115,7 +115,8 @@ npm run serve
- 阴影
- 透明度
- 翻转
- 编辑文字
- 形状格式刷
- 编辑文字(支持富文本,与文字元素的富文本编辑功能近似)
#### 线条
- 颜色
- 宽度

View File

@ -0,0 +1,27 @@
import { storeToRefs } from 'pinia'
import { useMainStore } from '@/store'
import type { PPTShapeElement } from '@/types/slides'
export default () => {
const mainStore = useMainStore()
const { shapeFormatPainter, handleElement } = storeToRefs(mainStore)
const toggleShapeFormatPainter = () => {
const _handleElement = handleElement.value as PPTShapeElement
if (shapeFormatPainter.value) mainStore.setShapeFormatPainter(null)
else {
mainStore.setShapeFormatPainter({
fill: _handleElement.fill,
gradient: _handleElement.gradient,
outline: _handleElement.outline,
opacity: _handleElement.opacity,
shadow: _handleElement.shadow,
})
}
}
return {
toggleShapeFormatPainter,
}
}

View File

@ -5,7 +5,7 @@ export default () => {
const mainStore = useMainStore()
const { richTextAttrs, textFormatPainter } = storeToRefs(mainStore)
const toggleFormatPainter = () => {
const toggleTextFormatPainter = () => {
if (textFormatPainter.value) mainStore.setTextFormatPainter(null)
else {
mainStore.setTextFormatPainter({
@ -23,6 +23,6 @@ export default () => {
}
return {
toggleFormatPainter,
toggleTextFormatPainter,
}
}

View File

@ -1,7 +1,7 @@
import { customAlphabet } from 'nanoid'
import { defineStore } from 'pinia'
import { ToolbarStates } from '@/types/toolbar'
import type { CreatingElement, TextFormatPainter } from '@/types/edit'
import type { CreatingElement, ShapeFormatPainter, TextFormatPainter } from '@/types/edit'
import type { DialogForExportTypes } from '@/types/export'
import { type TextAttrs, defaultRichTextAttrs } from '@/utils/prosemirror/utils'
import { SYS_FONTS } from '@/configs/font'
@ -34,6 +34,7 @@ export interface MainState {
dialogForExport: DialogForExportTypes
databaseId: string
textFormatPainter: TextFormatPainter | null
shapeFormatPainter: ShapeFormatPainter | null
showSelectPanel: boolean
showSearchPanel: boolean
}
@ -67,6 +68,7 @@ export const useMainStore = defineStore('main', {
dialogForExport: '', // 导出面板
databaseId, // 标识当前应用的indexedDB数据库ID
textFormatPainter: null, // 文字格式刷
shapeFormatPainter: null, // 形状格式刷
showSelectPanel: false, // 打开选择面板
showSearchPanel: false, // 打开查找替换面板
}),
@ -183,6 +185,10 @@ export const useMainStore = defineStore('main', {
this.textFormatPainter = textFormatPainter
},
setShapeFormatPainter(shapeFormatPainter: ShapeFormatPainter | null) {
this.shapeFormatPainter = shapeFormatPainter
},
setSelectPanelState(show: boolean) {
this.showSelectPanel = show
},

View File

@ -1,6 +1,6 @@
import type { ShapePoolItem } from '@/configs/shapes'
import type { LinePoolItem } from '@/configs/lines'
import type { ImageClipDataRange } from './slides'
import type { ImageClipDataRange, PPTElementOutline, PPTElementShadow, ShapeGradient } from './slides'
export enum ElementOrderCommands {
UP = 'up',
@ -111,4 +111,12 @@ export interface TextFormatPainter {
fontsize?: string
fontname?: string
align?: 'left' | 'right' | 'center'
}
export interface ShapeFormatPainter {
fill?: string
gradient?: ShapeGradient
outline?: PPTElementOutline
opacity?: number
shadow?: PPTElementShadow
}

View File

@ -198,7 +198,7 @@
style="flex: 1;"
:checked="!!textFormatPainter"
v-tooltip="'格式刷'"
@click="toggleFormatPainter()"
@click="toggleTextFormatPainter()"
><IconFormatBrush /></CheckboxButton>
</ButtonGroup>
@ -234,6 +234,15 @@
<ElementShadow />
<Divider />
<ElementOpacity />
<Divider />
<div class="row">
<CheckboxButton
style="flex: 1;"
:checked="!!shapeFormatPainter"
@click="toggleShapeFormatPainter()"
><IconFormatBrush /> 形状格式刷</CheckboxButton>
</div>
</div>
</template>
@ -247,6 +256,7 @@ import { type ShapePoolItem, SHAPE_LIST, SHAPE_PATH_FORMULAS } from '@/configs/s
import emitter, { EmitterEvents } from '@/utils/emitter'
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import useTextFormatPainter from '@/hooks/useTextFormatPainter'
import useShapeFormatPainter from '@/hooks/useShapeFormatPainter'
import ElementOpacity from '../common/ElementOpacity.vue'
import ElementOutline from '../common/ElementOutline.vue'
@ -269,7 +279,7 @@ import Popover from '@/components/Popover.vue'
const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const { handleElement, handleElementId, richTextAttrs, availableFonts, textFormatPainter } = storeToRefs(mainStore)
const { handleElement, handleElementId, richTextAttrs, availableFonts, textFormatPainter, shapeFormatPainter } = storeToRefs(mainStore)
const handleShapeElement = handleElement as Ref<PPTShapeElement>
@ -292,7 +302,8 @@ watch(handleElement, () => {
}, { deep: true, immediate: true })
const { addHistorySnapshot } = useHistorySnapshot()
const { toggleFormatPainter } = useTextFormatPainter()
const { toggleTextFormatPainter } = useTextFormatPainter()
const { toggleShapeFormatPainter } = useShapeFormatPainter()
const updateElement = (props: Partial<PPTShapeElement>) => {
slidesStore.updateElement({ id: handleElementId.value, props })

View File

@ -142,7 +142,7 @@
style="flex: 1;"
:checked="!!textFormatPainter"
v-tooltip="'格式刷'"
@click="toggleFormatPainter()"
@click="toggleTextFormatPainter()"
><IconFormatBrush /></CheckboxButton>
<Popover placement="bottom-end" trigger="click" v-model:value="linkPopoverVisible" style="width: 33.33%;">
<template #content>
@ -424,7 +424,7 @@ const slidesStore = useSlidesStore()
const { handleElement, handleElementId, richTextAttrs, availableFonts, textFormatPainter } = storeToRefs(mainStore)
const { addHistorySnapshot } = useHistorySnapshot()
const { toggleFormatPainter } = useTextFormatPainter()
const { toggleTextFormatPainter } = useTextFormatPainter()
const updateElement = (props: Partial<PPTTextElement>) => {
slidesStore.updateElement({ id: handleElementId.value, props })

View File

@ -1,7 +1,10 @@
<template>
<div
class="editable-element-shape"
:class="{ 'lock': elementInfo.lock }"
:class="{
'lock': elementInfo.lock,
'format-painter': shapeFormatPainter,
}"
:style="{
top: elementInfo.top + 'px',
left: elementInfo.left + 'px',
@ -24,6 +27,7 @@
}"
v-contextmenu="contextmenus"
@mousedown="$event => handleSelectElement($event)"
@mouseup="execFormatPainter()"
@touchstart="$event => handleSelectElement($event)"
@dblclick="startEdit()"
>
@ -99,7 +103,7 @@ const props = defineProps<{
const mainStore = useMainStore()
const slidesStore = useSlidesStore()
const { handleElementId } = storeToRefs(mainStore)
const { handleElementId, shapeFormatPainter } = storeToRefs(mainStore)
const { addHistorySnapshot } = useHistorySnapshot()
@ -110,6 +114,17 @@ const handleSelectElement = (e: MouseEvent | TouchEvent, canMove = true) => {
props.selectElement(e, props.elementInfo, canMove)
}
const execFormatPainter = () => {
if (!shapeFormatPainter.value) return
slidesStore.updateElement({
id: props.elementInfo.id,
props: shapeFormatPainter.value,
})
addHistorySnapshot()
mainStore.setShapeFormatPainter(null)
}
const outline = computed(() => props.elementInfo.outline)
const { outlineWidth, outlineColor, strokeDashArray } = useElementOutline(outline)
@ -175,6 +190,9 @@ const startEdit = () => {
&.lock .element-content {
cursor: default;
}
&.format-painter .element-content {
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAVCAYAAACzK0UYAAAABHNCSVQICAgIfAhkiAAAAVRJREFUSInt1DFuwjAYBeCXUrFavgBN9yB6AKR6Bi7AVLrlBpFYgAUpp2i37AysVDIXcCIuwJRMEEYk9LrQDlVQ7EiVOvSt/v1/tmUbeZ7TGMPL5WLgEJLzNE2ptabWmsfjkTeLjTGUUvJ8Pjsjo9GIUkpKKam1voncuTRumn/EKfd1BSQnAF4qhvyK2k1VD88YQ6UUiqJI2+12r2LiPI7j2Xa7rV9yRZbLpRWiAKhGwjW1x3XN828jD9PpVK3X60bAarWy20lZltjv940QwO4KPzbu7oCgLMu/g3Q6ncZI73Q6WSFhGDZGnrIss0LG4zGEEG4ISZUkiW8DDAYDCCEQBIEbAmAWx7GNgSiKAOB1OBzaIyQnSZIom/cRRRG63e7C87z3MAw/fu7Gy/OcRVEgCIK01Wp9/10k37Ism9TdLCHEFzC/zvMPh8Nmt9v5ANDv9/EJD8ykxYswZDkAAAAASUVORK5CYII=) 1 10, default !important;
}
}
.rotate-wrapper {
width: 100%;