mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
update
This commit is contained in:
parent
653bf033a7
commit
40d234b0fc
@ -56,12 +56,4 @@ export const CLIPPATHS = {
|
|||||||
return `M ${width / 2} 0 L ${width} ${height / 2} L ${width / 2} ${height} L 0 ${height / 2} Z`
|
return `M ${width / 2} 0 L ${width} ${height / 2} L ${width / 2} ${height} L 0 ${height / 2} Z`
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
star: {
|
|
||||||
name: '五角星',
|
|
||||||
type: ClipPathTypes.POLYGON,
|
|
||||||
style: 'polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%)',
|
|
||||||
createPath: (width: number, height: number) => {
|
|
||||||
return `M ${width / 2} 0 L ${0.61 * width} ${0.35 * height} L ${0.98 * width} ${0.35 * height} L ${0.68 * width} ${0.57 * height} L ${0.79 * width} ${0.91 * height} L ${0.50 * width} ${0.70 * height} L ${0.21 * width} ${0.91 * height} L ${0.32 * width} ${0.57 * height} L ${0.02 * width} ${0.35 * height} L ${0.39 * width} ${0.35 * height} Z`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
@ -15,7 +15,9 @@ const clickListener = (el: HTMLElement, event: MouseEvent, binding: DirectiveBin
|
|||||||
const ClickOutsideDirective: Directive = {
|
const ClickOutsideDirective: Directive = {
|
||||||
mounted(el: HTMLElement, binding) {
|
mounted(el: HTMLElement, binding) {
|
||||||
el[CTX_CLICK_OUTSIDE_HANDLER] = (event: MouseEvent) => clickListener(el, event, binding)
|
el[CTX_CLICK_OUTSIDE_HANDLER] = (event: MouseEvent) => clickListener(el, event, binding)
|
||||||
document.addEventListener('click', el[CTX_CLICK_OUTSIDE_HANDLER])
|
setTimeout(() => {
|
||||||
|
document.addEventListener('click', el[CTX_CLICK_OUTSIDE_HANDLER])
|
||||||
|
}, 0)
|
||||||
},
|
},
|
||||||
|
|
||||||
unmounted(el: HTMLElement) {
|
unmounted(el: HTMLElement) {
|
||||||
|
@ -22,6 +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',
|
||||||
|
|
||||||
// snapshot
|
// snapshot
|
||||||
SET_SNAPSHOT_CURSOR = 'setSnapshotCursor',
|
SET_SNAPSHOT_CURSOR = 'setSnapshotCursor',
|
||||||
|
@ -6,6 +6,11 @@ import { CreatingElement } from '@/types/edit'
|
|||||||
import { FONT_NAMES } from '@/configs/fontName'
|
import { FONT_NAMES } from '@/configs/fontName'
|
||||||
import { isSupportFontFamily } from '@/utils/fontFamily'
|
import { isSupportFontFamily } from '@/utils/fontFamily'
|
||||||
|
|
||||||
|
interface RemoveElementPropData {
|
||||||
|
id: string;
|
||||||
|
propName: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface UpdateElementData {
|
interface UpdateElementData {
|
||||||
id: string | string[];
|
id: string | string[];
|
||||||
props: Partial<PPTElement>;
|
props: Partial<PPTElement>;
|
||||||
@ -115,6 +120,18 @@ 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) {
|
||||||
|
const { id, propName } = data
|
||||||
|
|
||||||
|
const slideIndex = state.slideIndex
|
||||||
|
const slide = state.slides[slideIndex]
|
||||||
|
const elements = slide.elements.map(el => {
|
||||||
|
if(el.id === id) delete el[propName]
|
||||||
|
return el
|
||||||
|
})
|
||||||
|
state.slides[slideIndex].elements = (elements as PPTElement[])
|
||||||
|
},
|
||||||
|
|
||||||
// snapshot
|
// snapshot
|
||||||
|
|
||||||
[MutationTypes.SET_SNAPSHOT_CURSOR](state, cursor: number) {
|
[MutationTypes.SET_SNAPSHOT_CURSOR](state, cursor: number) {
|
||||||
|
@ -5,7 +5,38 @@
|
|||||||
:style="{ backgroundImage: `url(${handleElement.src})` }"
|
:style="{ backgroundImage: `url(${handleElement.src})` }"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<Button class="full-width-btn" @click="clipImage()">裁剪图片</Button>
|
<Popover trigger="click" v-model:visible="clipPanelVisible">
|
||||||
|
<template #content>
|
||||||
|
<div class="clip">
|
||||||
|
<Button class="full-width-btn" @click="clipImage()">裁剪</Button>
|
||||||
|
|
||||||
|
<div class="title">按形状裁剪:</div>
|
||||||
|
<div class="shape-clip">
|
||||||
|
<div
|
||||||
|
class="shape-clip-item"
|
||||||
|
v-for="(item, index) in shapeClipPathOptions"
|
||||||
|
:key="index"
|
||||||
|
@click="presetImageClip(index)"
|
||||||
|
>
|
||||||
|
<div class="shape" :style="{ clipPath: item.style }"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-for="type in ratioClipOptions" :key="type.label">
|
||||||
|
<div class="title" v-if="type.label">{{type.label}}:</div>
|
||||||
|
<ButtonGroup class="row">
|
||||||
|
<Button
|
||||||
|
style="flex: 1;"
|
||||||
|
v-for="item in type.children"
|
||||||
|
:key="item.key"
|
||||||
|
@click="presetImageClip('rect', item.ratio)"
|
||||||
|
>{{item.key}}</Button>
|
||||||
|
</ButtonGroup>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<Button class="full-width-btn">裁剪图片</Button>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
<Popover trigger="click">
|
<Popover trigger="click">
|
||||||
<template #content>
|
<template #content>
|
||||||
@ -62,6 +93,7 @@ import { computed, defineComponent, ref, Ref, watch } from 'vue'
|
|||||||
import { useStore } from 'vuex'
|
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 useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
import ElementOutline from '../common/ElementOutline.vue'
|
import ElementOutline from '../common/ElementOutline.vue'
|
||||||
@ -87,6 +119,40 @@ const defaultFilters: FilterOption[] = [
|
|||||||
{ label: '不透明度', key: 'opacity', default: 100, value: 100, unit: '%', max: 100, step: 5 },
|
{ label: '不透明度', key: 'opacity', default: 100, value: 100, unit: '%', max: 100, step: 5 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const shapeClipPathOptions = CLIPPATHS
|
||||||
|
const ratioClipOptions = [
|
||||||
|
{
|
||||||
|
label: '纵横比(方形)',
|
||||||
|
children: [
|
||||||
|
{ key: '1:1', ratio: 1 / 1 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '纵横比(纵向)',
|
||||||
|
children: [
|
||||||
|
{ key: '2:3', ratio: 3 / 2 },
|
||||||
|
{ key: '3:4', ratio: 4 / 3 },
|
||||||
|
{ key: '3:5', ratio: 5 / 3 },
|
||||||
|
{ key: '4:5', ratio: 5 / 4 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '纵横比(横向)',
|
||||||
|
children: [
|
||||||
|
{ key: '3:2', ratio: 2 / 3 },
|
||||||
|
{ key: '4:3', ratio: 3 / 4 },
|
||||||
|
{ key: '5:3', ratio: 3 / 5 },
|
||||||
|
{ key: '5:4', ratio: 4 / 5 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
{ key: '16:9', ratio: 9 / 16 },
|
||||||
|
{ key: '16:10', ratio: 10 / 16 },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'image-style-panel',
|
name: 'image-style-panel',
|
||||||
components: {
|
components: {
|
||||||
@ -97,6 +163,8 @@ export default defineComponent({
|
|||||||
const store = useStore<State>()
|
const store = useStore<State>()
|
||||||
const handleElement: Ref<PPTImageElement> = computed(() => store.getters.handleElement)
|
const handleElement: Ref<PPTImageElement> = computed(() => store.getters.handleElement)
|
||||||
|
|
||||||
|
const clipPanelVisible = ref(false)
|
||||||
|
|
||||||
const flip = ref({
|
const flip = ref({
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
@ -141,18 +209,94 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const clipImage = () => {
|
const clipImage = () => {
|
||||||
setTimeout(() => {
|
store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, handleElement.value.id)
|
||||||
store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, handleElement.value.id)
|
clipPanelVisible.value = false
|
||||||
}, 0)
|
}
|
||||||
|
|
||||||
|
const presetImageClip = (shape: string, ratio = 0) => {
|
||||||
|
// 图片当前宽高位置、裁剪范围
|
||||||
|
const imgWidth = handleElement.value.width
|
||||||
|
const imgHeight = handleElement.value.height
|
||||||
|
const imgLeft = handleElement.value.left
|
||||||
|
const imgTop = handleElement.value.top
|
||||||
|
const originClipRange = handleElement.value.clip ? handleElement.value.clip.range : [[0, 0], [100, 100]]
|
||||||
|
|
||||||
|
// 图片原本未裁剪过时的宽高位置
|
||||||
|
const originWidth = imgWidth / ((originClipRange[1][0] - originClipRange[0][0]) / 100)
|
||||||
|
const originHeight = imgHeight / ((originClipRange[1][1] - originClipRange[0][1]) / 100)
|
||||||
|
const originLeft = imgLeft - originWidth * (originClipRange[0][0] / 100)
|
||||||
|
const originTop = imgTop - originHeight * (originClipRange[0][1] / 100)
|
||||||
|
|
||||||
|
// 取消裁剪(移除裁剪属性,并将宽高位置设置为原本未裁剪过时的状态)
|
||||||
|
if(shape === 'none') {
|
||||||
|
store.commit(MutationTypes.UPDATE_ELEMENT, {
|
||||||
|
id: handleElement.value.id,
|
||||||
|
props: {
|
||||||
|
left: originLeft,
|
||||||
|
top: originTop,
|
||||||
|
width: originWidth,
|
||||||
|
height: originHeight,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
store.commit(MutationTypes.REMOVE_ELEMENT_PROP, {
|
||||||
|
id: handleElement.value.id,
|
||||||
|
propName: 'clip',
|
||||||
|
})
|
||||||
|
clipPanelVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置形状和纵横比
|
||||||
|
else if(ratio) {
|
||||||
|
const imageRatio = originHeight / originWidth
|
||||||
|
|
||||||
|
const min = 0
|
||||||
|
const max = 100
|
||||||
|
let range
|
||||||
|
|
||||||
|
if(imageRatio > ratio) {
|
||||||
|
const distance = ((1 - ratio / imageRatio) / 2) * 100
|
||||||
|
range = [[min, distance], [max, max - distance]]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const distance = ((1 - imageRatio / ratio) / 2) * 100
|
||||||
|
range = [[distance, min], [max - distance, max]]
|
||||||
|
}
|
||||||
|
store.commit(MutationTypes.UPDATE_ELEMENT, {
|
||||||
|
id: handleElement.value.id,
|
||||||
|
props: {
|
||||||
|
clip: { ...handleElement.value.clip, shape, range },
|
||||||
|
left: originLeft + originWidth * (range[0][0] / 100),
|
||||||
|
top: originTop + originHeight * (range[0][1] / 100),
|
||||||
|
width: originWidth * (range[1][0] - range[0][0]) / 100,
|
||||||
|
height: originHeight * (range[1][1] - range[0][1]) / 100,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
clipImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅设置形状(维持目前的裁剪范围)
|
||||||
|
else {
|
||||||
|
store.commit(MutationTypes.UPDATE_ELEMENT, {
|
||||||
|
id: handleElement.value.id,
|
||||||
|
props: {
|
||||||
|
clip: { ...handleElement.value.clip, shape, range: originClipRange }
|
||||||
|
},
|
||||||
|
})
|
||||||
|
clipImage()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
clipPanelVisible,
|
||||||
|
shapeClipPathOptions,
|
||||||
|
ratioClipOptions,
|
||||||
filterOptions,
|
filterOptions,
|
||||||
flip,
|
flip,
|
||||||
handleElement,
|
handleElement,
|
||||||
updateImage,
|
updateImage,
|
||||||
updateFilter,
|
updateFilter,
|
||||||
clipImage,
|
clipImage,
|
||||||
|
presetImageClip,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -203,4 +347,32 @@ export default defineComponent({
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clip {
|
||||||
|
width: 280px;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.shape-clip {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
@include grid-layout-wrapper();
|
||||||
|
}
|
||||||
|
.shape-clip-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
@include grid-layout-item(5, 19%);
|
||||||
|
|
||||||
|
.shape {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: #e1e1e1;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
Loading…
x
Reference in New Issue
Block a user