基本完成图片属性面板

This commit is contained in:
pipipi-pikachu 2021-01-12 10:24:21 +08:00
parent 40d234b0fc
commit eb33459d52
5 changed files with 109 additions and 62 deletions

View File

@ -13,6 +13,10 @@
} }
.ant-btn { .ant-btn {
font-size: 13px !important; font-size: 13px !important;
&.no-padding {
padding: 0;
}
} }
.ant-radio-group { .ant-radio-group {
font-size: 13px !important; font-size: 13px !important;

View File

@ -22,7 +22,7 @@ export enum MutationTypes {
UPDATE_SLIDE_INDEX = 'updateSlideIndex', UPDATE_SLIDE_INDEX = 'updateSlideIndex',
ADD_ELEMENT = 'addElement', ADD_ELEMENT = 'addElement',
UPDATE_ELEMENT = 'updateElement', UPDATE_ELEMENT = 'updateElement',
REMOVE_ELEMENT_PROP = 'removeElementProp', REMOVE_ELEMENT_PROPS = 'removeElementProps',
// snapshot // snapshot
SET_SNAPSHOT_CURSOR = 'setSnapshotCursor', SET_SNAPSHOT_CURSOR = 'setSnapshotCursor',

View File

@ -1,4 +1,5 @@
import { MutationTree } from 'vuex' import { MutationTree } from 'vuex'
import omit from 'lodash/omit'
import { MutationTypes } from './constants' import { MutationTypes } from './constants'
import { State } from './index' import { State } from './index'
import { Slide, PPTElement } from '@/types/slides' import { Slide, PPTElement } from '@/types/slides'
@ -8,7 +9,7 @@ import { isSupportFontFamily } from '@/utils/fontFamily'
interface RemoveElementPropData { interface RemoveElementPropData {
id: string; id: string;
propName: string; propName: string | string[];
} }
interface UpdateElementData { interface UpdateElementData {
@ -120,14 +121,14 @@ export const mutations: MutationTree<State> = {
state.slides[slideIndex].elements = (elements as PPTElement[]) state.slides[slideIndex].elements = (elements as PPTElement[])
}, },
[MutationTypes.REMOVE_ELEMENT_PROP](state, data: RemoveElementPropData) { [MutationTypes.REMOVE_ELEMENT_PROPS](state, data: RemoveElementPropData) {
const { id, propName } = data const { id, propName } = data
const propsNames = typeof propName === 'string' ? [propName] : propName
const slideIndex = state.slideIndex const slideIndex = state.slideIndex
const slide = state.slides[slideIndex] const slide = state.slides[slideIndex]
const elements = slide.elements.map(el => { const elements = slide.elements.map(el => {
if(el.id === id) delete el[propName] return el.id === id ? omit(el, propsNames) : el
return el
}) })
state.slides[slideIndex].elements = (elements as PPTElement[]) state.slides[slideIndex].elements = (elements as PPTElement[])
}, },

View File

@ -5,38 +5,39 @@
:style="{ backgroundImage: `url(${handleElement.src})` }" :style="{ backgroundImage: `url(${handleElement.src})` }"
></div> ></div>
<Popover trigger="click" v-model:visible="clipPanelVisible"> <ButtonGroup class="row">
<template #content> <Button style="flex: 5;" @click="clipImage()">裁剪图片</Button>
<div class="clip"> <Popover trigger="click" v-model:visible="clipPanelVisible">
<Button class="full-width-btn" @click="clipImage()">裁剪</Button> <template #content>
<div class="clip">
<div class="title">按形状裁剪</div> <div class="title">按形状裁剪</div>
<div class="shape-clip"> <div class="shape-clip">
<div <div
class="shape-clip-item" class="shape-clip-item"
v-for="(item, index) in shapeClipPathOptions" v-for="(item, index) in shapeClipPathOptions"
:key="index" :key="index"
@click="presetImageClip(index)" @click="presetImageClip(index)"
> >
<div class="shape" :style="{ clipPath: item.style }"></div> <div class="shape" :style="{ clipPath: item.style }"></div>
</div>
</div> </div>
</div>
<template v-for="type in ratioClipOptions" :key="type.label"> <template v-for="type in ratioClipOptions" :key="type.label">
<div class="title" v-if="type.label">{{type.label}}</div> <div class="title" v-if="type.label">{{type.label}}</div>
<ButtonGroup class="row"> <ButtonGroup class="row">
<Button <Button
style="flex: 1;" style="flex: 1;"
v-for="item in type.children" v-for="item in type.children"
:key="item.key" :key="item.key"
@click="presetImageClip('rect', item.ratio)" @click="presetImageClip('rect', item.ratio)"
>{{item.key}}</Button> >{{item.key}}</Button>
</ButtonGroup> </ButtonGroup>
</template> </template>
</div> </div>
</template> </template>
<Button class="full-width-btn">裁剪图片</Button> <Button class="no-padding" style="flex: 1;"><IconFont type="icon-down" /></Button>
</Popover> </Popover>
</ButtonGroup>
<Popover trigger="click"> <Popover trigger="click">
<template #content> <template #content>
@ -83,8 +84,10 @@
<ElementShadow /> <ElementShadow />
<Divider /> <Divider />
<Button class="full-width-btn">替换图片</Button> <FileInput @change="files => replaceImage(files)">
<Button class="full-width-btn">重置样式</Button> <Button class="full-width-btn">替换图片</Button>
</FileInput>
<Button class="full-width-btn" @click="resetImage()">重置样式</Button>
</div> </div>
</template> </template>
@ -94,6 +97,7 @@ import { useStore } from 'vuex'
import { MutationTypes, State } from '@/store' import { MutationTypes, State } from '@/store'
import { PPTImageElement } from '@/types/slides' import { PPTImageElement } from '@/types/slides'
import { CLIPPATHS } from '@/configs/imageClip' import { CLIPPATHS } from '@/configs/imageClip'
import { getImageDataURL } from '@/utils/image'
import useHistorySnapshot from '@/hooks/useHistorySnapshot' import useHistorySnapshot from '@/hooks/useHistorySnapshot'
import ElementOutline from '../common/ElementOutline.vue' import ElementOutline from '../common/ElementOutline.vue'
@ -213,7 +217,7 @@ export default defineComponent({
clipPanelVisible.value = false clipPanelVisible.value = false
} }
const presetImageClip = (shape: string, ratio = 0) => { const getImageElementDataBeforeClip = () => {
// //
const imgWidth = handleElement.value.width const imgWidth = handleElement.value.width
const imgHeight = handleElement.value.height const imgHeight = handleElement.value.height
@ -227,26 +231,26 @@ export default defineComponent({
const originLeft = imgLeft - originWidth * (originClipRange[0][0] / 100) const originLeft = imgLeft - originWidth * (originClipRange[0][0] / 100)
const originTop = imgTop - originHeight * (originClipRange[0][1] / 100) const originTop = imgTop - originHeight * (originClipRange[0][1] / 100)
// return {
if(shape === 'none') { originClipRange,
store.commit(MutationTypes.UPDATE_ELEMENT, { originWidth,
id: handleElement.value.id, originHeight,
props: { originLeft,
left: originLeft, originTop,
top: originTop,
width: originWidth,
height: originHeight,
},
})
store.commit(MutationTypes.REMOVE_ELEMENT_PROP, {
id: handleElement.value.id,
propName: 'clip',
})
clipPanelVisible.value = false
} }
}
const presetImageClip = (shape: string, ratio = 0) => {
const {
originClipRange,
originWidth,
originHeight,
originLeft,
originTop,
} = getImageElementDataBeforeClip()
// //
else if(ratio) { if(ratio) {
const imageRatio = originHeight / originWidth const imageRatio = originHeight / originWidth
const min = 0 const min = 0
@ -271,9 +275,7 @@ export default defineComponent({
height: originHeight * (range[1][1] - range[0][1]) / 100, height: originHeight * (range[1][1] - range[0][1]) / 100,
}, },
}) })
clipImage()
} }
// //
else { else {
store.commit(MutationTypes.UPDATE_ELEMENT, { store.commit(MutationTypes.UPDATE_ELEMENT, {
@ -282,8 +284,46 @@ export default defineComponent({
clip: { ...handleElement.value.clip, shape, range: originClipRange } clip: { ...handleElement.value.clip, shape, range: originClipRange }
}, },
}) })
clipImage()
} }
clipImage()
addHistorySnapshot()
}
const replaceImage = (files: File[]) => {
const imageFile = files[0]
if(!imageFile) return
getImageDataURL(imageFile).then(dataURL => {
const props = { src: dataURL }
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
})
addHistorySnapshot()
}
const resetImage = () => {
if(handleElement.value.clip) {
const {
originWidth,
originHeight,
originLeft,
originTop,
} = getImageElementDataBeforeClip()
store.commit(MutationTypes.UPDATE_ELEMENT, {
id: handleElement.value.id,
props: {
left: originLeft,
top: originTop,
width: originWidth,
height: originHeight,
},
})
}
store.commit(MutationTypes.REMOVE_ELEMENT_PROPS, {
id: handleElement.value.id,
propName: ['clip', 'outline', 'flip', 'shadow', 'filter'],
})
addHistorySnapshot()
} }
return { return {
@ -297,6 +337,8 @@ export default defineComponent({
updateFilter, updateFilter,
clipImage, clipImage,
presetImageClip, presetImageClip,
replaceImage,
resetImage,
} }
}, },
}) })

View File

@ -10,7 +10,7 @@
<div class="row"> <div class="row">
<div style="flex: 2;">水平阴影</div> <div style="flex: 2;">水平阴影</div>
<Slider <Slider
:min="1" :min="0"
:max="10" :max="10"
:step="1" :step="1"
:value="shadow.h" :value="shadow.h"
@ -21,7 +21,7 @@
<div class="row"> <div class="row">
<div style="flex: 2;">垂直阴影</div> <div style="flex: 2;">垂直阴影</div>
<Slider <Slider
:min="1" :min="0"
:max="10" :max="10"
:step="1" :step="1"
:value="shadow.v" :value="shadow.v"