mirror of
https://github.com/pipipi-pikachu/PPTist.git
synced 2025-04-15 02:20:00 +08:00
feat: 支持图片圆角
This commit is contained in:
parent
4950c3687d
commit
8dec4a59cf
@ -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
|
||||||
|
@ -87,6 +87,7 @@ npm run dev
|
|||||||
- 竖向文本
|
- 竖向文本
|
||||||
#### 图片
|
#### 图片
|
||||||
- 裁剪(自定义、按形状、按纵横比)
|
- 裁剪(自定义、按形状、按纵横比)
|
||||||
|
- 圆角
|
||||||
- 滤镜
|
- 滤镜
|
||||||
- 着色(蒙版)
|
- 着色(蒙版)
|
||||||
- 翻转
|
- 翻转
|
||||||
|
@ -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: '圆形',
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,15 @@
|
|||||||
<Button last class="popover-btn" style="width: 100%;"><IconDown /></Button>
|
<Button last class="popover-btn" style="width: 100%;"><IconDown /></Button>
|
||||||
</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 />
|
||||||
@ -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,
|
clip: { ..._handleElement.clip, shape, range },
|
||||||
props: {
|
left: originLeft + originWidth * (range[0][0] / 100),
|
||||||
clip: { ..._handleElement.clip, shape, range },
|
top: originTop + originHeight * (range[0][1] / 100),
|
||||||
left: originLeft + originWidth * (range[0][0] / 100),
|
width: originWidth * (range[1][0] - range[0][0]) / 100,
|
||||||
top: originTop + originHeight * (range[0][1] / 100),
|
height: originHeight * (range[1][1] - range[0][1]) / 100,
|
||||||
width: originWidth * (range[1][0] - range[0][0]) / 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,
|
left: originLeft,
|
||||||
props: {
|
top: originTop,
|
||||||
left: originLeft,
|
width: originWidth,
|
||||||
top: originTop,
|
height: originHeight,
|
||||||
width: originWidth,
|
|
||||||
height: originHeight,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
@ -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)
|
||||||
|
@ -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
|
|
||||||
|
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(() => {
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user