From e63f699dd5ce036900580f748c82d5e9eae5d613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=98=89=E4=B8=80?= Date: Sun, 7 Aug 2022 23:41:24 +0800 Subject: [PATCH 1/6] =?UTF-8?q?refactor:=20measureTextWidth=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E4=B8=BAspreadsheet=E5=AE=9E=E4=BE=8B=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-core/src/cell/base-cell.ts | 16 ++--- packages/s2-core/src/cell/corner-cell.ts | 9 ++- .../s2-core/src/facet/header/series-number.ts | 12 ++-- packages/s2-core/src/facet/pivot-facet.ts | 25 +++---- packages/s2-core/src/facet/table-facet.ts | 18 +++-- .../s2-core/src/sheet-type/spread-sheet.ts | 66 +++++++++++++++++-- packages/s2-core/src/utils/g-mini-charts.ts | 7 +- packages/s2-core/src/utils/text.ts | 13 +++- 8 files changed, 114 insertions(+), 52 deletions(-) diff --git a/packages/s2-core/src/cell/base-cell.ts b/packages/s2-core/src/cell/base-cell.ts index 7dec33e242..bd6c82eaaa 100644 --- a/packages/s2-core/src/cell/base-cell.ts +++ b/packages/s2-core/src/cell/base-cell.ts @@ -34,11 +34,7 @@ import { } from '../utils/cell/cell'; import { renderLine, renderText, updateShapeAttr } from '../utils/g-renders'; import { isMobile } from '../utils/is-mobile'; -import { - getEllipsisText, - getEmptyPlaceholder, - measureTextWidth, -} from '../utils/text'; +import { getEllipsisText, getEmptyPlaceholder } from '../utils/text'; export abstract class BaseCell extends Group { // cell's data meta info @@ -201,9 +197,13 @@ export abstract class BaseCell extends Group { const { formattedValue } = this.getFormattedFieldValue(); const maxTextWidth = this.getMaxTextWidth(); const textStyle = this.getTextStyle(); - const { placeholder } = this.spreadsheet.options; + const { + options: { placeholder }, + measureTextWidth, + } = this.spreadsheet; const emptyPlaceholder = getEmptyPlaceholder(this, placeholder); const ellipsisText = getEllipsisText({ + measureTextWidth, text: formattedValue, maxWidth: maxTextWidth, fontParam: textStyle, @@ -233,13 +233,13 @@ export abstract class BaseCell extends Group { const device = this.spreadsheet.options.style.device; // 配置了链接跳转 if (!isMobile(device)) { - const { minX, maxX, maxY }: BBox = this.textShape.getBBox(); + const { minX, maxY }: BBox = this.textShape.getBBox(); this.linkFieldShape = renderLine( this, { x1: minX, y1: maxY + 1, - x2: maxX, + x2: minX + this.actualTextWidth, // 不用 bbox 的 maxX,因为文字宽度预估偏差较大 y2: maxY + 1, }, { stroke: linkFillColor, lineWidth: 1 }, diff --git a/packages/s2-core/src/cell/corner-cell.ts b/packages/s2-core/src/cell/corner-cell.ts index 7bdaff60d8..d86f1e37b1 100644 --- a/packages/s2-core/src/cell/corner-cell.ts +++ b/packages/s2-core/src/cell/corner-cell.ts @@ -38,11 +38,7 @@ import { getResizeAreaAttrs, } from '../utils/interaction/resize'; import { isIPhoneX } from '../utils/is-mobile'; -import { - getEllipsisText, - getEmptyPlaceholder, - measureTextWidth, -} from '../utils/text'; +import { getEllipsisText, getEmptyPlaceholder } from '../utils/text'; import { i18n } from './../common/i18n'; import { shouldAddResizeArea } from './../utils/interaction/resize'; import { HeaderCell } from './header-cell'; @@ -86,7 +82,9 @@ export class CornerCell extends HeaderCell { this.meta, this.spreadsheet.options.placeholder, ); + const { measureTextWidth } = this.spreadsheet; const text = getEllipsisText({ + measureTextWidth, text: cornerText, maxWidth, fontParam: textStyle, @@ -106,6 +104,7 @@ export class CornerCell extends HeaderCell { secondLine = cornerText.slice(lastIndex); // 第二行重新计算...逻辑 secondLine = getEllipsisText({ + measureTextWidth, text: secondLine, maxWidth, fontParam: textStyle, diff --git a/packages/s2-core/src/facet/header/series-number.ts b/packages/s2-core/src/facet/header/series-number.ts index 64c04a181c..c0ca50c06a 100644 --- a/packages/s2-core/src/facet/header/series-number.ts +++ b/packages/s2-core/src/facet/header/series-number.ts @@ -1,14 +1,9 @@ import type { Group, IGroup, IShape } from '@antv/g-canvas'; import { each } from 'lodash'; -import { - CellBorderPosition, - type Padding, - type ViewMeta, -} from '../../common/interface'; +import { CellBorderPosition, type Padding } from '../../common/interface'; import type { SpreadSheet } from '../../sheet-type/index'; import { getBorderPositionAndStyle } from '../../utils/cell/cell'; import { renderLine, renderRect } from '../../utils/g-renders'; -import { measureTextWidth } from '../../utils/text'; import { getAdjustPosition } from '../../utils/text-absorption'; import type { PanelBBox } from '../bbox/panelBBox'; import { Node } from '../layout/node'; @@ -198,7 +193,10 @@ export class SeriesNumberHeader extends BaseHeader { private getTextPadding(text: string, cellWidth: number): Padding { const rowCellTheme = this.getStyle(); - const textWidth = measureTextWidth(text, rowCellTheme.seriesText); + const textWidth = this.headerConfig.spreadsheet.measureTextWidth( + text, + rowCellTheme.seriesText, + ); const padding = Math.max(Math.abs((cellWidth - textWidth) / 2), 4); return { ...rowCellTheme.cell.padding, diff --git a/packages/s2-core/src/facet/pivot-facet.ts b/packages/s2-core/src/facet/pivot-facet.ts index 4117443426..bb2f9214de 100644 --- a/packages/s2-core/src/facet/pivot-facet.ts +++ b/packages/s2-core/src/facet/pivot-facet.ts @@ -4,7 +4,6 @@ import { get, isArray, isEmpty, - isFunction, isNil, keys, last, @@ -14,14 +13,10 @@ import { size, } from 'lodash'; import { - DEFAULT_STYLE, DEFAULT_TREE_ROW_WIDTH, LAYOUT_SAMPLE_COUNT, - type CellCustomWidth, - type ColCfg, type IconTheme, type MultiData, - type RowCfg, } from '../common'; import { EXTRA_FIELD, LayoutWidthTypes, VALUE_FIELD } from '../common/constant'; import { CellTypes } from '../common/constant/interaction'; @@ -34,12 +29,7 @@ import { getIndexRangeWithOffsets, getSubTotalNodeWidthOrHeightByLevel, } from '../utils/facet'; -import { - getCellWidth, - measureTextWidth, - measureTextWidthRoughly, - safeJsonParse, -} from '../utils/text'; +import { getCellWidth, safeJsonParse } from '../utils/text'; import { BaseFacet } from './base-facet'; import { buildHeaderHierarchy } from './layout/build-header-hierarchy'; import type { Hierarchy } from './layout/hierarchy'; @@ -290,7 +280,7 @@ export class PivotFacet extends BaseFacet { colIconStyle, ); const leafNodeRoughWidth = - measureTextWidthRoughly(leafNodeLabel) + iconWidth; + this.spreadsheet.measureTextWidthRoughly(leafNodeLabel) + iconWidth; // 采样 50 个 label,逐个计算找出最长的 label let maxDataLabel: string; @@ -314,7 +304,8 @@ export class PivotFacet extends BaseFacet { cellData, filterDisplayDataItem, )}`; - const cellLabelWidth = measureTextWidthRoughly(cellLabel); + const cellLabelWidth = + this.spreadsheet.measureTextWidthRoughly(cellLabel); if (cellLabelWidth > maxDataLabelWidth) { maxDataLabel = cellLabel; @@ -336,7 +327,7 @@ export class PivotFacet extends BaseFacet { ); return ( - measureTextWidth(maxLabel, colCellTextStyle) + + this.spreadsheet.measureTextWidth(maxLabel, colCellTextStyle) + colCellStyle.padding?.left + colCellStyle.padding?.right + appendedWidth @@ -751,7 +742,7 @@ export class PivotFacet extends BaseFacet { this.spreadsheet.theme.cornerCell; // 初始化角头时,保证其在树形模式下不换行,给与两个icon的宽度空余(tree icon 和 action icon),减少复杂的 action icon 判断 const maxLabelWidth = - measureTextWidth(treeHeaderLabel, cornerCellTextStyle) + + this.spreadsheet.measureTextWidth(treeHeaderLabel, cornerCellTextStyle) + cornerIconStyle.size * 2 + cornerIconStyle.margin?.left + cornerIconStyle.margin?.right + @@ -808,7 +799,7 @@ export class PivotFacet extends BaseFacet { ); const maxLabel = maxBy(allLabels, (label) => `${label}`.length); const rowNodeWidth = - measureTextWidth(maxLabel, rowTextStyle) + + spreadsheet.measureTextWidth(maxLabel, rowTextStyle) + rowIconWidth + rowCellStyle.padding.left + rowCellStyle.padding.right; @@ -821,7 +812,7 @@ export class PivotFacet extends BaseFacet { cornerIconStyle, ); const fieldNameNodeWidth = - measureTextWidth(fieldName, cornerTextStyle) + + spreadsheet.measureTextWidth(fieldName, cornerTextStyle) + cornerIconWidth + cornerCellStyle.padding.left + cornerCellStyle.padding.right; diff --git a/packages/s2-core/src/facet/table-facet.ts b/packages/s2-core/src/facet/table-facet.ts index d1b2f9bec6..03daac8b42 100644 --- a/packages/s2-core/src/facet/table-facet.ts +++ b/packages/s2-core/src/facet/table-facet.ts @@ -1,5 +1,14 @@ import type { Group, IElement, IGroup } from '@antv/g-canvas'; -import { get, isBoolean, isNil, last, maxBy, set, values } from 'lodash'; +import { + get, + isBoolean, + isNil, + last, + maxBy, + set, + spread, + values, +} from 'lodash'; import { TableSeriesCell } from '../cell'; import { FRONT_GROUND_GROUP_COL_FROZEN_Z_INDEX, @@ -35,7 +44,6 @@ import { } from '../utils/grid'; import type { PanelIndexes } from '../utils/indexes'; import { getValidFrozenOptions } from '../utils/layout/frozen'; -import { measureTextWidth, measureTextWidthRoughly } from '../utils/text'; import { BaseFacet } from './base-facet'; import { CornerBBox } from './bbox/cornerBBox'; import type { SeriesNumberHeader } from './header'; @@ -375,7 +383,7 @@ export class TableFacet extends BaseFacet { datas?.map((data) => `${data[col.key]}`)?.slice(0, 50) || []; // 采样取了前50 allLabels.push(colLabel); const maxLabel = maxBy(allLabels, (label) => - measureTextWidthRoughly(label), + spreadsheet.measureTextWidthRoughly(label), ); const { bolderText: colCellTextStyle } = spreadsheet.theme.colCell; @@ -391,7 +399,7 @@ export class TableFacet extends BaseFacet { // 最长的 Label 如果是列名,按列名的标准计算宽度 if (colLabel === maxLabel) { colWidth = - measureTextWidth(maxLabel, colCellTextStyle) + + spreadsheet.measureTextWidth(maxLabel, colCellTextStyle) + getOccupiedWidthForTableCol( this.spreadsheet, col, @@ -401,7 +409,7 @@ export class TableFacet extends BaseFacet { // 额外添加一像素余量,防止 maxLabel 有多个同样长度情况下,一些 label 不能展示完全 const EXTRA_PIXEL = 1; colWidth = - measureTextWidth(maxLabel, dataCellTextStyle) + + spreadsheet.measureTextWidth(maxLabel, dataCellTextStyle) + cellStyle.padding.left + cellStyle.padding.right + EXTRA_PIXEL; diff --git a/packages/s2-core/src/sheet-type/spread-sheet.ts b/packages/s2-core/src/sheet-type/spread-sheet.ts index 15148970fb..a9b539c696 100644 --- a/packages/s2-core/src/sheet-type/spread-sheet.ts +++ b/packages/s2-core/src/sheet-type/spread-sheet.ts @@ -1,10 +1,5 @@ import EE from '@antv/event-emitter'; -import { - Canvas, - Event as CanvasEvent, - Group, - type IGroup, -} from '@antv/g-canvas'; +import { Canvas, Event as CanvasEvent, type IGroup } from '@antv/g-canvas'; import { forEach, forIn, @@ -13,6 +8,8 @@ import { isEmpty, isFunction, isString, + memoize, + values, } from 'lodash'; import { BaseCell } from '../cell'; import { @@ -681,4 +678,61 @@ export abstract class SpreadSheet extends EE { this.off(event); }); } + + /** + * 计算文本在画布中的宽度 + * @param text 待计算的文本 + * @param font 文本 css 样式 + * @returns 文本宽度 + */ + public measureTextWidth = memoize( + (text: number | string = '', font: unknown): number => { + if (!font) { + return 0; + } + + const ctx = this.getCanvasElement()?.getContext('2d'); + const { fontSize, fontFamily, fontWeight, fontStyle, fontVariant } = + font as CSSStyleDeclaration; + + ctx.font = [ + fontStyle, + fontVariant, + fontWeight, + `${fontSize}px`, + fontFamily, + ] + .join(' ') + .trim(); + + return ctx.measureText(`${text}`).width; + }, + (text: any, font) => [text, ...values(font)].join(''), + ); + + /** + * 粗略计算文本在画布中的宽度 + * @param text 待计算的文本 + * @param font 文本 css 样式 + * @returns 文本宽度 + */ + public measureTextWidthRoughly = (text: any, font: any = {}): number => { + const alphaWidth = this.measureTextWidth('a', font); + const chineseWidth = this.measureTextWidth('蚂', font); + + let w = 0; + if (!text) { + return w; + } + + // eslint-disable-next-line no-restricted-syntax + for (const char of text) { + const code = char.charCodeAt(0); + + // /[\u0000-\u00ff]/ + w += code >= 0 && code <= 255 ? alphaWidth : chineseWidth; + } + + return w; + }; } diff --git a/packages/s2-core/src/utils/g-mini-charts.ts b/packages/s2-core/src/utils/g-mini-charts.ts index b17d25d86a..70376e1848 100644 --- a/packages/s2-core/src/utils/g-mini-charts.ts +++ b/packages/s2-core/src/utils/g-mini-charts.ts @@ -19,7 +19,7 @@ import { renderText, } from '../utils/g-renders'; import { CellTypes, MiniChartTypes } from '../common/constant'; -import { getEllipsisText, measureTextWidth } from './text'; +import { getEllipsisText } from './text'; interface FractionDigitsOptions { min: number; @@ -231,7 +231,7 @@ export const drawBullet = (value: BulletValue, cell: S2CellType) => { const dataCellStyle = cell.getStyle(CellTypes.DATA_CELL); const bulletStyle = dataCellStyle.miniChart.bullet; - const { x, y, height, width } = cell.getMeta(); + const { x, y, height, width, spreadsheet } = cell.getMeta(); const { progressBar, comparativeMeasure, rangeColors, backgroundColor } = bulletStyle; @@ -245,7 +245,7 @@ export const drawBullet = (value: BulletValue, cell: S2CellType) => { // 对于负数, 进度条计算按照 0 处理, 但是展示还是要显示原来的百分比 const measurePercent = transformRatioToPercent(measure, 2); const measurePercentWidth = Math.ceil( - measureTextWidth(measurePercent, dataCellStyle), + spreadsheet.measureTextWidth(measurePercent, dataCellStyle), ); const bulletWidth = progressBar.widthPercent * width - measurePercentWidth; @@ -316,6 +316,7 @@ export const drawBullet = (value: BulletValue, cell: S2CellType) => { positionX - padding.right, y + height / 2, getEllipsisText({ + measureTextWidth: spreadsheet.measureTextWidth, text: measurePercent, maxWidth: measureWidth, fontParam: dataCellStyle.text, diff --git a/packages/s2-core/src/utils/text.ts b/packages/s2-core/src/utils/text.ts index d2e70ddd8d..7ec7aea368 100644 --- a/packages/s2-core/src/utils/text.ts +++ b/packages/s2-core/src/utils/text.ts @@ -28,6 +28,7 @@ import { renderMiniChart } from './g-mini-charts'; /** * 计算文本在画布中的宽度 + * @deprecated 已废弃,该方法计算宽度不准确,请使用 spreadsheet 实例上的同名方法 */ export const measureTextWidth = memoize( (text: number | string = '', font: unknown): number => { @@ -53,11 +54,13 @@ export const measureTextWidth = memoize( * 算法(减少每次 measureText 的长度,measureText 的性能跟字符串时间相关): * 1. 先通过 STEP 逐步计算,找到最后一个小于 maxWidth 的字符串 * 2. 然后对最后这个字符串二分计算 + * @param measureTextWidth 文本宽度预估函数 * @param text 需要计算的文本, 由于历史原因 除了支持string,还支持空值,number和数组等 * @param maxWidth * @param font */ export const getEllipsisTextInner = ( + measureTextWidth: (text: number | string, font: unknown) => number, text: any, maxWidth: number, font: CSSStyleDeclaration, @@ -148,6 +151,7 @@ export const getEllipsisTextInner = ( * 然后分别乘以中文、符号的宽度 * @param text * @param font + * @deprecated 已废弃,该方法计算宽度不准确,请使用 spreadsheet 实例上的同名方法 */ export const measureTextWidthRoughly = (text: any, font: any = {}): number => { const alphaWidth = measureTextWidth('a', font); @@ -171,18 +175,21 @@ export const measureTextWidthRoughly = (text: any, font: any = {}): number => { /** * @desc 改良版 获取文本的 ... 文本(可传入 优先文本片段) + * @param measureTextWidth 文本长度计算函数 * @param text 需要计算的文本 * @param maxWidth * @param font optional 文本字体 或 优先显示的文本 * @param priority optional 优先显示的文本 */ export const getEllipsisText = ({ + measureTextWidth, text, maxWidth, fontParam, priorityParam, placeholder, }: { + measureTextWidth: (text: number | string, font: unknown) => number; text: string | number; maxWidth: number; fontParam?: unknown; @@ -201,6 +208,7 @@ export const getEllipsisText = ({ } if (!priority || !priority.length) { return getEllipsisTextInner( + measureTextWidth, finalText, maxWidth, font as CSSStyleDeclaration, @@ -253,6 +261,7 @@ export const getEllipsisText = ({ // fix-边界处理: when subWidth <= DOT_WIDTH 不做 ... 处理 if (remainWidth < subWidth && subWidth > DOT_WIDTH) { const ellipsis = getEllipsisTextInner( + measureTextWidth, subText, remainWidth, font as CSSStyleDeclaration, @@ -391,7 +400,7 @@ export const drawObjectText = ( } = cell.getContentArea(); const text = multiData || (cell.getMeta().fieldValue as MultiData); const { values: textValues } = text; - const { options } = cell?.getMeta().spreadsheet; + const { options, measureTextWidth } = cell.getMeta().spreadsheet; const { valuesCfg } = options.style.cellCfg; // 趋势分析表默认只作用一个条件(因为指标挂行头,每列都不一样,直接在回调里判断是否需要染色即可) const textCondition = options?.conditions?.text?.[0]; @@ -418,6 +427,7 @@ export const drawObjectText = ( calX(x, padding), y + labelHeight, getEllipsisText({ + measureTextWidth, text: text.label, maxWidth: totalTextWidth, fontParam: labelStyle, @@ -473,6 +483,7 @@ export const drawObjectText = ( curX, curY, getEllipsisText({ + measureTextWidth, text: curText, maxWidth: avgWidth, fontParam: curStyle, From e14bbf7831b88db8ae34c49bb2f7620da71b5438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=98=89=E4=B8=80?= Date: Sun, 7 Aug 2022 23:48:19 +0800 Subject: [PATCH 2/6] =?UTF-8?q?test:=20=E8=B0=83=E6=95=B4=20text=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../s2-core/__tests__/unit/utils/text-spec.ts | 119 ++++++++++-------- packages/s2-core/src/cell/base-cell.ts | 2 +- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/packages/s2-core/__tests__/unit/utils/text-spec.ts b/packages/s2-core/__tests__/unit/utils/text-spec.ts index 682ea47ce9..0c0d0662f8 100644 --- a/packages/s2-core/__tests__/unit/utils/text-spec.ts +++ b/packages/s2-core/__tests__/unit/utils/text-spec.ts @@ -1,8 +1,8 @@ +import { createPivotSheet } from 'tests/util/helpers'; import { getEllipsisText, getEllipsisTextInner, isUpDataValue, - measureTextWidth, getCellWidth, getEmptyPlaceholder, } from '@/utils/text'; @@ -16,74 +16,91 @@ describe('Text Utils Tests', () => { fontWeight: 'normal', } as unknown as CSSStyleDeclaration; - test('should get correct text', () => { - const text = getEllipsisText({ - text: '12', - maxWidth: 200, - placeholder: '--', + describe('Test Widths Tests', () => { + let measureTextWidth: (text: number | string, font: unknown) => number; + + beforeEach(() => { + measureTextWidth = createPivotSheet( + {}, + { useSimpleData: true }, + ).measureTextWidth; }); - expect(text).toEqual('12'); - }); + test('should get correct text', () => { + const text = getEllipsisText({ + measureTextWidth, + text: '12', + maxWidth: 200, + placeholder: '--', + }); - test('should get correct text ellipsis', () => { - const text = getEllipsisText({ - text: '12121212121212121212', - maxWidth: 20, - placeholder: '--', + expect(text).toEqual('12'); }); - expect(text).toEndWith('...'); - expect(text.length).toBeLessThanOrEqual(5); - }); + test('should get correct text ellipsis', () => { + const text = getEllipsisText({ + measureTextWidth, + text: '12121212121212121212', + maxWidth: 20, + placeholder: '--', + }); - test('should get correct placeholder text with ""', () => { - const text = getEllipsisText({ - text: '', - maxWidth: 20, - placeholder: '--', + expect(text).toEndWith('...'); + expect(text.length).toBeLessThanOrEqual(5); }); - expect(text).toEqual('--'); - }); - test('should get correct placeholder text with 0', () => { - const text = getEllipsisText({ - text: 0 as unknown as string, - maxWidth: 20, - placeholder: '--', + test('should get correct placeholder text with ""', () => { + const text = getEllipsisText({ + measureTextWidth, + text: '', + maxWidth: 20, + placeholder: '--', + }); + expect(text).toEqual('--'); }); - expect(text).toEqual('0'); - }); + test('should get correct placeholder text with 0', () => { + const text = getEllipsisText({ + measureTextWidth, + text: 0 as unknown as string, + maxWidth: 20, + placeholder: '--', + }); - test('should get correct placeholder text with null', () => { - const text = getEllipsisText({ - text: null, - maxWidth: 20, - placeholder: '--', + expect(text).toEqual('0'); }); - expect(text).toEqual('--'); - }); + test('should get correct placeholder text with null', () => { + const text = getEllipsisText({ + measureTextWidth, + text: null, + maxWidth: 20, + placeholder: '--', + }); - test('should get correct ellipsis text', () => { - const text = getEllipsisText({ - text: '长度测试', - maxWidth: 24, + expect(text).toEqual('--'); }); - expect(text).toEndWith('...'); - expect(text.length).toBeLessThanOrEqual(4); - }); + test('should get correct ellipsis text', () => { + const text = getEllipsisText({ + measureTextWidth, + text: '长度测试', + maxWidth: 24, + }); - test('should get correct text width', () => { - const width = measureTextWidth('test', font); - expect(Math.floor(width)).toEqual(isHD ? 21 : 16); - }); + expect(text).toEndWith('...'); + expect(text.length).toBeLessThanOrEqual(4); + }); - test('should get correct ellipsis text inner', () => { - const text = getEllipsisTextInner('test', 15, font); - expect(text).toEqual('t...'); + test('should get correct text width', () => { + const width = measureTextWidth('test', font); + expect(Math.floor(width)).toEqual(isHD ? 21 : 16); + }); + + test('should get correct ellipsis text inner', () => { + const text = getEllipsisTextInner(measureTextWidth, 'test', 15, font); + expect(text).toEqual('t...'); + }); }); test.each` diff --git a/packages/s2-core/src/cell/base-cell.ts b/packages/s2-core/src/cell/base-cell.ts index bd6c82eaaa..e15f58ae76 100644 --- a/packages/s2-core/src/cell/base-cell.ts +++ b/packages/s2-core/src/cell/base-cell.ts @@ -239,7 +239,7 @@ export abstract class BaseCell extends Group { { x1: minX, y1: maxY + 1, - x2: minX + this.actualTextWidth, // 不用 bbox 的 maxX,因为文字宽度预估偏差较大 + x2: minX + this.actualTextWidth, // 不用 bbox 的 maxX,因为 g-base 文字宽度预估偏差较大 y2: maxY + 1, }, { stroke: linkFillColor, lineWidth: 1 }, From 02e159b64ec07e0d7f07cb512349d2a7c4ee5909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=98=89=E4=B8=80?= Date: Mon, 8 Aug 2022 11:43:12 +0800 Subject: [PATCH 3/6] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8Dtable-facet?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/unit/facet/table-facet-spec.ts | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/packages/s2-core/__tests__/unit/facet/table-facet-spec.ts b/packages/s2-core/__tests__/unit/facet/table-facet-spec.ts index cd1c73a68e..7ad52864c6 100644 --- a/packages/s2-core/__tests__/unit/facet/table-facet-spec.ts +++ b/packages/s2-core/__tests__/unit/facet/table-facet-spec.ts @@ -173,11 +173,33 @@ describe('Table Mode Facet Test With Adaptive Layout', () => { describe('Table Mode Facet Test With Compact Layout', () => { describe('should get correct col layout', () => { + const LABEL_WIDTH = [36, 36, 48, 24, 56]; // 采样的文本宽度 const ss: SpreadSheet = new MockSpreadSheet(); const dataSet: TableDataSet = new MockTableDataSet(ss); ss.getLayoutWidthType = () => { return 'compact'; }; + + const mockMeasureFunc = (text: string | number) => { + switch (text) { + case '浙江省': + return LABEL_WIDTH[0]; + case '杭州市': + return LABEL_WIDTH[1]; + case '办公用品': + return LABEL_WIDTH[2]; + case '沙发': + return LABEL_WIDTH[3]; + case 'undefined': + return LABEL_WIDTH[4]; + default: + return 0; + } + }; + ss.measureTextWidth = + mockMeasureFunc as unknown as SpreadSheet['measureTextWidth']; + ss.measureTextWidthRoughly = mockMeasureFunc; + const facet: TableFacet = new TableFacet({ spreadsheet: ss, dataSet, @@ -190,7 +212,6 @@ describe('Table Mode Facet Test With Compact Layout', () => { test('col hierarchy coordinate with compact layout', () => { const { colLeafNodes } = facet.layoutResult; - const COMPACT_WIDTH = [53, 53, 65, 41, 73]; let lastX = 0; @@ -205,11 +226,32 @@ describe('Table Mode Facet Test With Compact Layout', () => { }); describe('should get correct col layout with seriesNumber', () => { + const LABEL_WIDTH = [36, 36, 48, 24, 56]; // 采样的文本宽度 const ss: SpreadSheet = new MockSpreadSheet(); const dataSet: TableDataSet = new MockTableDataSet(ss); ss.getLayoutWidthType = () => { return 'compact'; }; + const mockMeasureFunc = (text: string | number) => { + switch (text) { + case '浙江省': + return LABEL_WIDTH[0]; + case '杭州市': + return LABEL_WIDTH[1]; + case '办公用品': + return LABEL_WIDTH[2]; + case '沙发': + return LABEL_WIDTH[3]; + case 'undefined': // seriesnumber & price + return LABEL_WIDTH[4]; + default: + return 0; + } + }; + ss.measureTextWidth = + mockMeasureFunc as unknown as SpreadSheet['measureTextWidth']; + ss.measureTextWidthRoughly = mockMeasureFunc; + const facet: TableFacet = new TableFacet({ spreadsheet: ss, dataSet, From 75684ed00ccbc45d7f676982bac227aea0fe57a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=98=89=E4=B8=80?= Date: Mon, 8 Aug 2022 14:09:35 +0800 Subject: [PATCH 4/6] =?UTF-8?q?test:=20=E4=BF=AE=E5=A4=8D=20pivot=20facet?= =?UTF-8?q?=20=E6=A0=91=E7=8A=B6=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts b/packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts index c0b5910be5..275f0ec92a 100644 --- a/packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts +++ b/packages/s2-core/__tests__/unit/facet/pivot-facet-spec.ts @@ -68,6 +68,8 @@ jest.mock('@/sheet-type', () => { interaction: { clearHoverTimer: jest.fn(), }, + measureTextWidth: + jest.fn() as unknown as SpreadSheet['measureTextWidth'], }; }), }; @@ -189,6 +191,7 @@ describe('Pivot Mode Facet Test', () => { describe('should get correct result when tree mode', () => { s2.isHierarchyTreeType = jest.fn().mockReturnValue(true); + const spy = jest.spyOn(s2, 'measureTextWidth').mockReturnValue(30); // 小于 DEFAULT_TREE_ROW_WIDTH const mockDataSet = new MockPivotDataSet(s2); const treeFacet = new PivotFacet({ spreadsheet: s2, @@ -200,6 +203,10 @@ describe('Pivot Mode Facet Test', () => { }); const { rowsHierarchy } = treeFacet.layoutResult; + afterAll(() => { + spy.mockRestore(); + }); + test('row hierarchy when tree mode', () => { const { cellCfg, spreadsheet } = facet.cfg; const rowCellStyle = spreadsheet.theme.rowCell.cell; From 3627212c0cdc072d8a72b35a654bc753b8363d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=98=89=E4=B8=80?= Date: Mon, 8 Aug 2022 14:27:09 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-core/src/sheet-type/spread-sheet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/s2-core/src/sheet-type/spread-sheet.ts b/packages/s2-core/src/sheet-type/spread-sheet.ts index a9b539c696..812d27d8cb 100644 --- a/packages/s2-core/src/sheet-type/spread-sheet.ts +++ b/packages/s2-core/src/sheet-type/spread-sheet.ts @@ -705,7 +705,7 @@ export abstract class SpreadSheet extends EE { .join(' ') .trim(); - return ctx.measureText(`${text}`).width; + return ctx.measureText(String(text)).width; }, (text: any, font) => [text, ...values(font)].join(''), ); From c89d41e6c267a5228b83086518465725c19b5ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=98=89=E4=B8=80?= Date: Tue, 9 Aug 2022 11:01:17 +0800 Subject: [PATCH 6/6] =?UTF-8?q?chore:=20deprecated=20=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/s2-core/src/utils/text.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/s2-core/src/utils/text.ts b/packages/s2-core/src/utils/text.ts index 7ec7aea368..62e22677ee 100644 --- a/packages/s2-core/src/utils/text.ts +++ b/packages/s2-core/src/utils/text.ts @@ -28,7 +28,7 @@ import { renderMiniChart } from './g-mini-charts'; /** * 计算文本在画布中的宽度 - * @deprecated 已废弃,该方法计算宽度不准确,请使用 spreadsheet 实例上的同名方法 + * @deprecated 已废弃,1.30.0 版本后移除。该方法计算宽度不准确,请使用 spreadsheet 实例上的同名方法 */ export const measureTextWidth = memoize( (text: number | string = '', font: unknown): number => { @@ -151,7 +151,7 @@ export const getEllipsisTextInner = ( * 然后分别乘以中文、符号的宽度 * @param text * @param font - * @deprecated 已废弃,该方法计算宽度不准确,请使用 spreadsheet 实例上的同名方法 + * @deprecated 已废弃,1.30.0 版本后移除。该方法计算宽度不准确,请使用 spreadsheet 实例上的同名方法 */ export const measureTextWidthRoughly = (text: any, font: any = {}): number => { const alphaWidth = measureTextWidth('a', font);