UNPKG

@visactor/vtable

Version:

canvas table width high performance

358 lines (340 loc) 24.3 kB
var __awaiter = this && this.__awaiter || function(thisArg, _arguments, P, generator) { return new (P || (P = Promise))((function(resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } function step(result) { var value; result.done ? resolve(result.value) : (value = result.value, value instanceof P ? value : new P((function(resolve) { resolve(value); }))).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); })); }; import { isNumber, isValid } from "@visactor/vutils"; import { Group } from "../../graphic/group"; import { computeColsWidth } from "../../layout/compute-col-width"; import { computeRowsHeight } from "../../layout/compute-row-height"; import { emptyGroup } from "../../utils/empty-group"; import { createColGroup } from "../column"; import { createComplexColumn } from "../column-helper"; import { createGroupForFirstScreen } from "./create-group-for-first-screen"; import { dynamicSetX, updateColContent } from "./update-position/dynamic-set-x"; import { dynamicSetY, updateRowContent } from "./update-position/dynamic-set-y"; import { updateAutoRow } from "./update-position/update-auto-row"; import { sortVertical } from "./update-position/sort-vertical"; import { sortHorizontal } from "./update-position/sort-horizontal"; import { updateAutoColumn } from "./update-position/update-auto-column"; import { handleTextStick } from "../../stick-text"; export class SceneProxy { constructor(table) { this.isRelease = !1, this.mode = "column", this.rowLimit = 200, this.currentRow = 0, this.rowStart = 0, this.rowEnd = 0, this.referenceRow = 0, this.screenTopRow = 0, this.deltaY = 0, this.deltaHeight = 0, this.colLimit = 100, this.screenLeftCol = 0, this.deltaX = 0, this.deltaWidth = 0, this.cellCache = new Map, this.table = table, this.table.isPivotChart() ? (this.rowLimit = Math.max(100, Math.ceil(2 * table.tableNoFrameHeight / table.defaultRowHeight)), this.colLimit = Math.max(100, Math.ceil(2 * table.tableNoFrameWidth / table.defaultColWidth))) : this.table.isAutoRowHeight(table.columnHeaderLevelCount) ? this.rowLimit = Math.max(100, Math.ceil(2 * table.tableNoFrameHeight / table.defaultRowHeight)) : ("autoWidth" === this.table.widthMode || (this.rowLimit = Math.max(200, Math.ceil(2 * table.tableNoFrameHeight / table.defaultRowHeight))), this.colLimit = Math.max(100, Math.ceil(2 * table.tableNoFrameWidth / table.defaultColWidth))), this.table.internalProps.transpose ? this.mode = "row" : this.table.isPivotTable() && (this.mode = "pivot"), this.table.options.maintainedDataCount && (this.rowLimit = this.table.options.maintainedDataCount), this.table.options.maintainedColumnCount && (this.colLimit = this.table.options.maintainedColumnCount), "adaptive" === this.table.heightMode && (this.rowLimit = this.table.rowCount), "adaptive" === this.table.widthMode && (this.colLimit = this.table.colCount); } get bodyLeftCol() { return this.table.frozenColCount; } get bodyTopRow() { return this.table.frozenRowCount; } setParamsForColumn() { this.bodyRightCol = this.table.colCount - 1 - this.table.rightFrozenColCount; const totalActualBodyColCount = Math.min(this.colLimit, this.bodyRightCol - this.bodyLeftCol + 1); this.totalActualBodyColCount = totalActualBodyColCount, this.totalCol = this.bodyLeftCol + totalActualBodyColCount - 1, this.colStart = this.bodyLeftCol, this.colEnd = this.totalCol; const defaultColWidth = this.table.defaultColWidth; this.taskColCount = 1 * Math.ceil(this.table.tableNoFrameWidth / defaultColWidth); const totalBodyWidth = defaultColWidth * totalActualBodyColCount, totalWidth = defaultColWidth * (this.bodyRightCol - this.bodyLeftCol + 1); this.xLimitLeft = totalBodyWidth / 2, this.xLimitRight = totalWidth - totalBodyWidth / 2; const widthLimit = 5 * this.table.tableNoFrameWidth; this.screenColCount = Math.ceil(this.table.tableNoFrameWidth / defaultColWidth), this.firstScreenColLimit = Math.max(15, this.bodyLeftCol + Math.min(this.colLimit, Math.ceil(widthLimit / defaultColWidth))), this.colUpdatePos = this.bodyRightCol; } setParamsForRow() { this.bodyBottomRow = this.table.rowCount - 1 - this.table.bottomFrozenRowCount; const totalActualBodyRowCount = Math.min(this.rowLimit, this.bodyBottomRow - this.bodyTopRow + 1); this.totalActualBodyRowCount = totalActualBodyRowCount, this.totalRow = this.bodyTopRow + totalActualBodyRowCount - 1, this.rowStart = this.bodyTopRow, this.rowEnd = this.totalRow; const defaultRowHeight = this.table.defaultRowHeight; this.taskRowCount = 1 * Math.ceil(this.table.tableNoFrameHeight / defaultRowHeight); const totalBodyHeight = defaultRowHeight * totalActualBodyRowCount, totalHeight = defaultRowHeight * (this.bodyBottomRow - this.bodyTopRow + 1); this.yLimitTop = totalBodyHeight / 2, this.yLimitBottom = totalHeight - totalBodyHeight / 2; const heightLimit = 5 * this.table.tableNoFrameHeight; this.screenRowCount = Math.ceil(this.table.tableNoFrameHeight / defaultRowHeight), this.firstScreenRowLimit = Math.max(30, this.bodyTopRow + Math.min(this.rowLimit, Math.ceil(heightLimit / defaultRowHeight))), this.rowUpdatePos = this.bodyBottomRow; } refreshRowCount() { this.bodyBottomRow = this.table.rowCount - 1 - this.table.bottomFrozenRowCount; const totalActualBodyRowCount = Math.min(this.rowLimit, this.bodyBottomRow - this.bodyTopRow + 1); this.totalActualBodyRowCount = totalActualBodyRowCount, this.totalRow = this.rowStart + totalActualBodyRowCount - 1; } refreshColCount() { this.bodyRightCol = this.table.colCount - 1 - this.table.rightFrozenColCount; const totalActualBodyColCount = Math.min(this.colLimit, this.bodyRightCol - this.bodyLeftCol + 1); this.totalActualBodyColCount = totalActualBodyColCount, this.totalCol = this.bodyLeftCol + totalActualBodyColCount - 1, this.colStart = this.bodyLeftCol, this.colEnd = this.totalCol; } resize() { const defaultColWidth = this.table.defaultColWidth; this.taskColCount = 1 * Math.ceil(this.table.tableNoFrameWidth / defaultColWidth); const widthLimit = 5 * this.table.tableNoFrameWidth; this.screenColCount = Math.ceil(this.table.tableNoFrameWidth / defaultColWidth), this.firstScreenColLimit = this.bodyLeftCol + Math.min(this.colLimit, Math.ceil(widthLimit / defaultColWidth)); const defaultRowHeight = this.table.defaultRowHeight; this.taskRowCount = 1 * Math.ceil(this.table.tableNoFrameHeight / defaultRowHeight); const heightLimit = 5 * this.table.tableNoFrameHeight; this.screenRowCount = Math.ceil(this.table.tableNoFrameHeight / defaultRowHeight), this.firstScreenRowLimit = this.bodyTopRow + Math.min(this.rowLimit, Math.ceil(heightLimit / defaultRowHeight)); } createGroupForFirstScreen(cornerHeaderGroup, colHeaderGroup, rowHeaderGroup, rightFrozenGroup, bottomFrozenGroup, bodyGroup, xOrigin, yOrigin) { createGroupForFirstScreen(cornerHeaderGroup, colHeaderGroup, rowHeaderGroup, rightFrozenGroup, bottomFrozenGroup, bodyGroup, xOrigin, yOrigin, this); } progress() { return __awaiter(this, void 0, void 0, (function*() { if (!this.isProgressing) return this.isProgressing = !0, new Promise(((resolve, reject) => { setTimeout((() => __awaiter(this, void 0, void 0, (function*() { this.isProgressing = !1, this.isRelease || (this.colUpdatePos <= this.colEnd ? (yield this.updateColCellGroupsAsync(), yield this.progress()) : this.rowUpdatePos <= this.rowEnd ? (yield this.updateRowCellGroupsAsync(), yield this.progress()) : this.currentCol < this.totalCol ? (yield this.createCol(), yield this.progress()) : this.currentRow < this.totalRow && (yield this.createRow(), yield this.progress()), handleTextStick(this.table), this.table.scenegraph.updateNextFrame(), resolve()); }))), 16); })); })); } createRow() { return __awaiter(this, void 0, void 0, (function*() { this.taskRowCount && this.createRowCellGroup(this.taskRowCount); })); } createCol() { return __awaiter(this, void 0, void 0, (function*() { this.taskColCount && this.createColGroup(this.taskRowCount); })); } createRowCellGroup(onceCount) { const endRow = Math.min(this.totalRow, this.currentRow + onceCount); if ("adaptive" !== this.table.heightMode && computeRowsHeight(this.table, this.currentRow + 1, endRow, !1), this.rowEnd = endRow, this.table.frozenColCount) { let maxHeight = 0; for (let col = 0; col < this.table.frozenColCount; col++) { const colGroup = this.table.scenegraph.getColGroup(col), {height: height} = (this.table.isListTable(), createComplexColumn(colGroup, col, colGroup.attribute.width, this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, this.table.defaultRowHeight, this.table)); maxHeight = Math.max(maxHeight, height), this.table.scenegraph.rowHeaderGroup.setAttribute("height", maxHeight); } } if (this.table.rightFrozenColCount) { let maxHeight = 0; for (let col = this.table.colCount - this.table.rightFrozenColCount; col < this.table.colCount; col++) { const colGroup = this.table.scenegraph.getColGroup(col), {height: height} = (this.table.isPivotChart(), createComplexColumn(colGroup, col, colGroup.attribute.width, this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, this.table.defaultRowHeight, this.table)); maxHeight = Math.max(maxHeight, height), this.table.scenegraph.rightFrozenGroup.setAttribute("height", maxHeight); } } let maxHeight = 0; for (let col = this.bodyLeftCol; col <= this.bodyRightCol; col++) { const colGroup = this.table.scenegraph.getColGroup(col); if (!colGroup) continue; this.table.rowHeaderLevelCount; const {height: height} = createComplexColumn(colGroup, col, colGroup.attribute.width, this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, this.table.defaultRowHeight, this.table); maxHeight = Math.max(maxHeight, height); } this.table.scenegraph.bodyGroup.setAttribute("height", maxHeight), this.currentRow = endRow, this.rowUpdatePos = this.rowEnd, this.table.scenegraph.updateContainer(), this.table.scenegraph.updateBorderSizeAndPosition(); } createColGroup(onceCount) { const endCol = Math.min(this.totalCol, this.currentCol + onceCount); computeColsWidth(this.table, this.currentCol + 1, endCol), this.colEnd = endCol; for (let row = 0; row < this.table.rowCount; row++) { const cellGroup = this.highPerformanceGetCell(this.currentCol, row); "cell" === cellGroup.role && isNumber(cellGroup.mergeStartCol) && cellGroup.mergeStartCol > this.currentCol && this.table.scenegraph.updateCellContent(cellGroup.col, cellGroup.row); } if (this.table.frozenRowCount) { const lastColumnGroup = this.table.scenegraph.colHeaderGroup.lastChild instanceof Group ? this.table.scenegraph.colHeaderGroup.lastChild : this.table.scenegraph.colHeaderGroup.lastChild._prev, xOrigin = lastColumnGroup.attribute.x + lastColumnGroup.attribute.width, yOrigin = lastColumnGroup.attribute.y; createColGroup(this.table.scenegraph.colHeaderGroup, xOrigin, yOrigin, this.currentCol + 1, endCol, 0, this.table.frozenRowCount - 1, "columnHeader", this.table); } if (this.table.bottomFrozenRowCount) { const lastColumnGroup = this.table.scenegraph.bottomFrozenGroup.lastChild instanceof Group ? this.table.scenegraph.bottomFrozenGroup.lastChild : this.table.scenegraph.bottomFrozenGroup.lastChild._prev, xOrigin = lastColumnGroup.attribute.x + lastColumnGroup.attribute.width, yOrigin = lastColumnGroup.attribute.y; createColGroup(this.table.scenegraph.bottomFrozenGroup, xOrigin, yOrigin, this.currentCol + 1, endCol, this.table.rowCount - this.table.bottomFrozenRowCount, this.table.rowCount - 1, "columnHeader", this.table); } let lastColumnGroup = this.table.scenegraph.bodyGroup.lastChild && (this.table.scenegraph.bodyGroup.lastChild instanceof Group ? this.table.scenegraph.bodyGroup.lastChild : this.table.scenegraph.bodyGroup.lastChild._prev); lastColumnGroup || (lastColumnGroup = this.table.scenegraph.colHeaderGroup.lastChild && (this.table.scenegraph.colHeaderGroup.lastChild instanceof Group ? this.table.scenegraph.colHeaderGroup.lastChild : this.table.scenegraph.colHeaderGroup.lastChild._prev)); const xOrigin = lastColumnGroup.attribute.x + lastColumnGroup.attribute.width, yOrigin = lastColumnGroup.attribute.y; createColGroup(this.table.scenegraph.bodyGroup, xOrigin, yOrigin, this.currentCol + 1, endCol, this.rowStart, this.rowEnd, "body", this.table), this.currentCol = endCol, this.colUpdatePos = this.colEnd, this.table.scenegraph.updateContainer(), this.table.scenegraph.updateBorderSizeAndPosition(); } setY(y, isEnd = !1) { return __awaiter(this, void 0, void 0, (function*() { const yLimitTop = this.table.getRowsHeight(this.bodyTopRow, this.bodyTopRow + (this.rowEnd - this.rowStart + 1)) / 2, yLimitBottom = this.table.getAllRowsHeight() - yLimitTop, screenTop = this.table.getTargetRowAt(y + this.table.scenegraph.colHeaderGroup.attribute.height); screenTop && (this.screenTopRow = screenTop.row), y < yLimitTop && this.rowStart === this.bodyTopRow || y > yLimitBottom && this.rowEnd === this.bodyBottomRow ? (this.updateDeltaY(y), this.updateBody(y - this.deltaY)) : this.table.scenegraph.bodyGroup.firstChild && "group" === this.table.scenegraph.bodyGroup.firstChild.type && 0 !== this.table.scenegraph.bodyGroup.firstChild.childrenCount || this.table.scenegraph.rowHeaderGroup.firstChild && "group" === this.table.scenegraph.rowHeaderGroup.firstChild.type && 0 !== this.table.scenegraph.rowHeaderGroup.firstChild.childrenCount ? this.dynamicSetY(y, screenTop, isEnd) : (this.updateDeltaY(y), this.updateBody(y - this.deltaY)); })); } setX(x, isEnd = !1) { return __awaiter(this, void 0, void 0, (function*() { const xLimitLeft = this.table.getColsWidth(this.bodyLeftCol, this.bodyLeftCol + (this.colEnd - this.colStart + 1)) / 2, xLimitRight = this.table.getAllColsWidth() - xLimitLeft, screenLeft = this.table.getTargetColAt(x + this.table.scenegraph.rowHeaderGroup.attribute.width); screenLeft && (this.screenLeftCol = screenLeft.col), x < xLimitLeft && this.colStart === this.bodyLeftCol || x > xLimitRight && this.colEnd === this.bodyRightCol || this.table.scenegraph.bodyGroup.firstChild && "group" === this.table.scenegraph.bodyGroup.firstChild.type && 0 === this.table.scenegraph.bodyGroup.firstChild.childrenCount ? (this.updateDeltaX(x), this.table.scenegraph.setBodyAndColHeaderX(-x + this.deltaX)) : this.dynamicSetX(x, screenLeft, isEnd); })); } dynamicSetY(y, screenTop, isEnd = !1) { return __awaiter(this, void 0, void 0, (function*() { dynamicSetY(y, screenTop, isEnd, this); })); } dynamicSetX(x, screenLeft, isEnd = !1) { return __awaiter(this, void 0, void 0, (function*() { dynamicSetX(x, screenLeft, isEnd, this); })); } updateBody(y) { this.table.scenegraph.setBodyAndRowHeaderY(-y); } updateRowCellGroupsAsync() { return __awaiter(this, void 0, void 0, (function*() { this.updateCellGroups(this.taskRowCount); })); } updateCellGroups(count) { const distRow = Math.min(this.bodyBottomRow, this.rowUpdatePos + count); this.table.isAutoRowHeight(this.rowUpdatePos) && computeRowsHeight(this.table, this.rowUpdatePos, distRow, !1), updateRowContent(this.rowUpdatePos, distRow, this), this.table.isAutoRowHeight(this.rowUpdatePos) && (updateAutoRow(this.bodyLeftCol, this.bodyRightCol, this.rowUpdatePos, distRow, this.table, this.rowUpdateDirection, !0), updateAutoRow(0, this.table.frozenColCount - 1, this.rowUpdatePos, distRow, this.table, this.rowUpdateDirection, !0), updateAutoRow(this.table.colCount - this.table.rightFrozenColCount, this.table.colCount - 1, this.rowUpdatePos, distRow, this.table, this.rowUpdateDirection, !0)), this.rowUpdatePos = distRow + 1; } updateBottomFrozenCellGroups() { const startRow = this.table.rowCount - this.table.bottomFrozenRowCount, endRow = this.table.rowCount - 1; this.table.isAutoRowHeight(startRow) && computeRowsHeight(this.table, startRow, endRow, !1), updateRowContent(startRow, endRow, this), this.table.isAutoRowHeight(startRow) && (updateAutoRow(this.bodyLeftCol, this.bodyRightCol, startRow, endRow, this.table, this.rowUpdateDirection), updateAutoRow(0, this.table.frozenColCount - 1, startRow, endRow, this.table, this.rowUpdateDirection), updateAutoRow(this.table.colCount - this.table.rightFrozenColCount, this.table.colCount - 1, startRow, endRow, this.table, this.rowUpdateDirection)); } updateRightFrozenCellGroups() { const startCol = this.table.colCount - this.table.rightFrozenColCount, endCol = this.table.colCount - 1; "autoWidth" === this.table.widthMode && computeColsWidth(this.table, startCol, endCol, !1), updateColContent(startCol, endCol, this), this.table.isAutoRowHeight(this.rowStart) && updateAutoColumn(startCol, endCol, this.table, this.colUpdateDirection); } updateColCellGroupsAsync() { return __awaiter(this, void 0, void 0, (function*() { this.updateColGroups(this.taskRowCount); })); } updateColGroups(count) { const distCol = Math.min(this.bodyRightCol, this.colUpdatePos + count); computeColsWidth(this.table, this.colUpdatePos, distCol), updateColContent(this.colUpdatePos, distCol, this), this.colUpdatePos = distCol + 1; } updateCellGroupPosition(cellGroup, newRow, y) { cellGroup.row = newRow, cellGroup.mergeStartCol = void 0, cellGroup.mergeStartRow = void 0, cellGroup.mergeEndCol = void 0, cellGroup.mergeEndRow = void 0, cellGroup.setAttribute("y", y), cellGroup.needUpdate = !0, cellGroup.needUpdateForAutoRowHeight = !0; } updateCellGroupContent(cellGroup) { if (!cellGroup.needUpdate || "cell" !== cellGroup.role) return cellGroup; const newCellGroup = this.table.scenegraph.updateCellContent(cellGroup.col, cellGroup.row); return cellGroup.needUpdate = !1, newCellGroup || cellGroup; } sortCellVertical() { return __awaiter(this, void 0, void 0, (function*() { yield sortVertical(this); })); } sortCellHorizontal() { return __awaiter(this, void 0, void 0, (function*() { yield sortHorizontal(this); })); } highPerformanceGetCell(col, row, getShadow) { if (row >= this.table.frozenRowCount && row < this.table.rowCount - this.table.bottomFrozenRowCount && (row < this.rowStart || row > this.rowEnd)) return emptyGroup; if (col >= this.table.frozenColCount && col < this.table.colCount - this.table.rightFrozenColCount && (col < this.colStart || col > this.colEnd)) return emptyGroup; if (this.cellCache.get(col)) { const cacheCellGoup = this.cellCache.get(col); if ((cacheCellGoup._next || cacheCellGoup._prev) && Math.abs(cacheCellGoup.row - row) < row) { let cellGroup = getCellByCache(cacheCellGoup, row); return cellGroup && (getShadow || "shadow-cell" !== cellGroup.role) || (cellGroup = this.table.scenegraph.getCell(col, row, getShadow)), cellGroup.row && this.cellCache.set(col, cellGroup), cellGroup; } const cellGroup = this.table.scenegraph.getCell(col, row, getShadow); return cellGroup.col === col && cellGroup.row && this.cellCache.set(col, cellGroup), cellGroup; } const cellGroup = this.table.scenegraph.getCell(col, row, getShadow); return cellGroup.col === col && cellGroup.row && this.cellCache.set(col, cellGroup), cellGroup; } updateDeltaY(y, screenTopY, screenTopRow) { if (this.rowStart === this.bodyTopRow) { const cellGroup = this.table.scenegraph.highPerformanceGetCell(this.colStart, this.rowStart, !0); if ("cell" === cellGroup.role) { const deltaY = cellGroup.attribute.y; this.deltaY = -deltaY; } } else if (this.rowEnd === this.bodyBottomRow) { const cellGroup = this.table.scenegraph.highPerformanceGetCell(this.colStart, this.rowEnd, !0); if ("cell" === cellGroup.role) { const deltaY = cellGroup.attribute.y + cellGroup.attribute.height - (this.table.getAllRowsHeight() - this.table.getFrozenRowsHeight() - this.table.getBottomFrozenRowsHeight()); this.deltaY = -deltaY; } } else if (isValid(screenTopY) && isValid(screenTopRow)) { let cellGroup = this.table.scenegraph.highPerformanceGetCell(this.colStart, screenTopRow, !0); "cell" !== cellGroup.role && (cellGroup = this.table.scenegraph.highPerformanceGetCell(0, screenTopRow, !0)); const bodyY = y - this.deltaY, distRowYOffset = screenTopY - bodyY, currentRowYOffset = cellGroup.attribute.y - bodyY + this.table.getFrozenRowsHeight(); this.deltaY = distRowYOffset - currentRowYOffset; } } updateDeltaX(x, screenLeftX, screenLeftCol) { if (this.colStart === this.bodyLeftCol) { const colGroup = this.table.scenegraph.getColGroup(this.colStart); if (colGroup) { const deltaX = colGroup.attribute.x; this.deltaX = -deltaX; } } else if (this.colEnd === this.bodyRightCol) { const colGroup = this.table.scenegraph.getColGroup(this.colEnd); if (colGroup) { const deltaX = colGroup.attribute.x + colGroup.attribute.width - (this.table.getAllColsWidth() - this.table.getFrozenColsWidth() - this.table.getRightFrozenColsWidth()); this.deltaX = -deltaX; } } else if (isValid(screenLeftX) && isValid(screenLeftCol)) { const colGroup = this.table.scenegraph.getColGroup(screenLeftCol) || this.table.scenegraph.getColGroup(screenLeftCol, !0), bodyX = x - this.deltaX, distColXOffset = screenLeftX - bodyX, currentColXOffset = colGroup.attribute.x - bodyX + this.table.getFrozenColsWidth(); this.deltaX = distColXOffset - currentColXOffset; } } release() { this.isRelease = !0; } } function getCellByCache(cacheCellGroup, row) { if (!cacheCellGroup) return null; if (cacheCellGroup.row === row) return cacheCellGroup; const prev = cacheCellGroup._prev, next = cacheCellGroup._next; return cacheCellGroup.row > row && prev && prev.row === cacheCellGroup.row - 1 ? getCellByCache(prev, row) : cacheCellGroup.row < row && next && next.row === cacheCellGroup.row + 1 ? getCellByCache(next, row) : null; } //# sourceMappingURL=proxy.js.map