Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(sheet): render viewport wrong size #1727

Closed
wants to merge 13 commits into from
Prev Previous commit
Next Next commit
chore: comments to english
fix: expand shouldupdatecache

chore: better code
  • Loading branch information
lumixraku committed May 24, 2024
commit cfae458e08be5f39f30521274ab2d9514d588c40
26 changes: 26 additions & 0 deletions packages/core/src/shared/rectangle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,4 +283,30 @@ export class Rectangle {

return result;
}

static hasIntersectionBetweenTwoBounds(
rect1: {
left: number;
top: number;
right: number;
bottom: number;
},
rect2: {
left: number;
top: number;
right: number;
bottom: number;
}
) {
if (
rect1.left > rect2.right || // rect1 在 rect2 右侧
rect1.right < rect2.left || // rect1 在 rect2 左侧
rect1.top > rect2.bottom || // rect1 在 rect2 下方
rect1.bottom < rect2.top // rect1 在 rect2 上方
) {
return false;
}

return true;
}
}
31 changes: 0 additions & 31 deletions packages/core/src/shared/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,35 +671,4 @@ export class Tools {

return !containsInvalidChars && isValidLength;
}

static hasIntersectionBetweenTwoBounds(
rect1: {
left: number;
top: number;
right: number;
bottom: number;
},
rect2: {
left: number;
top: number;
right: number;
bottom: number;
}
) {
// const rect1Right = rect1.left + rect1.width;
// const rect2Right = rect2.left + rect2.width;
// const rect1Bottom = rect1.top + rect1.height;
// const rect2Bottom = rect2.top + rect2.height;

if (
rect1.left > rect2.right || // rect1 在 rect2 右侧
rect1.right < rect2.left || // rect1 在 rect2 左侧
rect1.top > rect2.bottom || // rect1 在 rect2 下方
rect1.bottom < rect2.top // rect1 在 rect2 上方
) {
return false;
}

return true;
}
}
5 changes: 5 additions & 0 deletions packages/engine-render/src/base-object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export abstract class BaseObject extends Disposable {
protected _oKey: string;

protected _dirty: boolean = true;
protected _forceDirty: boolean = true;

private _top: number = 0;

Expand Down Expand Up @@ -367,6 +368,10 @@ export abstract class BaseObject extends Disposable {
return this;
}

makeForceDirty(state: boolean = true) {
this._forceDirty = state;
}

makeDirtyNoDebounce(state: boolean = true) {
this._dirty = state;
if (state) {
Expand Down
17 changes: 8 additions & 9 deletions packages/engine-render/src/basics/vector2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import type { Canvas } from '../canvas';
import type { SHEET_VIEWPORT_KEY } from '../components/sheets/interfaces';
import type { DeepImmutable, FloatArray } from './i-events';
import type { Transform } from './transform';

Expand Down Expand Up @@ -857,27 +858,26 @@ export interface IBoundRectNoAngle {
}

export interface IViewportInfo {
/**
* viewBound 的 left right 包括列头行头
*/
viewBound: IBoundRectNoAngle;
diffBounds: IBoundRectNoAngle[];

/**
* 让更多右侧内容呈现 diffX < 0
* scroll right further diffX < 0
* previewBound.x - viewbound.x
*/
diffX: number;
diffY: number;

/**
* 冻结行列在 canvas 上的物理位置, drawImage 用
* 例如冻结从第四列开始, left 就是 4 * column + rowHeaderWidth
* The physical position of the frozen rows and columns on the canvas, used for drawImage.
* For example, if the freezing starts from the fourth column, the left position would be 4 * column + rowHeaderWidth.
* The physical position means the top and left values have already considered the scaling factor.
*/
viewPortPosition: IBoundRectNoAngle;
viewportKey: string;
viewportKey: string | SHEET_VIEWPORT_KEY;
/**
* 后续会通过 number 来表示究竟是什么原因导致的标脏 这里采用二进制数值方便运算
* In the future, a number will be used to indicate the reason for the "dirty" status
* Here, a binary value is used to facilitate computation.
*/
isDirty?: number;
isForceDirty?: boolean;
Expand All @@ -887,7 +887,6 @@ export interface IViewportInfo {
diffCacheBounds: IBoundRectNoAngle[];
cacheViewPortPosition: IBoundRectNoAngle;

// vp?: Viewport;
shouldCacheUpdate: number;
sceneTrans: Transform;
cacheCanvas?: Canvas;
Expand Down
9 changes: 7 additions & 2 deletions packages/engine-render/src/components/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import type { IDocumentRenderConfig, IScale, Nullable } from '@univerjs/core';
import type { IDocumentRenderConfig, IRange, IScale, Nullable } from '@univerjs/core';
import { Registry } from '@univerjs/core';

import type { BaseObject } from '../base-object';
Expand All @@ -31,6 +31,11 @@ export interface IExtensionConfig {
renderConfig?: IDocumentRenderConfig;
}

export interface IDrawInfo {
viewRanges: IRange[];
viewportKey: string;
checkOutOfViewBound?: boolean;
}
export class ComponentExtension<T, U, V> {
uKey: string = '';

Expand All @@ -50,7 +55,7 @@ export class ComponentExtension<T, U, V> {
return this.Z_INDEX;
}

draw(ctx: UniverRenderingContext, parentScale: IScale, skeleton: T, diffBounds?: V, more?: any) {
draw(ctx: UniverRenderingContext, parentScale: IScale, skeleton: T, diffBounds?: V, more?: IDrawInfo) {
/* abstract */
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type { IRange, IScale } from '@univerjs/core';

import { fixLineWidthByScale, getColor, inViewRanges, mergeRangeIfIntersects } from '../../../basics/tools';
import type { UniverRenderingContext } from '../../../context';
import type { IDrawInfo } from '../../extension';
import { SpreadsheetExtensionRegistry } from '../../extension';
import type { SpreadsheetSkeleton } from '../sheet-skeleton';
import type { Spreadsheet } from '../spreadsheet';
Expand Down Expand Up @@ -48,7 +49,7 @@ export class Background extends SheetExtension {
parentScale: IScale,
spreadsheetSkeleton: SpreadsheetSkeleton,
diffRanges: IRange[],
{ viewRanges, checkOutOfViewBound }: { viewRanges: IRange[]; checkOutOfViewBound: boolean; viewportKey: string }
{ viewRanges, checkOutOfViewBound }: IDrawInfo
) {
const { stylesCache } = spreadsheetSkeleton;
const { background, backgroundPositions } = stylesCache;
Expand All @@ -75,13 +76,12 @@ export class Background extends SheetExtension {

ctx.fillStyle = rgb || getColor([255, 255, 255])!;

const backgroundPaths = new Path2D(); // 使用 Path 对象代替原有的 ctx.moveTo ctx.lineTo, Path 性能更好
const backgroundPaths = new Path2D();
backgroundCache.forValue((rowIndex, columnIndex) => {
if (!checkOutOfViewBound && !inViewRanges(viewRanges, rowIndex, columnIndex)) {
return true;
}

// 合并单元格可能从视野外很远的位置开始, 因此需要全局遍历
const cellInfo = backgroundPositions?.getValue(rowIndex, columnIndex);
if (cellInfo == null) {
return true;
Expand All @@ -91,16 +91,18 @@ export class Background extends SheetExtension {
const mergeTo = diffRanges && diffRanges.length > 0 ? diffRanges : viewRanges;
const combineWithMergeRanges = mergeRangeIfIntersects(mergeTo, [mergeInfo]);

// 不在范围(视野 + 合并单元格)内, 提前退出
// If curr cell is not in the viewrange (viewport + merged cells), exit early.
if (!inViewRanges(combineWithMergeRanges!, rowIndex, columnIndex)) {
return true;
}

// 合并后的单元格 && 非左上角单元格 // 此刻需要使用左上角单元格的背景色
// For merged cells && cells that are not top-left,
// we need to use the background color of the top-left cell.
if (isMerged) {
return true;
}
// 合并单元格, 但是区域是合并的左上角

// For merged cells, and the current cell is the top-left cell in the merged region.
if (isMergedMainCell) {
startY = mergeInfo.startY;
endY = mergeInfo.endY;
Expand Down
45 changes: 24 additions & 21 deletions packages/engine-render/src/components/sheets/extensions/font.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import type { ICellData, IRange, IScale, ObjectMatrix } from '@univerjs/core';
import { HorizontalAlign, WrapStrategy } from '@univerjs/core';

import { VERTICAL_ROTATE_ANGLE } from '../../../basics/text-rotation';
import { inCurrentAndAboveViewRanges, inRowViewRanges, inViewRanges, mergeRangeIfIntersects } from '../../../basics/tools';
import { inRowViewRanges, inViewRanges, mergeRangeIfIntersects } from '../../../basics/tools';
import type { UniverRenderingContext } from '../../../context';
import type { Documents } from '../../docs/document';
import type { IDrawInfo } from '../../extension';
import { SpreadsheetExtensionRegistry } from '../../extension';
import type { IFontCacheItem } from '../interfaces';
import type { SheetComponent } from '../sheet-component';
Expand Down Expand Up @@ -57,7 +58,7 @@ export class Font extends SheetExtension {
parentScale: IScale,
spreadsheetSkeleton: SpreadsheetSkeleton,
diffRanges: IRange[],
moreBoundsInfo: { viewRanges: IRange[]; checkOutOfViewBound?: boolean; viewportKey: string }
moreBoundsInfo: IDrawInfo
) {
const { viewRanges = [], checkOutOfViewBound } = moreBoundsInfo;
const { stylesCache, dataMergeCache, overflowCache, worksheet } = spreadsheetSkeleton;
Expand All @@ -84,22 +85,18 @@ export class Font extends SheetExtension {
Object.keys(fontList).forEach((fontFormat: string) => {
const fontObjectArray = fontList[fontFormat];

// 提前退出渲染的条件
// viewMainTop 要考虑来自左右两边单元格的溢出
// viewLeft 要考虑来自上方合并单元格
// viewMain 要考虑左右溢出和上方合并的单元格的内容
// 因此, 在 viewBounds 中的下方的单元格 ---> 提前退出
// 而 viewMainLeftTop 不受 viewBounds 之外的影响, 因此视野外的单元格提前退出
// 此外, 不是溢出, 又不在视野内可以提前退出 (视野需要考虑合并单元格带来的影响)
// Since the overflow can spill out to both the left and right sides,
// we need to consider the content outside the viewBounds.
// At the same time, there are also merged cells, so we need to merge the current
// viewrange and a single merged area when calculating.

// Early exit from font condition
// If it's not an overflow and not within the field of view, we can exit early
// (the field of view needs to consider the impact of merged cells).

// eslint-disable-next-line complexity
fontObjectArray.forValue((rowIndex, columnIndex, docsConfig) => {
if (checkOutOfViewBound) {
// 下方单元格 提前退出
if (!inCurrentAndAboveViewRanges(viewRanges!, rowIndex)) {
return true;
}
} else {
if (!checkOutOfViewBound) {
if (!inViewRanges(viewRanges!, rowIndex, columnIndex)) {
return true;
}
Expand All @@ -118,10 +115,12 @@ export class Font extends SheetExtension {
return true;
}

// 合并后单元格与当前 viewRange 有交叉, 则合并到当前 viewRange 中
// 合并后 font extension 在当前 viewBounds 中也走一次绘制
// 但是此刻还不能认为不在 viewRanges 内就退出
// 横向还可能存在 overflow, 因此此刻只能排除不在当前 row 的单元格
// If the merged cell area intersects with the current viewRange,
// then merge it into the current viewRange.
// After the merge, the font extension within the current viewBounds
// also needs to be drawn once.
// But at this moment, we cannot assume that it is not within the viewRanges and exit, because there may still be horizontal overflow.
// At this moment, we can only exclude the cells that are not within the current row.
const mergeTo = diffRanges && diffRanges.length > 0 ? diffRanges : viewRanges;
const combineWithMergeRanges = mergeRangeIfIntersects(mergeTo, [mergeInfo]);
if (!inRowViewRanges(combineWithMergeRanges, rowIndex)) {
Expand Down Expand Up @@ -160,11 +159,14 @@ export class Font extends SheetExtension {
return true;
}

// 单元格是否溢出 没有设置溢出 overflowRectangle 为 undefined
// If the cell is overflowing, but the overflowRectangle has not been set,
// then overflowRectangle is set to undefined.
const overflowRectangle = overflowCache.getValue(rowIndex, columnIndex);
const { horizontalAlign, vertexAngle = 0, centerAngle = 0 } = docsConfig;

// 既不是溢出, 又不在当前 range 内, 那么提前退出(已考虑合并单元格带来的 range 扩展)
// If it's neither an overflow nor within the current range,
// then we can exit early (taking into account the range extension
// caused by the merged cells).
if (!overflowRectangle && !inViewRanges(combineWithMergeRanges, rowIndex, columnIndex)) {
return true;
}
Expand Down Expand Up @@ -269,6 +271,7 @@ export class Font extends SheetExtension {
}
ctx.translate(startX + FIX_ONE_PIXEL_BLUR_OFFSET, startY + FIX_ONE_PIXEL_BLUR_OFFSET);
this._renderDocuments(ctx, docsConfig, startX, startY, endX, endY, rowIndex, columnIndex, overflowCache);
ctx.closePath();
ctx.restore();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ export class SheetExtension extends ComponentExtension<SpreadsheetSkeleton, SHEE
* @param curStartRow
* @param curEndRow
* @param viewranges
* @returns
*/
isRowInRanges(curStartRow: number, curEndRow: number, viewranges?: IRange[]) {
if (viewranges == null || viewranges.length === 0) {
Expand All @@ -154,12 +153,12 @@ export class SheetExtension extends ComponentExtension<SpreadsheetSkeleton, SHEE

for (const range of viewranges) {
const { startRow, endRow } = range;
// if (curStartRow >= startRow && curStartRow <= endRow) {
// return true;
// }
// if (curEndRow >= startRow && curEndRow <= endRow) {
// return true;
// }
if (curStartRow >= startRow && curStartRow <= endRow) {
return true;
}
if (curEndRow >= startRow && curEndRow <= endRow) {
return true;
}

const isIntersect = Rectangle.intersects(
{
Expand Down
38 changes: 38 additions & 0 deletions packages/engine-render/src/components/sheets/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import type {

import type { BORDER_TYPE } from '../../basics/const';
import type { DocumentSkeleton } from '../docs/layout/doc-skeleton';
import type { Canvas } from '../../canvas';
import type { UniverRenderingContext } from '../../context';

export interface BorderCache {
[key: string]: BorderCacheItem | {};
Expand Down Expand Up @@ -69,3 +71,39 @@ export enum ShowGridlinesState {
OFF,
ON,
}

export enum SHEET_VIEWPORT_KEY {
VIEW_MAIN = 'viewMain',
VIEW_MAIN_LEFT_TOP = 'viewMainLeftTop',
VIEW_MAIN_TOP = 'viewMainTop',
VIEW_MAIN_LEFT = 'viewMainLeft',

VIEW_ROW_TOP = 'viewRowTop',
VIEW_ROW_BOTTOM = 'viewRowBottom',
VIEW_COLUMN_LEFT = 'viewColumnLeft',
VIEW_COLUMN_RIGHT = 'viewColumnRight',
VIEW_LEFT_TOP = 'viewLeftTop',
}

export interface IPaintForRefresh {
cacheCanvas: Canvas;
cacheCtx: UniverRenderingContext;
mainCtx: UniverRenderingContext;
topOrigin: number;
leftOrigin: number;
bufferEdgeX: number;
bufferEdgeY: number;
}
export interface IPaintForScrolling {
cacheCanvas: Canvas;
cacheCtx: UniverRenderingContext;
mainCtx: UniverRenderingContext;
topOrigin: number;
leftOrigin: number;
bufferEdgeX: number;
bufferEdgeY: number;
rowHeaderWidth: number;
columnHeaderHeight: number;
scaleX: number;
scaleY: number;
}
Loading