diff --git a/README.md b/README.md
index e305eeb7..6492e3a6 100644
--- a/README.md
+++ b/README.md
@@ -91,6 +91,7 @@ npm run dev
- Vertical text
#### Images
- Crop (custom, shape, aspect ratio)
+- Rounding
- Filters
- Tint (mask)
- Flip
diff --git a/README_zh.md b/README_zh.md
index d2b86860..ed341430 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -87,6 +87,7 @@ npm run dev
- 竖向文本
#### 图片
- 裁剪(自定义、按形状、按纵横比)
+- 圆角
- 滤镜
- 着色(蒙版)
- 翻转
diff --git a/src/configs/imageClip.ts b/src/configs/imageClip.ts
index da409d57..b3f41a46 100644
--- a/src/configs/imageClip.ts
+++ b/src/configs/imageClip.ts
@@ -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: '圆形',
diff --git a/src/types/slides.ts b/src/types/slides.ts
index 71140d70..24cf08ed 100644
--- a/src/types/slides.ts
+++ b/src/types/slides.ts
@@ -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
}
diff --git a/src/views/Editor/Toolbar/ElementStylePanel/ImageStylePanel.vue b/src/views/Editor/Toolbar/ElementStylePanel/ImageStylePanel.vue
index 71154449..f9f7afe3 100644
--- a/src/views/Editor/Toolbar/ElementStylePanel/ImageStylePanel.vue
+++ b/src/views/Editor/Toolbar/ElementStylePanel/ImageStylePanel.vue
@@ -40,6 +40,15 @@
+
+
+
圆角半径:
+
updateImage({ radius: value })"
+ style="width: 60%;"
+ />
+
@@ -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) => {
+ 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 = { 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,
})
}
diff --git a/src/views/components/element/ImageElement/BaseImageElement.vue b/src/views/components/element/ImageElement/BaseImageElement.vue
index c7bce8f4..2ffb35e5 100644
--- a/src/views/components/element/ImageElement/BaseImageElement.vue
+++ b/src/views/components/element/ImageElement/BaseImageElement.vue
@@ -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)
diff --git a/src/views/components/element/ImageElement/ImageOutline/index.vue b/src/views/components/element/ImageElement/ImageOutline/index.vue
index 09035920..7c41e8d5 100644
--- a/src/views/components/element/ImageElement/ImageOutline/index.vue
+++ b/src/views/components/element/ImageElement/ImageOutline/index.vue
@@ -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)
\ No newline at end of file
diff --git a/src/views/components/element/ImageElement/index.vue b/src/views/components/element/ImageElement/index.vue
index 972743a3..a4355158 100644
--- a/src/views/components/element/ImageElement/index.vue
+++ b/src/views/components/element/ImageElement/index.vue
@@ -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)
diff --git a/src/views/components/element/ImageElement/useClipImage.ts b/src/views/components/element/ImageElement/useClipImage.ts
index 02901223..b1136788 100644
--- a/src/views/components/element/ImageElement/useClipImage.ts
+++ b/src/views/components/element/ImageElement/useClipImage.ts
@@ -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) => {
+export default (element: Ref) => {
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) => {
}
}
- 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