From 13d36614a7ab550ac6deca58e0a29cdde6b951fd Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Thu, 26 Dec 2024 16:29:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BD=BF=E7=94=A8=E7=A6=BB=E5=B1=8F=20C?= =?UTF-8?q?anvas=20=E6=B5=8B=E9=87=8F=E6=96=87=E6=9C=AC,=20=E6=8F=90?= =?UTF-8?q?=E9=AB=98=E5=AE=BD=E5=BA=A6=E8=AE=A1=E7=AE=97=E7=9A=84=E5=87=86?= =?UTF-8?q?=E7=A1=AE=E6=80=A7=20close=20#3018?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../spreadsheet/compare-layout-spec.ts | 193 +++++++++++++++++- .../__tests__/unit/utils/canvas-spec.ts | 2 +- packages/s2-core/scripts/test-live.mjs | 2 +- packages/s2-core/src/cell/base-cell.ts | 3 +- packages/s2-core/src/facet/base-facet.ts | 14 +- packages/s2-core/src/facet/pivot-facet.ts | 2 +- packages/s2-core/src/facet/table-facet.ts | 2 +- .../s2-core/src/sheet-type/spread-sheet.ts | 4 +- packages/s2-core/src/utils/canvas.ts | 4 +- packages/s2-react/playground/config.tsx | 5 +- packages/s2-react/scripts/test-live.mjs | 2 +- 11 files changed, 198 insertions(+), 35 deletions(-) diff --git a/packages/s2-core/__tests__/spreadsheet/compare-layout-spec.ts b/packages/s2-core/__tests__/spreadsheet/compare-layout-spec.ts index 7980c50b89..2ccf650541 100644 --- a/packages/s2-core/__tests__/spreadsheet/compare-layout-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/compare-layout-spec.ts @@ -21,7 +21,7 @@ const s2Options: S2Options = { describe('Compare Layout Tests', () => { const expectTextOverflowing = (s2: SpreadSheet) => { - [...s2.facet.getColCells(), ...s2.facet.getDataCells()].forEach((cell) => { + s2.facet.getCells().forEach((cell) => { expect(cell.getTextShape().isOverflowing()).toBeFalsy(); }); }; @@ -55,9 +55,9 @@ describe('Compare Layout Tests', () => { const colLeafNodes = s2.facet.getColLeafNodes(); - expect(Math.floor(colLeafNodes[0].width)).toBeCloseTo(133); + expect(Math.floor(colLeafNodes[0].width)).toBeCloseTo(122); expect(Math.floor(colLeafNodes[1].width)).toEqual( - options.showDefaultHeaderActionIcon ? 71 : 66, + options.showDefaultHeaderActionIcon ? 66 : 63, ); expectTextOverflowing(s2); }, @@ -84,15 +84,15 @@ describe('Compare Layout Tests', () => { }); await s2.render(); - const expectWidth = options.showDefaultHeaderActionIcon ? 71 : 66; + const expectWidth = options.showDefaultHeaderActionIcon ? 66 : 63; const isLargeFontSize = options.fontSize === 20; const colLeafNodes = s2.facet.getColLeafNodes(); expect(Math.floor(colLeafNodes[0].width)).toBeCloseTo( - isLargeFontSize ? 209 : 133, + isLargeFontSize ? 191 : 122, ); expect(Math.floor(colLeafNodes[1].width)).toEqual( - isLargeFontSize ? 97 : expectWidth, + isLargeFontSize ? 92 : expectWidth, ); expectTextOverflowing(s2); }); @@ -123,7 +123,7 @@ describe('Compare Layout Tests', () => { const colLeafNodes = s2.facet.getColLeafNodes(); const { dataCellWidthList, colLeafNodeWidthList } = mapWidthList(s2); - const expectWidth = 207; + const expectWidth = 183; expect(Math.floor(colLeafNodes[0].width)).toBeCloseTo(expectWidth); expect( @@ -171,13 +171,186 @@ describe('Compare Layout Tests', () => { expect(dataCellWidthList).toEqual( options.showDefaultHeaderActionIcon - ? [227, 227, 227, 227, 115, 115, 115, 115, 93, 93, 93, 93] - : [227, 227, 227, 227, 115, 115, 115, 115, 71, 71, 71, 71], + ? [209, 209, 209, 209, 110, 110, 110, 110, 85, 85, 85, 85] + : [209, 209, 209, 209, 110, 110, 110, 110, 69, 69, 69, 69], ); expect(colLeafNodeWidthList).toEqual( - options.showDefaultHeaderActionIcon ? [227, 115, 93] : [227, 115, 71], + options.showDefaultHeaderActionIcon ? [209, 110, 85] : [209, 110, 69], ); expectTextOverflowing(s2); }, ); + + test.each([{ showIcon: true }, { showIcon: false }])( + 'should get max row width for pivot sheet and format name by %o', + async (options) => { + const s2 = new PivotSheet( + getContainer(), + { + ...mockDataConfig, + meta: [ + { field: 'province', name: '省份666' }, + { field: 'city', name: '城市1234567' }, + ], + }, + { + ...s2Options, + headerActionIcons: options.showIcon + ? [ + { + icons: ['SortDown'], + belongsCell: 'cornerCell', + }, + ] + : [], + }, + ); + + await s2.render(); + + const rowNodes = s2.facet.getRowNodes(); + + expect(Math.floor(rowNodes[0].width)).toBeCloseTo( + options.showIcon ? 80 : 62, + ); + expect(Math.floor(rowNodes[1].width)).toEqual( + options.showIcon ? 106 : 88, + ); + expectTextOverflowing(s2); + }, + ); + + test('should not render overflowing text for table sheet and a difference type text', async () => { + const s2 = new TableSheet(getContainer(), mockDataConfig, s2Options); + + s2.setDataCfg({ + fields: { + columns: [ + 'date', + 'zh', + 'percentage', + 'number', + 'url-number', + 'url-en', + 'url-zh', + ], + }, + meta: [ + { + field: 'date', + formatter: () => '2021-09-08', + }, + { + field: 'zh', + formatter: () => '中文文本测试中文文本', + }, + { + field: 'percentage', + formatter: () => '100.23433333%', + }, + { + field: 'number', + formatter: () => '111111111111', + }, + { + field: 'url-number', + formatter: () => `https://wwww.test.cn?test=${'1'.repeat(10)}`, + }, + { + field: 'url-en', + formatter: () => `https://wwww.test.cn?test=${'t'.repeat(10)}`, + }, + { + field: 'url-zh', + formatter: () => `https://wwww.test.cn?test=${'测'.repeat(10)}`, + }, + ], + }); + + await s2.render(); + + expectTextOverflowing(s2); + }); + + test('should get max col width for pivot sheet conditions', async () => { + const s2 = new PivotSheet(getContainer(), mockDataConfig, { + ...s2Options, + conditions: { + icon: [ + { + field: 'price', + position: 'left', + mapping(fieldValue: number) { + if (!fieldValue) { + return null; + } + + return fieldValue > 0 + ? { + fill: 'red', + icon: 'CellUp', + } + : { + fill: 'green', + icon: 'CellDown', + }; + }, + }, + ], + }, + }); + + s2.setDataCfg({ + meta: [ + { + field: 'price', + formatter: (value) => (value === '111' ? '35333.7%' : value), + }, + ], + }); + + await s2.render(); + + const { dataCellWidthList, colLeafNodeWidthList } = mapWidthList(s2); + + expect(dataCellWidthList).toEqual([ + 140, 140, 140, 140, 81, 81, 81, 81, 92, 92, 92, 92, + ]); + expect(colLeafNodeWidthList).toEqual([140, 81, 92]); + expectTextOverflowing(s2); + }); + + test.each([ + { text: '中文文本测试中文文本测试', width: 145 }, + { text: '中文文本测试中文文本123', width: 142 }, + { text: '中文文本测试中文文本word', width: 150 }, + { text: '11111111111111111', width: 104 }, + { text: '参数:', width: 37 }, + { text: 'word', width: 30 }, + { text: 'word123', width: 50 }, + { text: 'word123...', width: 60 }, + { text: '100.234%', width: 56 }, + { text: '2024-12-24', width: 63 }, + { text: '纸张123456', width: 66 }, + { text: `https://wwww.test.cn?test=${'1'.repeat(10)}`, width: 217 }, + ])('should get correctly text width for %o', async ({ text, width }) => { + const s2 = new PivotSheet(getContainer(), mockDataConfig, { + ...s2Options, + }); + + await s2.render(); + + const result = s2.facet.measureTextWidth(text, { + fontFamily: 'Roboto, PingFangSC, Microsoft YaHei, Arial, sans-serif', + fontSize: 12, + fontWeight: 700, + fill: '#000000', + opacity: 1, + textAlign: 'left', + textBaseline: 'middle', + linkTextFill: '#2C60D4', + }); + + expect(result).toEqual(width); + }); }); diff --git a/packages/s2-core/__tests__/unit/utils/canvas-spec.ts b/packages/s2-core/__tests__/unit/utils/canvas-spec.ts index c4c1161a7a..cb4c38fb7e 100644 --- a/packages/s2-core/__tests__/unit/utils/canvas-spec.ts +++ b/packages/s2-core/__tests__/unit/utils/canvas-spec.ts @@ -2,7 +2,7 @@ import { getOffscreenCanvas, removeOffscreenCanvas } from '@/utils'; import { sleep } from './../../util/helpers'; describe('Canvas Utils Tests', () => { - const ID = 's2-offscreen-canvas'; + const ID = 'antv-s2-offscreen-canvas'; test('should get offscreen canvas', () => { const canvas = getOffscreenCanvas(); diff --git a/packages/s2-core/scripts/test-live.mjs b/packages/s2-core/scripts/test-live.mjs index 5acde67f40..ac491d2ea5 100644 --- a/packages/s2-core/scripts/test-live.mjs +++ b/packages/s2-core/scripts/test-live.mjs @@ -8,7 +8,7 @@ import ora from 'ora'; inquirer.registerPrompt('autocomplete', autoCompletePrompt); function run(path) { - const command = `cross-env DEBUG_MODE=1 npx jest ${path} --passWithNoTests --detectOpenHandles`; + const command = `cross-env DEBUG_MODE=1 npx jest ${path} --passWithNoTests`; const jestSpinner = ora(`[测试运行中]: ${command}`).start(); try { diff --git a/packages/s2-core/src/cell/base-cell.ts b/packages/s2-core/src/cell/base-cell.ts index 8b607b1a12..0d30616d91 100644 --- a/packages/s2-core/src/cell/base-cell.ts +++ b/packages/s2-core/src/cell/base-cell.ts @@ -491,13 +491,14 @@ export abstract class BaseCell extends Group { const maxTextWidth = Math.max(this.getMaxTextWidth(), 0) + EXTRA_PIXEL; const textStyle = this.getTextStyle(); const maxLines = this.getResizedTextMaxLines() || textStyle?.maxLines; + const text = this.getFieldValue()!; // 在坐标计算 (getTextPosition) 之前, 预渲染一次, 提前生成 textShape, 获得文字宽度, 用于计算 icon 绘制坐标 this.renderTextShape({ ...textStyle, x: 0, y: 0, - text: this.getFieldValue()!, + text, wordWrapWidth: maxTextWidth, maxLines, }); diff --git a/packages/s2-core/src/facet/base-facet.ts b/packages/s2-core/src/facet/base-facet.ts index f882447ffb..69959c710d 100644 --- a/packages/s2-core/src/facet/base-facet.ts +++ b/packages/s2-core/src/facet/base-facet.ts @@ -2454,22 +2454,10 @@ export abstract class BaseFacet { * @tip 和 this.spreadsheet.measureTextWidth() 的区别在于: * 1. 额外添加一像素余量,防止 maxLabel 有多个同样长度情况下,一些 label 不能展示完全, 出现省略号 * 2. 测量时, 文本宽度取整, 避免子像素的不一致性 - * 3. TODO: 由于 G 测量文本是一个一个字符进行计算, 在数字/英文等场景会有较大误差, 这里为了防止紧凑模式出现省略号, 暂时保持一样的策略 */ - protected measureTextWidth( - text: SimpleData, - font: unknown, - roughly = true, - ): number { + protected measureTextWidth(text: SimpleData, font: unknown): number { const EXTRA_PIXEL = 1; - if (roughly) { - return ( - Math.ceil(this.spreadsheet.measureTextWidthRoughly(text, font)) + - EXTRA_PIXEL - ); - } - return ( Math.ceil(this.spreadsheet.measureTextWidth(text, font)) + EXTRA_PIXEL ); diff --git a/packages/s2-core/src/facet/pivot-facet.ts b/packages/s2-core/src/facet/pivot-facet.ts index dc423659eb..f118aa4689 100644 --- a/packages/s2-core/src/facet/pivot-facet.ts +++ b/packages/s2-core/src/facet/pivot-facet.ts @@ -803,7 +803,7 @@ export class PivotFacet extends FrozenFacet { * 额外增加 1,当内容和容器宽度恰好相等时会出现换行 */ const maxLabelWidth = - this.measureTextWidth(treeHeaderLabel, cornerCellTextStyle, false) + + this.measureTextWidth(treeHeaderLabel, cornerCellTextStyle) + cornerIconStyle.size * 2 + cornerIconStyle.margin?.left + cornerIconStyle.margin?.right + diff --git a/packages/s2-core/src/facet/table-facet.ts b/packages/s2-core/src/facet/table-facet.ts index 5e26b85d9f..b22e870130 100644 --- a/packages/s2-core/src/facet/table-facet.ts +++ b/packages/s2-core/src/facet/table-facet.ts @@ -134,7 +134,7 @@ export class TableFacet extends FrozenFacet { const iconX = viewportWidth / 2 - icon.width / 2; const iconY = height / 2 + maxY - icon.height / 2 + icon.margin.top; const text = empty?.description ?? i18n('暂无数据'); - const descWidth = this.measureTextWidth(text, description, false); + const descWidth = this.measureTextWidth(text, description); const descX = viewportWidth / 2 - descWidth / 2; const descY = iconY + icon.height + icon.margin.bottom; diff --git a/packages/s2-core/src/sheet-type/spread-sheet.ts b/packages/s2-core/src/sheet-type/spread-sheet.ts index 91723a6e43..8ccbd217db 100644 --- a/packages/s2-core/src/sheet-type/spread-sheet.ts +++ b/packages/s2-core/src/sheet-type/spread-sheet.ts @@ -65,7 +65,7 @@ import { RootInteraction } from '../interaction/root'; import { getTheme } from '../theme'; import { HdAdapter } from '../ui/hd-adapter'; import { BaseTooltip } from '../ui/tooltip'; -import { removeOffscreenCanvas } from '../utils/canvas'; +import { getOffscreenCanvas, removeOffscreenCanvas } from '../utils/canvas'; import { clearValueRangeState } from '../utils/condition/state-controller'; import { hideColumnsByThunkGroup } from '../utils/hide-columns'; import { isMobile } from '../utils/is-mobile'; @@ -784,7 +784,7 @@ export abstract class SpreadSheet extends EE { return null; } - const ctx = this.getCanvasElement()?.getContext('2d')!; + const ctx = getOffscreenCanvas()?.getContext('2d')!; const { fontSize, fontFamily, fontWeight, fontStyle, fontVariant } = font as CSSStyleDeclaration; diff --git a/packages/s2-core/src/utils/canvas.ts b/packages/s2-core/src/utils/canvas.ts index fe96b36f65..aa777e9533 100644 --- a/packages/s2-core/src/utils/canvas.ts +++ b/packages/s2-core/src/utils/canvas.ts @@ -1,4 +1,6 @@ -const OFFSCREEN_CANVAS_DOM_ID = 's2-offscreen-canvas'; +import { S2_PREFIX_CLS } from '../common/constant/classnames'; + +const OFFSCREEN_CANVAS_DOM_ID = `${S2_PREFIX_CLS}-offscreen-canvas`; /** * 获取工具 canvas diff --git a/packages/s2-react/playground/config.tsx b/packages/s2-react/playground/config.tsx index 74be56b948..6fe753173a 100644 --- a/packages/s2-react/playground/config.tsx +++ b/packages/s2-react/playground/config.tsx @@ -103,7 +103,7 @@ export const pivotSheetDataCfgForCompactMode = customMerge( { province: '浙江省', city: '杭州市', - sub_type: '纸张', + sub_type: '纸张123456', type: '办公用品', number: 2, }, @@ -112,7 +112,7 @@ export const pivotSheetDataCfgForCompactMode = customMerge( city: '舟山市', sub_type: '笔', type: '办公用品', - number: 2, + number: '20000.334%', }, { province: '浙江省', @@ -412,7 +412,6 @@ export const s2Options: SheetComponentOptions = { // ], // ], tooltip: S2TooltipOptions, - style: {}, }; export const sliderOptions: SliderSingleProps = { diff --git a/packages/s2-react/scripts/test-live.mjs b/packages/s2-react/scripts/test-live.mjs index 5ce0d08372..72d2d0a299 100644 --- a/packages/s2-react/scripts/test-live.mjs +++ b/packages/s2-react/scripts/test-live.mjs @@ -8,7 +8,7 @@ import ora from 'ora'; inquirer.registerPrompt('autocomplete', autoCompletePrompt); function run(path) { - const command = `cross-env DEBUG_MODE=1 npx jest ${path} --passWithNoTests --detectOpenHandles`; + const command = `cross-env DEBUG_MODE=1 npx jest ${path} --passWithNoTests`; const jestSpinner = ora(`[测试运行中]: ${command}`).start(); try {