feat: 添加形状翻转
This commit is contained in:
parent
5910c3fce3
commit
2838426ca9
|
@ -41,6 +41,10 @@ export interface PPTTextElement {
|
|||
shadow?: PPTElementShadow;
|
||||
}
|
||||
|
||||
export interface ImageOrShapeFlip {
|
||||
x?: number;
|
||||
y?: number;
|
||||
}
|
||||
export interface ImageElementFilters {
|
||||
'blur'?: string;
|
||||
'brightness'?: string;
|
||||
|
@ -68,7 +72,7 @@ export interface PPTImageElement {
|
|||
range: [[number, number], [number, number]];
|
||||
shape: 'rect' | 'roundRect' | 'ellipse' | 'triangle' | 'pentagon' | 'rhombus' | 'star';
|
||||
};
|
||||
flip?: { x?: number; y?: number };
|
||||
flip?: ImageOrShapeFlip;
|
||||
shadow?: PPTElementShadow;
|
||||
}
|
||||
|
||||
|
@ -94,6 +98,7 @@ export interface PPTShapeElement {
|
|||
rotate?: number;
|
||||
outline?: PPTElementOutline;
|
||||
opacity?: number;
|
||||
flip?: ImageOrShapeFlip;
|
||||
shadow?: PPTElementShadow;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,20 +58,8 @@
|
|||
</template>
|
||||
<Button class="full-width-btn"><IconColorFilter class="btn-icon" /> 设置滤镜</Button>
|
||||
</Popover>
|
||||
|
||||
<CheckboxButtonGroup class="row">
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="flip.x === 180"
|
||||
@click="updateImage({ flip: { x: flip.x === 180 ? 0 : 180, y: flip.y } })"
|
||||
><IconFlipVertically /> 水平翻转</CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="flip.y === 180"
|
||||
@click="updateImage({ flip: { x: flip.x, y: flip.y === 180 ? 0 : 180 } })"
|
||||
><IconFlipHorizontally /> 垂直翻转</CheckboxButton>
|
||||
</CheckboxButtonGroup>
|
||||
|
||||
|
||||
<ElementFlip />
|
||||
<Divider />
|
||||
<ElementOutline />
|
||||
<Divider />
|
||||
|
@ -96,6 +84,7 @@ import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
|||
|
||||
import ElementOutline from '../common/ElementOutline.vue'
|
||||
import ElementShadow from '../common/ElementShadow.vue'
|
||||
import ElementFlip from '../common/ElementFlip.vue'
|
||||
|
||||
interface FilterOption {
|
||||
label: string;
|
||||
|
@ -156,6 +145,7 @@ export default defineComponent({
|
|||
components: {
|
||||
ElementOutline,
|
||||
ElementShadow,
|
||||
ElementFlip,
|
||||
},
|
||||
setup() {
|
||||
const store = useStore()
|
||||
|
@ -164,24 +154,11 @@ export default defineComponent({
|
|||
|
||||
const clipPanelVisible = ref(false)
|
||||
|
||||
const flip = ref({
|
||||
x: 0,
|
||||
y: 0,
|
||||
})
|
||||
|
||||
const filterOptions = ref<FilterOption[]>(JSON.parse(JSON.stringify(defaultFilters)))
|
||||
|
||||
watch(handleElement, () => {
|
||||
if (!handleElement.value || handleElement.value.type !== 'image') return
|
||||
|
||||
if (handleElement.value.flip) {
|
||||
flip.value = {
|
||||
x: handleElement.value.flip.x || 0,
|
||||
y: handleElement.value.flip.y || 0,
|
||||
}
|
||||
}
|
||||
else flip.value = { x: 0, y: 0 }
|
||||
|
||||
|
||||
const filters = handleElement.value.filters
|
||||
if (filters) {
|
||||
filterOptions.value = defaultFilters.map(item => {
|
||||
|
@ -194,11 +171,6 @@ export default defineComponent({
|
|||
|
||||
const { addHistorySnapshot } = useHistorySnapshot()
|
||||
|
||||
const updateImage = (props: Partial<PPTImageElement>) => {
|
||||
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
||||
addHistorySnapshot()
|
||||
}
|
||||
|
||||
const updateFilter = (filter: FilterOption, value: number) => {
|
||||
const originFilters = handleElement.value.filters || {}
|
||||
const filters = { ...originFilters, [filter.key]: `${value}${filter.unit}` }
|
||||
|
@ -337,9 +309,7 @@ export default defineComponent({
|
|||
shapeClipPathOptions,
|
||||
ratioClipOptions,
|
||||
filterOptions,
|
||||
flip,
|
||||
handleElement,
|
||||
updateImage,
|
||||
updateFilter,
|
||||
clipImage,
|
||||
presetImageClip,
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<ElementFlip />
|
||||
<Divider />
|
||||
<ElementOutline />
|
||||
<Divider />
|
||||
|
@ -86,6 +87,7 @@ import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
|||
import ElementOpacity from '../common/ElementOpacity.vue'
|
||||
import ElementOutline from '../common/ElementOutline.vue'
|
||||
import ElementShadow from '../common/ElementShadow.vue'
|
||||
import ElementFlip from '../common/ElementFlip.vue'
|
||||
import ColorButton from '../common/ColorButton.vue'
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -94,6 +96,7 @@ export default defineComponent({
|
|||
ElementOpacity,
|
||||
ElementOutline,
|
||||
ElementShadow,
|
||||
ElementFlip,
|
||||
ColorButton,
|
||||
},
|
||||
setup() {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<div class="element-flip">
|
||||
<CheckboxButtonGroup class="row">
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="flip.x === 180"
|
||||
@click="updateFlip({ x: flip.x === 180 ? 0 : 180, y: flip.y })"
|
||||
><IconFlipVertically /> 水平翻转</CheckboxButton>
|
||||
<CheckboxButton
|
||||
style="flex: 1;"
|
||||
:checked="flip.y === 180"
|
||||
@click="updateFlip({ x: flip.x, y: flip.y === 180 ? 0 : 180 })"
|
||||
><IconFlipHorizontally /> 垂直翻转</CheckboxButton>
|
||||
</CheckboxButtonGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref, watch } from 'vue'
|
||||
import { MutationTypes, useStore } from '@/store'
|
||||
import { PPTImageElement, PPTShapeElement } from '@/types/slides'
|
||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'element-flip',
|
||||
setup() {
|
||||
const store = useStore()
|
||||
const handleElement = computed<PPTImageElement | PPTShapeElement>(() => store.getters.handleElement)
|
||||
|
||||
const flip = ref({
|
||||
x: 0,
|
||||
y: 0,
|
||||
})
|
||||
|
||||
watch(handleElement, () => {
|
||||
if (!handleElement.value || !['image', 'shape'].includes(handleElement.value.type)) return
|
||||
|
||||
if (handleElement.value.flip) {
|
||||
flip.value = {
|
||||
x: handleElement.value.flip.x || 0,
|
||||
y: handleElement.value.flip.y || 0,
|
||||
}
|
||||
}
|
||||
else flip.value = { x: 0, y: 0 }
|
||||
}, { deep: true, immediate: true })
|
||||
|
||||
const { addHistorySnapshot } = useHistorySnapshot()
|
||||
|
||||
const updateFlip = (value: number) => {
|
||||
const props = { flip: value }
|
||||
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
||||
addHistorySnapshot()
|
||||
}
|
||||
|
||||
return {
|
||||
flip,
|
||||
updateFlip,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
|
@ -13,7 +13,7 @@
|
|||
class="element-content"
|
||||
:style="{
|
||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||
transform: flip,
|
||||
transform: flipStyle,
|
||||
}"
|
||||
>
|
||||
<ImageRectOutline
|
||||
|
@ -66,6 +66,7 @@ import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
|||
import ImagePolygonOutline from './ImagePolygonOutline.vue'
|
||||
|
||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'base-element-image',
|
||||
|
@ -122,23 +123,17 @@ export default defineComponent({
|
|||
return filter
|
||||
})
|
||||
|
||||
const flip = computed(() => {
|
||||
if (!props.elementInfo.flip) return ''
|
||||
const { x, y } = props.elementInfo.flip
|
||||
if (x && y) return `rotateX(${x}deg) rotateY(${y}deg)`
|
||||
else if (x) return `rotateX(${x}deg)`
|
||||
else if (y) return `rotateY(${y}deg)`
|
||||
return ''
|
||||
})
|
||||
|
||||
const shadow = computed(() => props.elementInfo.shadow)
|
||||
const { shadowStyle } = useElementShadow(shadow)
|
||||
|
||||
const flip = computed(() => props.elementInfo.flip)
|
||||
const { flipStyle } = useElementFlip(flip)
|
||||
|
||||
return {
|
||||
imgPosition,
|
||||
clipShape,
|
||||
filter,
|
||||
flip,
|
||||
flipStyle,
|
||||
shadowStyle,
|
||||
}
|
||||
},
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
v-contextmenu="contextmenus"
|
||||
:style="{
|
||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||
transform: flip,
|
||||
transform: flipStyle,
|
||||
}"
|
||||
>
|
||||
<ImageRectOutline
|
||||
|
@ -77,6 +77,7 @@ import { PPTImageElement } from '@/types/slides'
|
|||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
|
||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
|
||||
|
||||
import ImageRectOutline from './ImageRectOutline.vue'
|
||||
import ImageEllipseOutline from './ImageEllipseOutline.vue'
|
||||
|
@ -113,6 +114,9 @@ export default defineComponent({
|
|||
const shadow = computed(() => props.elementInfo.shadow)
|
||||
const { shadowStyle } = useElementShadow(shadow)
|
||||
|
||||
const flip = computed(() => props.elementInfo.flip)
|
||||
const { flipStyle } = useElementFlip(flip)
|
||||
|
||||
const handleSelectElement = (e: MouseEvent) => {
|
||||
if (props.elementInfo.lock) return
|
||||
e.stopPropagation()
|
||||
|
@ -159,15 +163,6 @@ export default defineComponent({
|
|||
return filter
|
||||
})
|
||||
|
||||
const flip = computed(() => {
|
||||
if (!props.elementInfo.flip) return ''
|
||||
const { x, y } = props.elementInfo.flip
|
||||
if (x && y) return `rotateX(${x}deg) rotateY(${y}deg)`
|
||||
else if (x) return `rotateX(${x}deg)`
|
||||
else if (y) return `rotateY(${y}deg)`
|
||||
return ''
|
||||
})
|
||||
|
||||
const clip = (data: ImageClipedEmitData) => {
|
||||
store.commit(MutationTypes.SET_CLIPING_IMAGE_ELEMENT_ID, '')
|
||||
|
||||
|
@ -195,7 +190,7 @@ export default defineComponent({
|
|||
clipShape,
|
||||
imgPosition,
|
||||
filter,
|
||||
flip,
|
||||
flipStyle,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
:style="{
|
||||
opacity: elementInfo.opacity,
|
||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||
transform: flipStyle,
|
||||
}"
|
||||
>
|
||||
<SvgWrapper
|
||||
|
@ -55,6 +56,7 @@ import { computed, defineComponent, PropType } from 'vue'
|
|||
import { PPTShapeElement } from '@/types/slides'
|
||||
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
|
||||
|
||||
import GradientDefs from './GradientDefs.vue'
|
||||
|
||||
|
@ -76,11 +78,15 @@ export default defineComponent({
|
|||
const shadow = computed(() => props.elementInfo.shadow)
|
||||
const { shadowStyle } = useElementShadow(shadow)
|
||||
|
||||
const flip = computed(() => props.elementInfo.flip)
|
||||
const { flipStyle } = useElementFlip(flip)
|
||||
|
||||
return {
|
||||
shadowStyle,
|
||||
outlineWidth,
|
||||
outlineStyle,
|
||||
outlineColor,
|
||||
flipStyle,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
:style="{
|
||||
opacity: elementInfo.opacity,
|
||||
filter: shadowStyle ? `drop-shadow(${shadowStyle})` : '',
|
||||
transform: flipStyle,
|
||||
}"
|
||||
>
|
||||
<SvgWrapper
|
||||
|
@ -59,6 +60,7 @@ import { PPTShapeElement } from '@/types/slides'
|
|||
import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||
import useElementFlip from '@/views/components/element/hooks/useElementFlip'
|
||||
|
||||
import GradientDefs from './GradientDefs.vue'
|
||||
|
||||
|
@ -94,12 +96,16 @@ export default defineComponent({
|
|||
const shadow = computed(() => props.elementInfo.shadow)
|
||||
const { shadowStyle } = useElementShadow(shadow)
|
||||
|
||||
const flip = computed(() => props.elementInfo.flip)
|
||||
const { flipStyle } = useElementFlip(flip)
|
||||
|
||||
return {
|
||||
handleSelectElement,
|
||||
shadowStyle,
|
||||
outlineWidth,
|
||||
outlineStyle,
|
||||
outlineColor,
|
||||
flipStyle,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { ref, Ref, watchEffect } from 'vue'
|
||||
import { ImageOrShapeFlip } from '@/types/slides'
|
||||
|
||||
export default (flip: Ref<ImageOrShapeFlip | undefined>) => {
|
||||
const flipStyle = ref('')
|
||||
|
||||
watchEffect(() => {
|
||||
if (flip.value) {
|
||||
const { x, y } = flip.value
|
||||
if (x && y) flipStyle.value = `rotateX(${x}deg) rotateY(${y}deg)`
|
||||
else if (x) flipStyle.value = `rotateX(${x}deg)`
|
||||
else if (y) flipStyle.value = `rotateY(${y}deg)`
|
||||
else flipStyle.value = ''
|
||||
}
|
||||
else flipStyle.value = ''
|
||||
})
|
||||
|
||||
return {
|
||||
flipStyle,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue