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 - Vertical text
#### Images #### Images
- Crop (custom, shape, aspect ratio) - Crop (custom, shape, aspect ratio)
- Rounding
- Filters - Filters
- Tint (mask) - Tint (mask)
- Flip - Flip

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,17 +1,28 @@
import { computed, type Ref } from 'vue' import { computed, type Ref } from 'vue'
import { CLIPPATHS, ClipPathTypes } from '@/configs/imageClip' 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(() => { const clipShape = computed(() => {
if (!clip.value) return CLIPPATHS.rect let _clipShape = CLIPPATHS.rect
const shape = clip.value.shape || ClipPathTypes.RECT
return CLIPPATHS[shape] 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 _clipShape
}) })
const imgPosition = computed(() => { const imgPosition = computed(() => {
if (!clip.value) { if (!element.value.clip) {
return { return {
top: '0', top: '0',
left: '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 widthScale = (end[0] - start[0]) / 100
const heightScale = (end[1] - start[1]) / 100 const heightScale = (end[1] - start[1]) / 100