From 9d6fd394ae25ac620b594fe0460d21580d5fdaa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=A6=82=E6=80=80=E5=BF=B5=EF=BC=88=E4=BA=91?= =?UTF-8?q?=E8=B0=8C=EF=BC=89?= Date: Thu, 2 Jan 2025 10:30:03 +0800 Subject: [PATCH] fix: multi-line text wrapping exception (#1887) --- .changeset/popular-items-wave.md | 5 +++ __tests__/demos/bugfix/textWordWrap.ts | 33 +++++++++++++- packages/g-lite/src/services/TextService.ts | 48 +++++++++++++-------- 3 files changed, 66 insertions(+), 20 deletions(-) create mode 100644 .changeset/popular-items-wave.md diff --git a/.changeset/popular-items-wave.md b/.changeset/popular-items-wave.md new file mode 100644 index 000000000..0ca4fca12 --- /dev/null +++ b/.changeset/popular-items-wave.md @@ -0,0 +1,5 @@ +--- +'@antv/g-lite': patch +--- + +fix: multi-line text wrapping exception diff --git a/__tests__/demos/bugfix/textWordWrap.ts b/__tests__/demos/bugfix/textWordWrap.ts index 000518f50..243a97519 100644 --- a/__tests__/demos/bugfix/textWordWrap.ts +++ b/__tests__/demos/bugfix/textWordWrap.ts @@ -90,7 +90,7 @@ export async function textWordWrap(context: { canvas: Canvas }) { textBaseline: 'top', textOverflow: 'ellipsis', wordWrap: true, - wordWrapWidth: 30, + wordWrapWidth: 84, }, }); const rect2 = new Rect({ @@ -103,12 +103,43 @@ export async function textWordWrap(context: { canvas: Canvas }) { }, }); + const text3 = new Text({ + style: { + x: 300, + y: 300, + wordWrap: true, + wordWrapWidth: 2, + maxLines: 5, + textOverflow: 'ellipsis', + fontFamily: 'Roboto, PingFangSC, Microsoft YaHei, Arial, sans-serif', + fontSize: 12, + fontWeight: 700, + fill: '#000000', + opacity: 1, + textAlign: 'center', + textBaseline: 'middle', + linkTextFill: '#326EF4', + text: '千亿数据', + }, + }); + const rect3 = new Rect({ + style: { + x: text3.style.x, + y: text3.style.y, + width: text3.style.wordWrapWidth, + height: +text3.style.fontSize * text3.style.maxLines, + stroke: '#000000', + }, + }); + canvas.appendChild(text0); canvas.appendChild(rect0); canvas.appendChild(text1); canvas.appendChild(rect1); canvas.appendChild(text2); canvas.appendChild(rect2); + canvas.appendChild(text3); + canvas.appendChild(rect3); // benchmark // ---------- diff --git a/packages/g-lite/src/services/TextService.ts b/packages/g-lite/src/services/TextService.ts index 1cac297e0..323c500d7 100644 --- a/packages/g-lite/src/services/TextService.ts +++ b/packages/g-lite/src/services/TextService.ts @@ -360,6 +360,7 @@ export class TextService { let lines: string[] = []; let currentLineIndex = 0; let currentLineWidth = 0; + let prevLineLastCharIndex = 0; const cache: { [key in string]: number } = {}; const calcWidth = (txt: string): number => { @@ -377,32 +378,37 @@ export class TextService { * * @see https://github.com/antvis/G/issues/1833 * - * @param txt - Current line of text - * @param textCharIndex - The index of the last character of the current line in the entire text + * @param lineTxt - Current line of text + * @param txtLastCharIndex - The index of the last character of the current line in the entire text + * @param txtStartCharIndex - The index of the start character of the current line in the entire text */ function findCharIndexClosestWidthThreshold( - txt: string, - textCharIndex: number, + lineTxt: string, + txtLastCharIndex: number, + txtStartCharIndex: number, widthThreshold: number, ) { while ( - calcWidth(txt) < widthThreshold && - textCharIndex < chars.length - 1 + calcWidth(lineTxt) < widthThreshold && + txtLastCharIndex < chars.length - 1 ) { - if (self.isNewline(chars[textCharIndex + 1])) { + if (self.isNewline(chars[txtLastCharIndex + 1])) { break; } - textCharIndex += 1; - txt += chars[textCharIndex]; + txtLastCharIndex += 1; + lineTxt += chars[txtLastCharIndex]; } - while (calcWidth(txt) > widthThreshold && textCharIndex > 0) { - textCharIndex -= 1; - txt = txt.slice(0, -1); + while ( + calcWidth(lineTxt) > widthThreshold && + txtLastCharIndex > txtStartCharIndex + ) { + txtLastCharIndex -= 1; + lineTxt = lineTxt.slice(0, -1); } return { - txt, - textCharIndex, + lineTxt, + txtLastCharIndex, }; } @@ -422,10 +428,11 @@ export class TextService { const result = findCharIndexClosestWidthThreshold( lines[lineIndex], textCharIndex, + prevLineLastCharIndex + 1, maxWidth - ellipsisWidth, ); - lines[lineIndex] = result.txt + ellipsis; + lines[lineIndex] = result.lineTxt + ellipsis; } for (let i = 0; i < chars.length; i++) { @@ -446,6 +453,7 @@ export class TextService { break; } + prevLineLastCharIndex = i - 1; currentLineIndex += 1; currentLineWidth = 0; lines[currentLineIndex] = ''; @@ -457,16 +465,17 @@ export class TextService { const result = findCharIndexClosestWidthThreshold( lines[currentLineIndex], i - 1, + prevLineLastCharIndex + 1, maxWidth, ); - if (result.textCharIndex !== i - 1) { - lines[currentLineIndex] = result.txt; + if (result.txtLastCharIndex !== i - 1) { + lines[currentLineIndex] = result.lineTxt; - if (result.textCharIndex === chars.length - 1) { + if (result.txtLastCharIndex === chars.length - 1) { break; } - i = result.textCharIndex + 1; + i = result.txtLastCharIndex + 1; char = chars[i]; prevChar = chars[i - 1]; nextChar = chars[i + 1]; @@ -481,6 +490,7 @@ export class TextService { break; } + prevLineLastCharIndex = i - 1; currentLineIndex += 1; currentLineWidth = 0; lines[currentLineIndex] = '';