feat: 支持图片圆角

This commit is contained in:
zxc 2024-08-26 22:26:45 +08:00
parent 4950c3687d
commit 8dec4a59cf
9 changed files with 64 additions and 40 deletions

View File

@ -91,6 +91,7 @@ npm run dev
- Vertical text
#### Images
- Crop (custom, shape, aspect ratio)
- Rounding
- Filters
- Tint (mask)
- Flip

View File

@ -87,6 +87,7 @@ npm run dev
- 竖向文本
#### 图片
- 裁剪(自定义、按形状、按纵横比)
- 圆角
- 滤镜
- 着色(蒙版)
- 翻转

View File

@ -51,7 +51,7 @@ export const CLIPPATHS: ClipPath = {
name: '圆角矩形',
type: ClipPathTypes.RECT,
radius: '10px',
style: 'inset(0 0 0 0 round 10px 10px 10px 10px)',
style: 'inset(0 round 10px)',
},
ellipse: {
name: '圆形',

View File

@ -258,6 +258,10 @@ export interface ImageElementClip {
* flipV?: 垂直翻转
*
* shadow?: 阴影
*
* radius?: 圆角半径
*
* colorMask?: 颜色蒙版
*/
export interface PPTImageElement extends PPTBaseElement {
type: 'image'
@ -269,6 +273,7 @@ export interface PPTImageElement extends PPTBaseElement {
flipH?: boolean
flipV?: boolean
shadow?: PPTElementShadow
radius?: number
colorMask?: string
}

View File

@ -40,6 +40,15 @@
<Button last class="popover-btn" style="width: 100%;"><IconDown /></Button>
</Popover>
</ButtonGroup>
<div class="row">
<div style="width: 40%;">圆角半径</div>
<NumberInput
:value="handleImageElement.radius || 0"
@update:value="value => updateImage({ radius: value })"
style="width: 60%;"
/>
</div>
<Divider />
<ElementColorMask />
@ -78,6 +87,7 @@ import Divider from '@/components/Divider.vue'
import Button from '@/components/Button.vue'
import ButtonGroup from '@/components/ButtonGroup.vue'
import Popover from '@/components/Popover.vue'
import NumberInput from '@/components/NumberInput.vue'
const shapeClipPathOptions = CLIPPATHS
const ratioClipOptions = [
@ -155,6 +165,12 @@ const getImageElementDataBeforeClip = () => {
}
}
const updateImage = (props: Partial<PPTImageElement>) => {
if (!handleElement.value) return
slidesStore.updateElement({ id: handleElementId.value, props })
addHistorySnapshot()
}
//
const presetImageClip = (shape: string, ratio = 0) => {
const _handleElement = handleElement.value as PPTImageElement
@ -183,28 +199,22 @@ const presetImageClip = (shape: string, ratio = 0) => {
const distance = ((1 - imageRatio / ratio) / 2) * 100
range = [[distance, min], [max - distance, max]]
}
slidesStore.updateElement({
id: handleElementId.value,
props: {
clip: { ..._handleElement.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,
},
updateImage({
clip: { ..._handleElement.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,
})
}
//
else {
slidesStore.updateElement({
id: handleElementId.value,
props: {
clip: { ..._handleElement.clip, shape, range: originClipRange }
},
})
const clipData = { ..._handleElement.clip, shape, range: originClipRange }
let props: Partial<PPTImageElement> = { clip: clipData }
if (shape === 'rect') props = { clip: clipData, radius: 0 }
updateImage(props)
}
clipImage()
addHistorySnapshot()
}
//
@ -213,9 +223,8 @@ const replaceImage = (files: FileList) => {
if (!imageFile) return
getImageDataURL(imageFile).then(dataURL => {
const props = { src: dataURL }
slidesStore.updateElement({ id: handleElementId.value, props })
updateImage(props)
})
addHistorySnapshot()
}
//
@ -230,14 +239,11 @@ const resetImage = () => {
originTop,
} = getImageElementDataBeforeClip()
slidesStore.updateElement({
id: handleElementId.value,
props: {
left: originLeft,
top: originTop,
width: originWidth,
height: originHeight,
},
updateImage({
left: originLeft,
top: originTop,
width: originWidth,
height: originHeight,
})
}

View File

@ -67,8 +67,8 @@ const flipH = computed(() => props.elementInfo.flipH)
const flipV = computed(() => props.elementInfo.flipV)
const { flipStyle } = useElementFlip(flipH, flipV)
const clip = computed(() => props.elementInfo.clip)
const { clipShape, imgPosition } = useClipImage(clip)
const imageElement = computed(() => props.elementInfo)
const { clipShape, imgPosition } = useClipImage(imageElement)
const filters = computed(() => props.elementInfo.filters)
const { filter } = useFilter(filters)

View File

@ -36,6 +36,6 @@ const props = defineProps<{
elementInfo: PPTImageElement
}>()
const clip = computed(() => props.elementInfo.clip)
const { clipShape } = useClipImage(clip)
const imageElement = computed(() => props.elementInfo)
const { clipShape } = useClipImage(imageElement)
</script>

View File

@ -101,8 +101,8 @@ const flipH = computed(() => props.elementInfo.flipH)
const flipV = computed(() => props.elementInfo.flipV)
const { flipStyle } = useElementFlip(flipH, flipV)
const clip = computed(() => props.elementInfo.clip)
const { clipShape, imgPosition } = useClipImage(clip)
const imageElement = computed(() => props.elementInfo)
const { clipShape, imgPosition } = useClipImage(imageElement)
const filters = computed(() => props.elementInfo.filters)
const { filter } = useFilter(filters)

View File

@ -1,17 +1,28 @@
import { computed, type Ref } from 'vue'
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip'
import type { ImageElementClip } from '@/types/slides'
import type { PPTImageElement } from '@/types/slides'
export default (clip: Ref<ImageElementClip | undefined>) => {
export default (element: Ref<PPTImageElement>) => {
const clipShape = computed(() => {
if (!clip.value) return CLIPPATHS.rect
const shape = clip.value.shape || ClipPathTypes.RECT
let _clipShape = CLIPPATHS.rect
if (element.value.clip) {
const shape = element.value.clip.shape || ClipPathTypes.RECT
_clipShape = CLIPPATHS[shape]
}
if (_clipShape.radius !== undefined && element.value.radius) {
_clipShape = {
..._clipShape,
radius: `${element.value.radius}px`,
style: `inset(0 round ${element.value.radius}px)`,
}
}
return CLIPPATHS[shape]
return _clipShape
})
const imgPosition = computed(() => {
if (!clip.value) {
if (!element.value.clip) {
return {
top: '0',
left: '0',
@ -20,7 +31,7 @@ export default (clip: Ref<ImageElementClip | undefined>) => {
}
}
const [start, end] = clip.value.range
const [start, end] = element.value.clip.range
const widthScale = (end[0] - start[0]) / 100
const heightScale = (end[1] - start[1]) / 100