From 46e153d588e78302e26827461a06682bbccd75aa Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Thu, 4 Apr 2024 19:03:58 +0800 Subject: [PATCH] fix: strikeout style rendering position #498 --- src/editor/core/draw/Draw.ts | 49 +++++++++++++++---- src/editor/core/draw/particle/TextParticle.ts | 18 ++++++- src/editor/core/draw/richtext/Strikeout.ts | 3 +- src/editor/dataset/constant/Common.ts | 2 + src/editor/utils/element.ts | 9 +++- 5 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index 18e2869..1b63717 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -80,7 +80,10 @@ import { I18n } from '../i18n/I18n' import { ImageObserver } from '../observer/ImageObserver' import { Zone } from '../zone/Zone' import { Footer } from './frame/Footer' -import { INLINE_ELEMENT_TYPE } from '../../dataset/constant/Element' +import { + INLINE_ELEMENT_TYPE, + TEXTLIKE_ELEMENT_TYPE +} from '../../dataset/constant/Element' import { ListParticle } from './particle/ListParticle' import { Placeholder } from './frame/Placeholder' import { EventBus } from '../event/eventbus/EventBus' @@ -1079,7 +1082,7 @@ export class Draw { ctx.direction = 'ltr' } - private _getFont(el: IElement, scale = 1): string { + public getElementFont(el: IElement, scale = 1): string { const { defaultSize, defaultFont } = this.options const font = el.font || defaultFont const size = el.actualSize || el.size || defaultSize @@ -1088,6 +1091,10 @@ export class Draw { }px ${font}` } + public getElementSize(el: IElement) { + return el.actualSize || el.size || this.options.defaultSize + } + public computeRowList(innerWidth: number, elementList: IElement[]) { const { defaultSize, defaultRowMargin, scale, tdPadding, defaultTabWidth } = this.options @@ -1414,7 +1421,7 @@ export class Draw { element.actualSize = Math.ceil(size * 0.6) } metrics.height = (element.actualSize || size) * scale - ctx.font = this._getFont(element) + ctx.font = this.getElementFont(element) const fontMetrics = this.textParticle.measureText(ctx, element) metrics.width = fontMetrics.width * scale if (element.letterSpacing) { @@ -1445,7 +1452,7 @@ export class Draw { rowMargin const rowElement: IRowElement = Object.assign(element, { metrics, - style: this._getFont(element, scale) + style: this.getElementFont(element, scale) }) // 暂时只考虑非换行场景:控件开始时统计宽度,结束时消费宽度及还原 if (rowElement.control?.minWidth) { @@ -1831,12 +1838,34 @@ export class Draw { } // 删除线记录 if (element.strikeout) { - this.strikeout.recordFillInfo( - ctx, - x, - y + curRow.height / 2, - metrics.width - ) + // 仅文本类元素支持删除线 + if (!element.type || TEXTLIKE_ELEMENT_TYPE.includes(element.type)) { + // 字体大小不同时需立即绘制 + if ( + preElement && + this.getElementSize(preElement) !== this.getElementSize(element) + ) { + this.strikeout.render(ctx) + } + // 基线文字测量信息 + const standardMetrics = this.textParticle.measureBasisWord( + ctx, + this.getElementFont(element) + ) + // 文字渲染位置 + 基线文字下偏移量 - 一半文字高度 + let adjustY = + y + + offsetY + + standardMetrics.actualBoundingBoxDescent * scale - + metrics.height / 2 + // 上下标位置调整 + if (element.type === ElementType.SUBSCRIPT) { + adjustY += this.subscriptParticle.getOffsetY(element) + } else if (element.type === ElementType.SUPERSCRIPT) { + adjustY += this.superscriptParticle.getOffsetY(element) + } + this.strikeout.recordFillInfo(ctx, x, adjustY, metrics.width) + } } else if (preElement?.strikeout) { this.strikeout.render(ctx) } diff --git a/src/editor/core/draw/particle/TextParticle.ts b/src/editor/core/draw/particle/TextParticle.ts index dc67663..b538861 100644 --- a/src/editor/core/draw/particle/TextParticle.ts +++ b/src/editor/core/draw/particle/TextParticle.ts @@ -1,5 +1,8 @@ import { ElementType, IEditorOption, IElement } from '../../..' -import { PUNCTUATION_LIST } from '../../../dataset/constant/Common' +import { + PUNCTUATION_LIST, + METRICS_BASIS_TEXT +} from '../../../dataset/constant/Common' import { DeepRequired } from '../../../interface/Common' import { IRowElement } from '../../../interface/Row' import { ITextMetrics } from '../../../interface/Text' @@ -33,6 +36,19 @@ export class TextParticle { this.cacheMeasureText = new Map() } + public measureBasisWord( + ctx: CanvasRenderingContext2D, + font: string + ): ITextMetrics { + ctx.save() + ctx.font = font + const textMetrics = this.measureText(ctx, { + value: METRICS_BASIS_TEXT + }) + ctx.restore() + return textMetrics + } + public measureWord( ctx: CanvasRenderingContext2D, elementList: IElement[], diff --git a/src/editor/core/draw/richtext/Strikeout.ts b/src/editor/core/draw/richtext/Strikeout.ts index ac371ee..0c456af 100644 --- a/src/editor/core/draw/richtext/Strikeout.ts +++ b/src/editor/core/draw/richtext/Strikeout.ts @@ -12,9 +12,10 @@ export class Strikeout extends AbstractRichText { public render(ctx: CanvasRenderingContext2D) { if (!this.fillRect.width) return - const { strikeoutColor } = this.options + const { scale, strikeoutColor } = this.options const { x, y, width } = this.fillRect ctx.save() + ctx.lineWidth = scale ctx.strokeStyle = strikeoutColor const adjustY = y + 0.5 // 从1处渲染,避免线宽度等于3 ctx.beginPath() diff --git a/src/editor/dataset/constant/Common.ts b/src/editor/dataset/constant/Common.ts index 3e90650..e4cbf2c 100644 --- a/src/editor/dataset/constant/Common.ts +++ b/src/editor/dataset/constant/Common.ts @@ -40,3 +40,5 @@ export const LETTER_CLASS = { SWEDISH: 'A-Za-zÅåÄäÖö', GREEK: 'ΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩω' } + +export const METRICS_BASIS_TEXT = '日' diff --git a/src/editor/utils/element.ts b/src/editor/utils/element.ts index d4689fb..2428bf4 100644 --- a/src/editor/utils/element.ts +++ b/src/editor/utils/element.ts @@ -754,6 +754,9 @@ export function convertElementToDom( if (element.underline) { dom.style.textDecoration = 'underline' } + if (element.strikeout) { + dom.style.textDecoration += ' line-through' + } dom.innerText = element.value.replace(new RegExp(`${ZERO}`, 'g'), '\n') return dom } @@ -981,9 +984,13 @@ export function convertTextNodeToElement( element.highlight = style.backgroundColor } // 下划线 - if (style.textDecorationLine === 'underline') { + if (style.textDecorationLine.includes('underline')) { element.underline = true } + // 删除线 + if (style.textDecorationLine.includes('line-through')) { + element.strikeout = true + } return element }