Skip to content

Commit 8ec54c5

Browse files
authored
fix: reduce set font and limit fillText out of viewbounds (#4800)
1 parent 4497c4f commit 8ec54c5

File tree

16 files changed

+179
-74
lines changed

16 files changed

+179
-74
lines changed

e2e/perf/scroll.spec.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { sheetData as emptySheetData } from '../__testing__/emptysheet';
2121
import { sheetData as freezeData } from '../__testing__/freezesheet';
2222
import { sheetData as mergeCellData } from '../__testing__/mergecell';
2323
import { sheetData as overflowData } from '../__testing__/overflow';
24+
import { reportToPosthog } from '../utils/report-performance';
2425

2526
export interface IFPSData {
2627
fpsData: number[];
@@ -39,7 +40,7 @@ interface IFPSResult {
3940
maxFrameTimes: number[];
4041
}
4142

42-
const isCI = !!process.env.CI;
43+
// const isCI = !!process.env.CI;
4344
/**
4445
* measure FPS of scrolling time.
4546
* @param page Page from playwright
@@ -132,7 +133,7 @@ async function measureFPS(page: Page, testDuration = 5, deltaX: number, deltaY:
132133
return fpsCounterPromise as Promise<IFPSResult>;
133134
}
134135

135-
const createTest = (title: string, sheetData: IJsonObject, minFpsValue: number, deltaX = 0, deltaY = 0) => {
136+
const createTest = (title: string, telemetryName: string, sheetData: IJsonObject, minFpsValue: number, deltaX = 0, deltaY = 0) => {
136137
// Default Size Of browser: 1280x720 pixels. And default DPR is 1.
137138
test(title, async ({ page }) => {
138139
await page.goto('http://localhost:3000/sheets/');
@@ -154,6 +155,8 @@ const createTest = (title: string, sheetData: IJsonObject, minFpsValue: number,
154155
console.log('FPS', resultOfFPS.fps);
155156
console.log('medianFrameTime', resultOfFPS.medianFrameTime);
156157
console.log('max10FrameTimes', resultOfFPS.maxFrameTimes);
158+
159+
await reportToPosthog(telemetryName, resultOfFPS);
157160
expect(resultOfFPS.fps).toBeGreaterThan(minFpsValue);
158161
});
159162
} catch (error) {
@@ -165,7 +168,7 @@ const createTest = (title: string, sheetData: IJsonObject, minFpsValue: number,
165168
});
166169
};
167170

168-
createTest('sheet scroll empty', emptySheetData, 50, 10, 100);
169-
createTest('sheet scroll after freeze', freezeData, 10, 10, 100);
170-
createTest('sheet scroll in a lots of merge cell', mergeCellData, 10, 10, 50);
171-
createTest('sheet X scroll in a lots of overflow', overflowData, 10, 50, 5);
171+
createTest('sheet scroll empty', 'perf.sheet.scroll.empty', emptySheetData, 50, 10, 100);
172+
createTest('sheet scroll after freeze', 'perf.sheet.scroll.freeze', freezeData, 10, 10, 100);
173+
createTest('sheet scroll in a lots of merge cell', 'perf.sheet.scroll.mergeCell', mergeCellData, 10, 10, 50);
174+
createTest('sheet X scroll in a lots of overflow', 'perf.sheet.scroll.overflow', overflowData, 10, 50, 5);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"scripts": {
2626
"prepare": "husky",
2727
"pre-commit": "lint-staged",
28-
"dev": "turbo dev:demo",
28+
"dev": "turbo dev:demo -- --host 0.0.0.0",
2929
"dev:libs": "pnpm --filter univer-examples dev:demo-libs",
3030
"dev:e2e": "pnpm --filter univer-examples dev:e2e",
3131
"lint:types": "turbo lint:types",

packages-experimental/debugger/src/controllers/e2e/e2e.controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
import { awaitTime, Disposable, ICommandService, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
18+
1819
import { DEFAULT_WORKBOOK_DATA_DEMO, DEFAULT_WORKBOOK_DATA_DEMO_DEFAULT_STYLE } from '@univerjs/mockdata';
1920
import { DisposeUniverCommand } from '../../commands/commands/unit.command';
2021
import { getDefaultDocData } from './data/default-doc';

packages/core/src/sheets/sheet-skeleton.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ export class SheetSkeleton extends Skeleton {
9797
this._isRowStylePrecedeColumnStyle = this._configService.getConfig(IS_ROW_STYLE_PRECEDE_COLUMN_STYLE) ?? false;
9898
}
9999

100-
_resetCache() {
100+
resetCache() {
101101
//
102102
}
103103

@@ -447,12 +447,16 @@ export class SheetSkeleton extends Skeleton {
447447
* @param bounds
448448
*/
449449
calculate(): Nullable<SheetSkeleton> {
450-
this._resetCache();
450+
this.resetCache();
451451
this._updateLayout();
452452

453453
return this;
454454
}
455455

456+
resetRangeCache(_ranges: IRange[]): void {
457+
// ...
458+
}
459+
456460
private _dynamicallyUpdateRowHeaderWidth(rowHeader: {
457461
width: number;
458462
}): number {

packages/engine-render/src/components/docs/doc-component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export abstract class DocComponent extends RenderComponent<
8282
}
8383
}
8484

85-
override render(mainCtx: UniverRenderingContext, bounds?: IViewportInfo) {
85+
override render(mainCtx: UniverRenderingContext, bounds?: Partial<IViewportInfo>) {
8686
if (!this.visible) {
8787
this.makeDirty(false);
8888
return this;
@@ -146,5 +146,5 @@ export abstract class DocComponent extends RenderComponent<
146146
return false;
147147
}
148148

149-
protected abstract _draw(ctx: UniverRenderingContext, bounds?: IViewportInfo): void;
149+
protected abstract _draw(ctx: UniverRenderingContext, bounds?: Partial<IViewportInfo>): void;
150150
}

packages/engine-render/src/components/docs/document.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import type { Transform } from '../../basics/transform';
2121
import type { IBoundRectNoAngle, IViewportInfo } from '../../basics/vector2';
2222
import type { UniverRenderingContext } from '../../context';
2323
import type { Scene } from '../../scene';
24-
import type { ComponentExtension, IExtensionConfig } from '../extension';
24+
import type { ComponentExtension, IDrawInfo, IExtensionConfig } from '../extension';
2525
import type { IDocumentsConfig, IPageMarginLayout } from './doc-component';
2626
import type { DocumentSkeleton } from './layout/doc-skeleton';
2727
import { CellValueType, HorizontalAlign, VerticalAlign, WrapStrategy } from '@univerjs/core';
@@ -432,7 +432,9 @@ export class Documents extends DocComponent {
432432

433433
for (const extension of glyphExtensionsExcludeBackground) {
434434
extension.extensionOffset = extensionOffset;
435-
extension.draw(ctx, parentScale, glyph);
435+
extension.draw(ctx, parentScale, glyph, [], {
436+
viewBound: bounds?.viewBound,
437+
} as IDrawInfo);
436438
}
437439
}
438440

packages/engine-render/src/components/docs/extensions/font-and-base-line.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
*/
1616

1717
import type { IScale } from '@univerjs/core';
18+
import type { IBoundRectNoAngle } from '../../../basics';
1819
import type { IDocumentSkeletonGlyph } from '../../../basics/i-document-skeleton-cached';
1920
import type { UniverRenderingContext } from '../../../context';
21+
import type { IDrawInfo } from '../../extension';
2022
import { BaselineOffset, getColorStyle } from '@univerjs/core';
2123
import { GlyphType, hasCJK } from '../../../basics';
2224
import { COLOR_BLACK_RGB } from '../../../basics/const';
@@ -29,14 +31,31 @@ const UNIQUE_KEY = 'DefaultDocsFontAndBaseLineExtension';
2931

3032
const DOC_EXTENSION_Z_INDEX = 20;
3133

34+
/**
35+
* Singleton
36+
*/
3237
export class FontAndBaseLine extends docExtension {
3338
override uKey = UNIQUE_KEY;
3439

3540
override Z_INDEX = DOC_EXTENSION_Z_INDEX;
3641

3742
private _preFontColor = '';
3843

39-
override draw(ctx: UniverRenderingContext, parentScale: IScale, glyph: IDocumentSkeletonGlyph) {
44+
/**
45+
* ctx.font = val; then ctx.font is not exactly the same as val
46+
* that is because canvas would normalize the font string, remove default value and convert pt to px.
47+
* so we need a map to store actual value and set value
48+
*/
49+
actualFontMap: Record<string, string> = {};
50+
51+
constructor() {
52+
super();
53+
}
54+
55+
// invoked by document.ts
56+
override draw(ctx: UniverRenderingContext, _parentScale: IScale, glyph: IDocumentSkeletonGlyph, _?: IBoundRectNoAngle, more?: IDrawInfo) {
57+
// _parentScale: IScale, _skeleton: T, _diffBounds?: V, _more?: IDrawInfo
58+
4059
const line = glyph.parent?.parent;
4160
if (!line) {
4261
return;
@@ -46,6 +65,15 @@ export class FontAndBaseLine extends docExtension {
4665

4766
const { spanPointWithFont = Vector2.create(0, 0) } = this.extensionOffset;
4867

68+
if (more) {
69+
if (more.viewBound) {
70+
// ctx.fillText('', x, y), the 'y' is the baseline of a character, not the left top of the character
71+
if (spanPointWithFont.x > more.viewBound.right || spanPointWithFont.y - glyph.bBox.aba > more.viewBound.bottom) {
72+
return;
73+
}
74+
}
75+
}
76+
4977
if (content == null) {
5078
return;
5179
}
@@ -55,8 +83,12 @@ export class FontAndBaseLine extends docExtension {
5583
return;
5684
}
5785

58-
if (ctx.font !== fontStyle?.fontString) {
59-
ctx.font = fontStyle?.fontString || '';
86+
const fontStringPxStr = fontStyle?.fontString || '';
87+
if (fontStringPxStr) {
88+
if (ctx.font !== this.actualFontMap[fontStringPxStr]) {
89+
ctx.font = fontStringPxStr;
90+
this.actualFontMap[fontStringPxStr] = ctx.font;
91+
}
6092
}
6193

6294
const { cl: colorStyle, va: baselineOffset } = textStyle;
@@ -71,7 +103,6 @@ export class FontAndBaseLine extends docExtension {
71103
} else if (baselineOffset === BaselineOffset.SUBSCRIPT) {
72104
spanPointWithFont.y += bBox.sbo;
73105
}
74-
75106
this._fillText(ctx, glyph, spanPointWithFont);
76107
}
77108

packages/engine-render/src/components/docs/layout/shaping-engine/font-cache.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,12 @@ export class FontCache {
218218
}, fontStyle);
219219
}
220220

221-
// 获取有值单元格文本大小
222-
// let measureTextCache = {}, measureTextCacheTimeOut = null;
221+
/**
222+
* Measure text on another canvas.
223+
* @param content
224+
* @param fontString
225+
* @returns IMeasureTextCache
226+
*/
223227
static getMeasureText(content: string, fontString: string): IMeasureTextCache {
224228
if (!this._context) {
225229
const canvas = document.createElement('canvas');
@@ -242,7 +246,6 @@ export class FontCache {
242246
if (mtc != null) {
243247
return mtc;
244248
}
245-
246249
ctx.font = fontString;
247250

248251
const textMetrics = ctx.measureText(content);

packages/engine-render/src/components/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export class ComponentExtension<T, U, V> {
5656
return this.Z_INDEX;
5757
}
5858

59-
draw(ctx: UniverRenderingContext, parentScale: IScale, skeleton: T, diffBounds?: V, more?: IDrawInfo) {
59+
draw(_ctx: UniverRenderingContext, _parentScale: IScale, _skeleton: T, _diff?: V, _more?: IDrawInfo) {
6060
/* abstract */
6161
}
6262

0 commit comments

Comments
 (0)