From 2d089b19d19732756e6dc1b0ea7367ff875af747 Mon Sep 17 00:00:00 2001 From: Zhong Date: Wed, 6 Aug 2025 17:55:33 +0800 Subject: [PATCH] fix(components): [watermark] resolve text clipping caused by textAlign (#21639) * fix(components): [watermark] resolve text clipping caused by textAlign * refactor(components): [watermark] update based on review feeddback * fix: update --- packages/components/watermark/src/useClips.ts | 15 +++++++++++++-- packages/components/watermark/src/watermark.vue | 10 ++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/components/watermark/src/useClips.ts b/packages/components/watermark/src/useClips.ts index beb3af7df1..f59231889c 100644 --- a/packages/components/watermark/src/useClips.ts +++ b/packages/components/watermark/src/useClips.ts @@ -4,6 +4,15 @@ import type { WatermarkProps } from './watermark' export const FontGap = 3 +// [alignRatio, spaceRatio] +const TEXT_ALIGN_RATIO_MAP = { + left: [0, 0.5], + start: [0, 0.5], + center: [0.5, 0], + right: [1, -0.5], + end: [1, -0.5], +} as const + function prepareCanvas( width: number, height: number, @@ -39,7 +48,8 @@ export default function useClips() { height: number, font: Required>, gapX: number, - gapY: number + gapY: number, + space: number ): [dataURL: string, finalWidth: number, finalHeight: number] { // ================= Text / Image ================= const [ctx, canvas, contentWidth, contentHeight] = prepareCanvas( @@ -70,9 +80,10 @@ export default function useClips() { ctx.textBaseline = textBaseline const contents = isArray(content) ? content : [content] contents?.forEach((item, index) => { + const [alignRatio, spaceRatio] = TEXT_ALIGN_RATIO_MAP[textAlign] ctx.fillText( item ?? '', - contentWidth / 2, + contentWidth * alignRatio + space * spaceRatio, index * (mergedFontSize + FontGap * ratio) ) }) diff --git a/packages/components/watermark/src/watermark.vue b/packages/components/watermark/src/watermark.vue index f86145dc72..90d7d2fc9b 100644 --- a/packages/components/watermark/src/watermark.vue +++ b/packages/components/watermark/src/watermark.vue @@ -112,6 +112,7 @@ const appendWatermark = (base64Url: string, markWidth: number) => { const getMarkSize = (ctx: CanvasRenderingContext2D) => { let defaultWidth = 120 let defaultHeight = 64 + let space = 0 const { image, content, width, height, rotate } = props @@ -144,12 +145,12 @@ const getMarkSize = (ctx: CanvasRenderingContext2D) => { maxHeight * contents.length + (contents.length - 1) * FontGap const angle = (Math.PI / 180) * Number(rotate) - const space = Math.ceil(Math.abs(Math.sin(angle) * defaultHeight) / 2) + space = Math.ceil(Math.abs(Math.sin(angle) * defaultHeight) / 2) defaultWidth += space } - return [width ?? defaultWidth, height ?? defaultHeight] as const + return [width ?? defaultWidth, height ?? defaultHeight, space] as const } const getClips = useClips() @@ -167,7 +168,7 @@ const renderWatermark = () => { } const ratio = getPixelRatio() - const [markWidth, markHeight] = getMarkSize(ctx) + const [markWidth, markHeight, space] = getMarkSize(ctx) const drawCanvas = ( drawContent?: NonNullable | HTMLImageElement @@ -188,7 +189,8 @@ const renderWatermark = () => { textBaseline: textBaseline.value, }, gapX.value, - gapY.value + gapY.value, + space ) appendWatermark(textClips, clipWidth)