mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
基本完成图片属性面板
This commit is contained in:
parent
40d234b0fc
commit
eb33459d52
@ -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;
|
||||||
|
@ -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',
|
||||||
|
@ -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[])
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -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"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user