mirror of
https://github.com/VisActor/VTable
synced 2024-11-21 17:40:10 +00:00
feat: optimize the performance of large data interaction (#3)
* optimize the performance of large data interaction * add pixel ratio and select api
This commit is contained in:
parent
9996b2b55c
commit
fc27cf1d61
2
.gitignore
vendored
2
.gitignore
vendored
@ -95,5 +95,5 @@ packages/vtable-docs/public/zh/documents
|
|||||||
packages/vtable-docs/public/en/documents
|
packages/vtable-docs/public/en/documents
|
||||||
packages/vtable-docs/public/*.html
|
packages/vtable-docs/public/*.html
|
||||||
|
|
||||||
#git-hook
|
# git-hook
|
||||||
common/scripts/pre-commit
|
common/scripts/pre-commit
|
@ -78,6 +78,7 @@ import {
|
|||||||
import { MenuHandler } from '../menu/dom/MenuHandler';
|
import { MenuHandler } from '../menu/dom/MenuHandler';
|
||||||
import type { BaseTableAPI, BaseTableConstructorOptions, IBaseTableProtected } from '../ts-types/base-table';
|
import type { BaseTableAPI, BaseTableConstructorOptions, IBaseTableProtected } from '../ts-types/base-table';
|
||||||
import { FocusInput } from './FouseInput';
|
import { FocusInput } from './FouseInput';
|
||||||
|
import { defaultPixelRatio } from '../tools/pixel-ratio';
|
||||||
const { toBoxArray } = utilStyle;
|
const { toBoxArray } = utilStyle;
|
||||||
const { isTouchEvent } = event;
|
const { isTouchEvent } = event;
|
||||||
const rangeReg = /^\$(\d+)\$(\d+)$/;
|
const rangeReg = /^\$(\d+)\$(\d+)$/;
|
||||||
@ -156,7 +157,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
|
|||||||
menu,
|
menu,
|
||||||
select: click,
|
select: click,
|
||||||
customRender,
|
customRender,
|
||||||
pixelRatio = 1
|
pixelRatio = defaultPixelRatio
|
||||||
} = options;
|
} = options;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this._widthMode = widthMode;
|
this._widthMode = widthMode;
|
||||||
@ -700,7 +701,8 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
|
|||||||
* @param pixelRatio
|
* @param pixelRatio
|
||||||
*/
|
*/
|
||||||
setPixelRatio(pixelRatio: number) {
|
setPixelRatio(pixelRatio: number) {
|
||||||
// do nothing
|
this.internalProps.pixelRatio = pixelRatio;
|
||||||
|
this.scenegraph.setPixelRatio(pixelRatio);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 窗口尺寸发生变化 或者像数比变化
|
* 窗口尺寸发生变化 或者像数比变化
|
||||||
@ -2030,7 +2032,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
|
|||||||
* 清除选中单元格
|
* 清除选中单元格
|
||||||
*/
|
*/
|
||||||
clearSelected() {
|
clearSelected() {
|
||||||
// do nothing
|
this.stateManeger.updateSelectPos(-1, -1);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 选中单元格 和鼠标选中单元格效果一致
|
* 选中单元格 和鼠标选中单元格效果一致
|
||||||
@ -2038,7 +2040,8 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
|
|||||||
* @param row
|
* @param row
|
||||||
*/
|
*/
|
||||||
selectCell(col: number, row: number) {
|
selectCell(col: number, row: number) {
|
||||||
// do nothing
|
this.stateManeger.updateSelectPos(col, row);
|
||||||
|
this.stateManeger.endSelectCells();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract isListTable(): boolean;
|
abstract isListTable(): boolean;
|
||||||
|
@ -3,6 +3,7 @@ import type { Group } from '../../graphic/group';
|
|||||||
import type { WrapText } from '../../graphic/text';
|
import type { WrapText } from '../../graphic/text';
|
||||||
import { updateCellHeightForColumn } from '../../layout/update-height';
|
import { updateCellHeightForColumn } from '../../layout/update-height';
|
||||||
import type { Scenegraph } from '../../scenegraph';
|
import type { Scenegraph } from '../../scenegraph';
|
||||||
|
import { emptyGroup } from '../../scenegraph';
|
||||||
import { getProp } from '../../utils/get-prop';
|
import { getProp } from '../../utils/get-prop';
|
||||||
import { getPadding } from '../../utils/padding';
|
import { getPadding } from '../../utils/padding';
|
||||||
import { createColGroup } from '../column';
|
import { createColGroup } from '../column';
|
||||||
@ -251,6 +252,8 @@ export class SceneProxy {
|
|||||||
// 不改变row,更新body group范围
|
// 不改变row,更新body group范围
|
||||||
this.updateBody(y);
|
this.updateBody(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.scenegraph.updateNextFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateBody(y: number) {
|
updateBody(y: number) {
|
||||||
@ -304,7 +307,7 @@ export class SceneProxy {
|
|||||||
for (let col = this.bodyLeftCol; col <= this.bodyRightCol; col++) {
|
for (let col = this.bodyLeftCol; col <= this.bodyRightCol; col++) {
|
||||||
for (let row = syncTopRow; row <= syncBottomRow; row++) {
|
for (let row = syncTopRow; row <= syncBottomRow; row++) {
|
||||||
// const cellGroup = this.table.scenegraph.getCell(col, row);
|
// const cellGroup = this.table.scenegraph.getCell(col, row);
|
||||||
const cellGroup = this.highPerformanceGetCell(col, row);
|
const cellGroup = this.highPerformanceGetCell(col, row, distStartRow, distEndRow);
|
||||||
this.updateCellGroupContent(cellGroup);
|
this.updateCellGroupContent(cellGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -367,7 +370,7 @@ export class SceneProxy {
|
|||||||
for (let col = this.bodyLeftCol; col <= this.bodyRightCol; col++) {
|
for (let col = this.bodyLeftCol; col <= this.bodyRightCol; col++) {
|
||||||
for (let row = syncTopRow; row <= syncBottomRow; row++) {
|
for (let row = syncTopRow; row <= syncBottomRow; row++) {
|
||||||
// const cellGroup = this.table.scenegraph.getCell(col, row);
|
// const cellGroup = this.table.scenegraph.getCell(col, row);
|
||||||
const cellGroup = this.highPerformanceGetCell(col, row);
|
const cellGroup = this.highPerformanceGetCell(col, row, distStartRow, distEndRow);
|
||||||
this.updateCellGroupContent(cellGroup);
|
this.updateCellGroupContent(cellGroup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -398,8 +401,6 @@ export class SceneProxy {
|
|||||||
(this.table as any).scenegraph.bodyGroup.firstChild.lastChild.row
|
(this.table as any).scenegraph.bodyGroup.firstChild.lastChild.row
|
||||||
);
|
);
|
||||||
|
|
||||||
this.scenegraph.renderSceneGraph();
|
|
||||||
|
|
||||||
if (!this.table.internalProps.autoRowHeight) {
|
if (!this.table.internalProps.autoRowHeight) {
|
||||||
await this.progress();
|
await this.progress();
|
||||||
}
|
}
|
||||||
@ -524,7 +525,10 @@ export class SceneProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
highPerformanceGetCell(col: number, row: number) {
|
highPerformanceGetCell(col: number, row: number, rowStart: number = this.rowStart, rowEnd: number = this.rowEnd) {
|
||||||
|
if (row < rowStart || row > rowEnd) {
|
||||||
|
return emptyGroup;
|
||||||
|
}
|
||||||
if (this.cellCache.get(col)) {
|
if (this.cellCache.get(col)) {
|
||||||
const cacheCellGoup = this.cellCache.get(col);
|
const cacheCellGoup = this.cellCache.get(col);
|
||||||
if ((cacheCellGoup._next || cacheCellGoup._prev) && Math.abs(cacheCellGoup.row - row) < row) {
|
if ((cacheCellGoup._next || cacheCellGoup._prev) && Math.abs(cacheCellGoup.row - row) < row) {
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
createRowHeaderColGroup
|
createRowHeaderColGroup
|
||||||
} from './group-creater/column';
|
} from './group-creater/column';
|
||||||
import type { WrapText } from './graphic/text';
|
import type { WrapText } from './graphic/text';
|
||||||
import { updateAutoColWidth } from './layout/auto-width';
|
|
||||||
import { updateAutoRowHeight } from './layout/auto-height';
|
import { updateAutoRowHeight } from './layout/auto-height';
|
||||||
import { getCellMergeInfo } from './utils/get-cell-merge';
|
import { getCellMergeInfo } from './utils/get-cell-merge';
|
||||||
import { updateColWidth } from './layout/update-width';
|
import { updateColWidth } from './layout/update-width';
|
||||||
@ -22,21 +21,23 @@ import { createFrameBorder } from './style/frame-border';
|
|||||||
import { ResizeColumnHotSpotSize } from '../tools/global';
|
import { ResizeColumnHotSpotSize } from '../tools/global';
|
||||||
import splitModule from './graphic/contributions';
|
import splitModule from './graphic/contributions';
|
||||||
import { getProp } from './utils/get-prop';
|
import { getProp } from './utils/get-prop';
|
||||||
import { createCellContent, dealWithIcon } from './utils/text-icon-layout';
|
import { dealWithIcon } from './utils/text-icon-layout';
|
||||||
import { SceneProxy } from './group-creater/progress/proxy';
|
import { SceneProxy } from './group-creater/progress/proxy';
|
||||||
import { SortOrder } from '../state/state';
|
import { SortOrder } from '../state/state';
|
||||||
import type { ListTable } from '../ListTable';
|
|
||||||
import type { TooltipOptions } from '../ts-types/tooltip';
|
import type { TooltipOptions } from '../ts-types/tooltip';
|
||||||
import { computeColWidth, computeColsWidth } from './layout/compute-col-width';
|
import { computeColWidth, computeColsWidth } from './layout/compute-col-width';
|
||||||
import { getStyleTheme } from './group-creater/column-helper';
|
|
||||||
import { moveHeaderPosition } from './layout/move-cell';
|
import { moveHeaderPosition } from './layout/move-cell';
|
||||||
import { updateCell } from './group-creater/cell-helper';
|
import { updateCell } from './group-creater/cell-helper';
|
||||||
import type { BaseTableAPI } from '../ts-types/base-table';
|
import type { BaseTableAPI } from '../ts-types/base-table';
|
||||||
|
import { updateAllSelectComponent, updateCellSelectBorder } from './select/update-select-border';
|
||||||
|
import { createCellSelectBorder } from './select/create-select-border';
|
||||||
|
import { moveSelectingRangeComponentsToSelectedRangeComponents } from './select/move-select-border';
|
||||||
|
import { deleteAllSelectBorder, deleteLastSelectedRangeComponents } from './select/delete-select-border';
|
||||||
|
|
||||||
container.load(splitModule);
|
container.load(splitModule);
|
||||||
|
|
||||||
const groupForDebug = new Group({});
|
export const emptyGroup = new Group({});
|
||||||
groupForDebug.role = 'empty';
|
emptyGroup.role = 'empty';
|
||||||
/**
|
/**
|
||||||
* @description: 表格场景树,存储和管理表格全部的场景图元
|
* @description: 表格场景树,存储和管理表格全部的场景图元
|
||||||
* @return {*}
|
* @return {*}
|
||||||
@ -74,7 +75,8 @@ export class Scenegraph {
|
|||||||
width: table.canvas.width,
|
width: table.canvas.width,
|
||||||
height: table.canvas.height,
|
height: table.canvas.height,
|
||||||
disableDirtyBounds: false,
|
disableDirtyBounds: false,
|
||||||
background: table.theme.underlayBackgroundColor
|
background: table.theme.underlayBackgroundColor,
|
||||||
|
dpr: table.internalProps.pixelRatio
|
||||||
});
|
});
|
||||||
|
|
||||||
this.stage.defaultLayer.setTheme({
|
this.stage.defaultLayer.setTheme({
|
||||||
@ -349,7 +351,14 @@ export class Scenegraph {
|
|||||||
cell = this.getCell(range.start.col, range.start.row);
|
cell = this.getCell(range.start.col, range.start.row);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cell || groupForDebug;
|
return cell || emptyGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
highPerformanceGetCell(col: number, row: number): Group {
|
||||||
|
if (!this.isPivot && !this.transpose && !this.table.isHeader(col, row)) {
|
||||||
|
return this.proxy.highPerformanceGetCell(col, row);
|
||||||
|
}
|
||||||
|
return this.getCell(col, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
getColGroup(col: number, isCornerOrColHeader = false): Group {
|
getColGroup(col: number, isCornerOrColHeader = false): Group {
|
||||||
@ -396,179 +405,7 @@ export class Scenegraph {
|
|||||||
this.stage.renderNextFrame();
|
this.stage.renderNextFrame();
|
||||||
}
|
}
|
||||||
resetAllSelectComponent() {
|
resetAllSelectComponent() {
|
||||||
this.selectingRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
updateAllSelectComponent(this);
|
||||||
const [startCol, startRow, endCol, endRow] = key.split('-');
|
|
||||||
let cellsBounds;
|
|
||||||
for (let i = parseInt(startCol, 10); i <= parseInt(endCol, 10); i++) {
|
|
||||||
for (let j = parseInt(startRow, 10); j <= parseInt(endRow, 10); j++) {
|
|
||||||
const cellGroup = this.getCell(i, j);
|
|
||||||
cellGroup.AABBBounds.width(); // hack: globalAABBBounds可能不会自动更新,这里强制更新一下
|
|
||||||
const bounds = cellGroup.globalAABBBounds;
|
|
||||||
if (!cellsBounds) {
|
|
||||||
cellsBounds = bounds;
|
|
||||||
} else {
|
|
||||||
cellsBounds.union(bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selectComp.rect.setAttributes({
|
|
||||||
x: cellsBounds.x1 - this.tableGroup.attribute.x,
|
|
||||||
y: cellsBounds.y1 - this.tableGroup.attribute.y,
|
|
||||||
width: cellsBounds.width(),
|
|
||||||
height: cellsBounds.height(),
|
|
||||||
visible: true
|
|
||||||
});
|
|
||||||
|
|
||||||
//#region 判断是不是按着表头部分的选中框 因为绘制层级的原因 线宽会被遮住一半,因此需要动态调整层级
|
|
||||||
const isNearRowHeader =
|
|
||||||
// this.table.scrollLeft === 0 &&
|
|
||||||
parseInt(startCol, 10) === this.table.frozenColCount;
|
|
||||||
const isNearColHeader =
|
|
||||||
// this.table.scrollTop === 0 &&
|
|
||||||
parseInt(startRow, 10) === this.table.frozenRowCount;
|
|
||||||
if (
|
|
||||||
(isNearRowHeader && selectComp.rect.attribute.stroke[3]) ||
|
|
||||||
(isNearColHeader && selectComp.rect.attribute.stroke[0])
|
|
||||||
) {
|
|
||||||
if (isNearRowHeader) {
|
|
||||||
this.tableGroup.insertAfter(
|
|
||||||
selectComp.rect,
|
|
||||||
selectComp.role === 'columnHeader' ? this.cornerHeaderGroup : this.rowHeaderGroup
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (isNearColHeader) {
|
|
||||||
this.tableGroup.insertAfter(
|
|
||||||
selectComp.rect,
|
|
||||||
selectComp.role === 'rowHeader' ? this.cornerHeaderGroup : this.colHeaderGroup
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//#region 调整层级后 滚动情况下会出现绘制范围出界 如body的选中框 渲染在了rowheader上面,所有需要调整选中框rect的 边界
|
|
||||||
if (
|
|
||||||
selectComp.rect.attribute.x < this.rowHeaderGroup.attribute.width &&
|
|
||||||
this.table.scrollLeft > 0 &&
|
|
||||||
(selectComp.role === 'body' || selectComp.role === 'columnHeader')
|
|
||||||
) {
|
|
||||||
selectComp.rect.setAttributes({
|
|
||||||
x: selectComp.rect.attribute.x + (this.rowHeaderGroup.attribute.width - selectComp.rect.attribute.x),
|
|
||||||
width: selectComp.rect.attribute.width - (this.rowHeaderGroup.attribute.width - selectComp.rect.attribute.x)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
selectComp.rect.attribute.y < this.colHeaderGroup.attribute.height &&
|
|
||||||
this.table.scrollTop > 0 &&
|
|
||||||
(selectComp.role === 'body' || selectComp.role === 'rowHeader')
|
|
||||||
) {
|
|
||||||
selectComp.rect.setAttributes({
|
|
||||||
y: selectComp.rect.attribute.y + (this.colHeaderGroup.attribute.height - selectComp.rect.attribute.y),
|
|
||||||
height:
|
|
||||||
selectComp.rect.attribute.height - (this.colHeaderGroup.attribute.height - selectComp.rect.attribute.y)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
} else {
|
|
||||||
this.tableGroup.insertAfter(
|
|
||||||
selectComp.rect,
|
|
||||||
selectComp.role === 'body'
|
|
||||||
? this.bodyGroup
|
|
||||||
: selectComp.role === 'columnHeader'
|
|
||||||
? this.colHeaderGroup
|
|
||||||
: selectComp.role === 'rowHeader'
|
|
||||||
? this.rowHeaderGroup
|
|
||||||
: this.cornerHeaderGroup
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
});
|
|
||||||
this.selectedRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
|
||||||
const [startCol, startRow, endCol, endRow] = key.split('-');
|
|
||||||
let cellsBounds;
|
|
||||||
for (let i = parseInt(startCol, 10); i <= parseInt(endCol, 10); i++) {
|
|
||||||
for (let j = parseInt(startRow, 10); j <= parseInt(endRow, 10); j++) {
|
|
||||||
const cellGroup = this.getCell(i, j);
|
|
||||||
cellGroup.AABBBounds.width(); // hack: globalAABBBounds可能不会自动更新,这里强制更新一下
|
|
||||||
const bounds = cellGroup.globalAABBBounds;
|
|
||||||
if (!cellsBounds) {
|
|
||||||
cellsBounds = bounds;
|
|
||||||
} else {
|
|
||||||
cellsBounds.union(bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selectComp.rect.setAttributes({
|
|
||||||
x: cellsBounds.x1 - this.tableGroup.attribute.x,
|
|
||||||
y: cellsBounds.y1 - this.tableGroup.attribute.y,
|
|
||||||
width: cellsBounds.width(),
|
|
||||||
height: cellsBounds.height(),
|
|
||||||
visible: true
|
|
||||||
});
|
|
||||||
|
|
||||||
//#region 判断是不是按着表头部分的选中框 因为绘制层级的原因 线宽会被遮住一半,因此需要动态调整层级
|
|
||||||
const isNearRowHeader =
|
|
||||||
// this.table.scrollLeft === 0 &&
|
|
||||||
parseInt(startCol, 10) === this.table.frozenColCount;
|
|
||||||
const isNearColHeader =
|
|
||||||
// this.table.scrollTop === 0 &&
|
|
||||||
parseInt(startRow, 10) === this.table.frozenRowCount;
|
|
||||||
if (
|
|
||||||
(isNearRowHeader && selectComp.rect.attribute.stroke[3]) ||
|
|
||||||
(isNearColHeader && selectComp.rect.attribute.stroke[0])
|
|
||||||
) {
|
|
||||||
if (isNearRowHeader) {
|
|
||||||
this.tableGroup.insertAfter(
|
|
||||||
selectComp.rect,
|
|
||||||
selectComp.role === 'columnHeader' ? this.cornerHeaderGroup : this.rowHeaderGroup
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (isNearColHeader) {
|
|
||||||
this.tableGroup.insertAfter(
|
|
||||||
selectComp.rect,
|
|
||||||
selectComp.role === 'rowHeader' ? this.cornerHeaderGroup : this.colHeaderGroup
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//#region 调整层级后 滚动情况下会出现绘制范围出界 如body的选中框 渲染在了rowheader上面,所有需要调整选中框rect的 边界
|
|
||||||
if (
|
|
||||||
selectComp.rect.attribute.x < this.rowHeaderGroup.attribute.width &&
|
|
||||||
this.table.scrollLeft > 0 &&
|
|
||||||
(selectComp.role === 'body' || selectComp.role === 'columnHeader')
|
|
||||||
) {
|
|
||||||
selectComp.rect.setAttributes({
|
|
||||||
x: selectComp.rect.attribute.x + (this.rowHeaderGroup.attribute.width - selectComp.rect.attribute.x),
|
|
||||||
width: selectComp.rect.attribute.width - (this.rowHeaderGroup.attribute.width - selectComp.rect.attribute.x)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
selectComp.rect.attribute.y < this.colHeaderGroup.attribute.height &&
|
|
||||||
this.table.scrollTop > 0 &&
|
|
||||||
(selectComp.role === 'body' || selectComp.role === 'rowHeader')
|
|
||||||
) {
|
|
||||||
selectComp.rect.setAttributes({
|
|
||||||
y: selectComp.rect.attribute.y + (this.colHeaderGroup.attribute.height - selectComp.rect.attribute.y),
|
|
||||||
height:
|
|
||||||
selectComp.rect.attribute.height - (this.colHeaderGroup.attribute.height - selectComp.rect.attribute.y)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
} else {
|
|
||||||
this.tableGroup.insertAfter(
|
|
||||||
selectComp.rect,
|
|
||||||
selectComp.role === 'body'
|
|
||||||
? this.bodyGroup
|
|
||||||
: selectComp.role === 'columnHeader'
|
|
||||||
? this.colHeaderGroup
|
|
||||||
: selectComp.role === 'rowHeader'
|
|
||||||
? this.rowHeaderGroup
|
|
||||||
: this.cornerHeaderGroup
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//#endregion
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeInteractionBorder(col: number, row: number) {
|
|
||||||
const cellGroup = this.getCell(col, row);
|
|
||||||
cellGroup.setAttribute('highlightStroke', undefined);
|
|
||||||
cellGroup.setAttribute('highlightStrokeArrayWidth', undefined);
|
|
||||||
cellGroup.setAttribute('highlightStrokeArrayColor', undefined);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hideHoverIcon(col: number, row: number) {
|
hideHoverIcon(col: number, row: number) {
|
||||||
@ -626,6 +463,13 @@ export class Scenegraph {
|
|||||||
(cellGroup?.firstChild as any)?.activate?.(this.table);
|
(cellGroup?.firstChild as any)?.activate?.(this.table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeInteractionBorder(col: number, row: number) {
|
||||||
|
const cellGroup = this.getCell(col, row);
|
||||||
|
cellGroup.setAttribute('highlightStroke', undefined);
|
||||||
|
cellGroup.setAttribute('highlightStrokeArrayWidth', undefined);
|
||||||
|
cellGroup.setAttribute('highlightStrokeArrayColor', undefined);
|
||||||
|
}
|
||||||
|
|
||||||
createCellSelectBorder(
|
createCellSelectBorder(
|
||||||
start_Col: number,
|
start_Col: number,
|
||||||
start_Row: number,
|
start_Row: number,
|
||||||
@ -635,238 +479,22 @@ export class Scenegraph {
|
|||||||
selectId: string, //整体区域${endRow}-${startCol}${startRow}${endCol}${endRow}作为其编号
|
selectId: string, //整体区域${endRow}-${startCol}${startRow}${endCol}${endRow}作为其编号
|
||||||
strokes?: boolean[]
|
strokes?: boolean[]
|
||||||
) {
|
) {
|
||||||
const startCol = Math.min(start_Col, end_Col);
|
createCellSelectBorder(this, start_Col, start_Row, end_Col, end_Row, selectRangeType, selectId, strokes);
|
||||||
const startRow = Math.min(start_Row, end_Row);
|
|
||||||
const endCol = Math.max(start_Col, end_Col);
|
|
||||||
const endRow = Math.max(start_Row, end_Row);
|
|
||||||
|
|
||||||
let cellsBounds;
|
|
||||||
for (let i = startCol; i <= endCol; i++) {
|
|
||||||
for (let j = startRow; j <= endRow; j++) {
|
|
||||||
const cellGroup = this.getCell(i, j);
|
|
||||||
if (cellGroup.role === 'shadow-cell') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const bounds = cellGroup.globalAABBBounds;
|
|
||||||
if (!cellsBounds) {
|
|
||||||
cellsBounds = bounds;
|
|
||||||
} else {
|
|
||||||
cellsBounds.union(bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const theme = this.table.theme;
|
|
||||||
// 框选外边框
|
|
||||||
const bodyClickBorderColor = theme.selectionStyle?.cellBorderColor;
|
|
||||||
const bodyClickLineWidth = theme.selectionStyle?.cellBorderLineWidth;
|
|
||||||
const rect = createRect({
|
|
||||||
pickable: false,
|
|
||||||
fill: true,
|
|
||||||
fillColor: (theme.selectionStyle?.cellBgColor as any) ?? 'rgba(0, 0, 255,0.1)',
|
|
||||||
strokeColor: bodyClickBorderColor as string,
|
|
||||||
lineWidth: bodyClickLineWidth as number,
|
|
||||||
stroke: strokes,
|
|
||||||
x: cellsBounds.x1 - this.tableGroup.attribute.x,
|
|
||||||
y: cellsBounds.y1 - this.tableGroup.attribute.y,
|
|
||||||
width: cellsBounds.width(),
|
|
||||||
height: cellsBounds.height(),
|
|
||||||
visible: true
|
|
||||||
});
|
|
||||||
this.lastSelectId = selectId;
|
|
||||||
this.selectingRangeComponents.set(`${startCol}-${startRow}-${endCol}-${endRow}-${selectId}`, {
|
|
||||||
rect,
|
|
||||||
role: selectRangeType
|
|
||||||
});
|
|
||||||
this.tableGroup.insertAfter(
|
|
||||||
rect,
|
|
||||||
selectRangeType === 'body'
|
|
||||||
? this.bodyGroup
|
|
||||||
: selectRangeType === 'columnHeader'
|
|
||||||
? this.colHeaderGroup
|
|
||||||
: selectRangeType === 'rowHeader'
|
|
||||||
? this.rowHeaderGroup
|
|
||||||
: this.cornerHeaderGroup
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
moveSelectingRangeComponentsToSelectedRangeComponents() {
|
moveSelectingRangeComponentsToSelectedRangeComponents() {
|
||||||
this.selectingRangeComponents.forEach((rangeComponent, key) => {
|
moveSelectingRangeComponentsToSelectedRangeComponents(this);
|
||||||
if (this.selectedRangeComponents.get(key)) {
|
|
||||||
this.selectedRangeComponents.get(key).rect.delete();
|
|
||||||
}
|
|
||||||
this.selectedRangeComponents.set(key, rangeComponent);
|
|
||||||
});
|
|
||||||
this.selectingRangeComponents = new Map();
|
|
||||||
this.updateNextFrame();
|
|
||||||
}
|
}
|
||||||
/** 按住shift 则继续上次选中范围 需要将现有的删除掉 */
|
/** 按住shift 则继续上次选中范围 需要将现有的删除掉 */
|
||||||
deleteLastSelectedRangeComponents() {
|
deleteLastSelectedRangeComponents() {
|
||||||
this.selectedRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
deleteLastSelectedRangeComponents(this);
|
||||||
const lastSelectId = key.split('-')[4];
|
|
||||||
if (lastSelectId === this.lastSelectId) {
|
|
||||||
selectComp.rect.delete();
|
|
||||||
this.selectedRangeComponents.delete(key);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
deleteAllSelectBorder() {
|
deleteAllSelectBorder() {
|
||||||
this.selectedRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
deleteAllSelectBorder(this);
|
||||||
selectComp.rect.delete();
|
|
||||||
});
|
|
||||||
this.selectedRangeComponents = new Map();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCellSelectBorder(newStartCol: number, newStartRow: number, newEndCol: number, newEndRow: number) {
|
updateCellSelectBorder(newStartCol: number, newStartRow: number, newEndCol: number, newEndRow: number) {
|
||||||
let startCol = Math.min(newEndCol, newStartCol);
|
updateCellSelectBorder(this, newStartCol, newStartRow, newEndCol, newEndRow);
|
||||||
let startRow = Math.min(newEndRow, newStartRow);
|
|
||||||
let endCol = Math.max(newEndCol, newStartCol);
|
|
||||||
let endRow = Math.max(newEndRow, newStartRow);
|
|
||||||
//#region region 校验四周的单元格有没有合并的情况,如有则扩大范围
|
|
||||||
const extendSelectRange = () => {
|
|
||||||
let isExtend = false;
|
|
||||||
for (let col = startCol; col <= endCol; col++) {
|
|
||||||
if (col === startCol) {
|
|
||||||
for (let row = startRow; row <= endRow; row++) {
|
|
||||||
const mergeInfo = getCellMergeInfo(this.table, col, row);
|
|
||||||
if (mergeInfo && mergeInfo.start.col < startCol) {
|
|
||||||
startCol = mergeInfo.start.col;
|
|
||||||
isExtend = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isExtend && col === endCol) {
|
|
||||||
for (let row = startRow; row <= endRow; row++) {
|
|
||||||
const mergeInfo = getCellMergeInfo(this.table, col, row);
|
|
||||||
if (mergeInfo && mergeInfo.end.col > endCol) {
|
|
||||||
endCol = mergeInfo.end.col;
|
|
||||||
isExtend = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isExtend) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isExtend) {
|
|
||||||
for (let row = startRow; row <= endRow; row++) {
|
|
||||||
if (row === startRow) {
|
|
||||||
for (let col = startCol; col <= endCol; col++) {
|
|
||||||
const mergeInfo = getCellMergeInfo(this.table, col, row);
|
|
||||||
if (mergeInfo && mergeInfo.start.row < startRow) {
|
|
||||||
startRow = mergeInfo.start.row;
|
|
||||||
isExtend = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isExtend && row === endRow) {
|
|
||||||
for (let col = startCol; col <= endCol; col++) {
|
|
||||||
const mergeInfo = getCellMergeInfo(this.table, col, row);
|
|
||||||
if (mergeInfo && mergeInfo.end.row > endRow) {
|
|
||||||
endRow = mergeInfo.end.row;
|
|
||||||
isExtend = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isExtend) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isExtend) {
|
|
||||||
extendSelectRange();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
extendSelectRange();
|
|
||||||
//#endregion
|
|
||||||
this.selectingRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
|
||||||
selectComp.rect.delete();
|
|
||||||
});
|
|
||||||
this.selectingRangeComponents = new Map();
|
|
||||||
|
|
||||||
let needRowHeader = false;
|
|
||||||
let needColumnHeader = false;
|
|
||||||
let needBody = false;
|
|
||||||
let needCornerHeader = false;
|
|
||||||
if (startCol <= this.table.frozenColCount - 1 && startRow <= this.table.frozenRowCount - 1) {
|
|
||||||
needCornerHeader = true;
|
|
||||||
}
|
|
||||||
if (startCol <= this.table.frozenColCount - 1 && endRow >= this.table.frozenRowCount) {
|
|
||||||
needRowHeader = true;
|
|
||||||
}
|
|
||||||
if (startRow <= this.table.frozenRowCount - 1 && endCol >= this.table.frozenColCount) {
|
|
||||||
needColumnHeader = true;
|
|
||||||
}
|
|
||||||
if (endCol >= this.table.frozenColCount && endRow >= this.table.frozenRowCount) {
|
|
||||||
needBody = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO 可以尝试不拆分三个表头和body【前提是theme中合并配置】 用一个SelectBorder 需要结合clip,并动态设置border的范围【依据区域范围 已经是否跨表头及body】
|
|
||||||
if (needCornerHeader) {
|
|
||||||
const cornerEndCol = Math.min(endCol, this.table.frozenColCount - 1);
|
|
||||||
const cornerEndRow = Math.min(endRow, this.table.frozenRowCount - 1);
|
|
||||||
const strokeArray = [true, !needColumnHeader, !needRowHeader, true];
|
|
||||||
this.createCellSelectBorder(
|
|
||||||
startCol,
|
|
||||||
startRow,
|
|
||||||
cornerEndCol,
|
|
||||||
cornerEndRow,
|
|
||||||
'cornerHeader',
|
|
||||||
`${startCol}${startRow}${endCol}${endRow}`,
|
|
||||||
strokeArray
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (needColumnHeader) {
|
|
||||||
const columnHeaderStartCol = Math.max(startCol, this.table.frozenColCount);
|
|
||||||
const columnHeaderEndRow = Math.min(endRow, this.table.frozenRowCount - 1);
|
|
||||||
const strokeArray = [true, true, !needBody, !needCornerHeader];
|
|
||||||
this.createCellSelectBorder(
|
|
||||||
columnHeaderStartCol,
|
|
||||||
startRow,
|
|
||||||
endCol,
|
|
||||||
columnHeaderEndRow,
|
|
||||||
'columnHeader',
|
|
||||||
`${startCol}${startRow}${endCol}${endRow}`,
|
|
||||||
strokeArray
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (needRowHeader) {
|
|
||||||
const columnHeaderStartRow = Math.max(startRow, this.table.frozenRowCount);
|
|
||||||
const columnHeaderEndCol = Math.min(endCol, this.table.frozenColCount - 1);
|
|
||||||
const strokeArray = [!needCornerHeader, !needBody, true, true];
|
|
||||||
this.createCellSelectBorder(
|
|
||||||
startCol,
|
|
||||||
columnHeaderStartRow,
|
|
||||||
columnHeaderEndCol,
|
|
||||||
endRow,
|
|
||||||
'rowHeader',
|
|
||||||
`${startCol}${startRow}${endCol}${endRow}`,
|
|
||||||
strokeArray
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (needBody) {
|
|
||||||
const columnHeaderStartCol = Math.max(startCol, this.table.frozenColCount);
|
|
||||||
const columnHeaderStartRow = Math.max(startRow, this.table.frozenRowCount);
|
|
||||||
const strokeArray = [!needColumnHeader, true, true, !needRowHeader];
|
|
||||||
this.createCellSelectBorder(
|
|
||||||
columnHeaderStartCol,
|
|
||||||
columnHeaderStartRow,
|
|
||||||
endCol,
|
|
||||||
endRow,
|
|
||||||
'body',
|
|
||||||
`${startCol}${startRow}${endCol}${endRow}`,
|
|
||||||
strokeArray
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// hideCellsSelectBorder() {
|
|
||||||
// this.component.selectBorder.setAttribute('visible', false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description: 获取指定单元格指定位置的icon mark
|
* @description: 获取指定单元格指定位置的icon mark
|
||||||
@ -1151,6 +779,8 @@ export class Scenegraph {
|
|||||||
|
|
||||||
// 更新滚动条状态
|
// 更新滚动条状态
|
||||||
this.component.updateScrollBar();
|
this.component.updateScrollBar();
|
||||||
|
|
||||||
|
this.updateNextFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1769,6 +1399,16 @@ export class Scenegraph {
|
|||||||
}
|
}
|
||||||
updateCell(col, row, this.table);
|
updateCell(col, row, this.table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPixelRatio(pixelRatio: number) {
|
||||||
|
// this.stage.setDpr(pixelRatio);
|
||||||
|
// 这里因为本时刻部分节点有更新bounds标记,直接render回导致开启DirtyBounds,无法完整重绘画布;
|
||||||
|
// 所以这里先关闭DirtyBounds,等待下一帧再开启
|
||||||
|
this.stage.disableDirtyBounds();
|
||||||
|
this.stage.window.setDpr(pixelRatio);
|
||||||
|
this.stage.render();
|
||||||
|
this.stage.enableDirtyBounds();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showIcon(scene: Scenegraph, cellGroup: Group, visibleTime: 'mouseenter_cell' | 'click_cell') {
|
function showIcon(scene: Scenegraph, cellGroup: Group, visibleTime: 'mouseenter_cell' | 'click_cell') {
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
import { createRect } from '@visactor/vrender';
|
||||||
|
import type { CellType } from '../../ts-types';
|
||||||
|
import type { Scenegraph } from '../scenegraph';
|
||||||
|
|
||||||
|
export function createCellSelectBorder(
|
||||||
|
scene: Scenegraph,
|
||||||
|
start_Col: number,
|
||||||
|
start_Row: number,
|
||||||
|
end_Col: number,
|
||||||
|
end_Row: number,
|
||||||
|
selectRangeType: CellType,
|
||||||
|
selectId: string, //整体区域${endRow}-${startCol}${startRow}${endCol}${endRow}作为其编号
|
||||||
|
strokes?: boolean[]
|
||||||
|
) {
|
||||||
|
const startCol = Math.min(start_Col, end_Col);
|
||||||
|
const startRow = Math.min(start_Row, end_Row);
|
||||||
|
const endCol = Math.max(start_Col, end_Col);
|
||||||
|
const endRow = Math.max(start_Row, end_Row);
|
||||||
|
|
||||||
|
let cellsBounds;
|
||||||
|
for (let i = startCol; i <= endCol; i++) {
|
||||||
|
for (let j = startRow; j <= endRow; j++) {
|
||||||
|
const cellGroup = scene.highPerformanceGetCell(i, j);
|
||||||
|
if (cellGroup.role === 'shadow-cell') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const bounds = cellGroup.globalAABBBounds;
|
||||||
|
if (!cellsBounds) {
|
||||||
|
cellsBounds = bounds;
|
||||||
|
} else {
|
||||||
|
cellsBounds.union(bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const theme = scene.table.theme;
|
||||||
|
// 框选外边框
|
||||||
|
const bodyClickBorderColor = theme.selectionStyle?.cellBorderColor;
|
||||||
|
const bodyClickLineWidth = theme.selectionStyle?.cellBorderLineWidth;
|
||||||
|
const rect = createRect({
|
||||||
|
pickable: false,
|
||||||
|
fill: true,
|
||||||
|
fillColor: (theme.selectionStyle?.cellBgColor as any) ?? 'rgba(0, 0, 255,0.1)',
|
||||||
|
strokeColor: bodyClickBorderColor as string,
|
||||||
|
lineWidth: bodyClickLineWidth as number,
|
||||||
|
stroke: strokes,
|
||||||
|
x: cellsBounds.x1 - scene.tableGroup.attribute.x,
|
||||||
|
y: cellsBounds.y1 - scene.tableGroup.attribute.y,
|
||||||
|
width: cellsBounds.width(),
|
||||||
|
height: cellsBounds.height(),
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
scene.lastSelectId = selectId;
|
||||||
|
scene.selectingRangeComponents.set(`${startCol}-${startRow}-${endCol}-${endRow}-${selectId}`, {
|
||||||
|
rect,
|
||||||
|
role: selectRangeType
|
||||||
|
});
|
||||||
|
scene.tableGroup.insertAfter(
|
||||||
|
rect,
|
||||||
|
selectRangeType === 'body'
|
||||||
|
? scene.bodyGroup
|
||||||
|
: selectRangeType === 'columnHeader'
|
||||||
|
? scene.colHeaderGroup
|
||||||
|
: selectRangeType === 'rowHeader'
|
||||||
|
? scene.rowHeaderGroup
|
||||||
|
: scene.cornerHeaderGroup
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import type { IRect } from '@visactor/vrender';
|
||||||
|
import type { Scenegraph } from '../scenegraph';
|
||||||
|
import type { CellType } from '../../ts-types';
|
||||||
|
|
||||||
|
/** 按住shift 则继续上次选中范围 需要将现有的删除掉 */
|
||||||
|
export function deleteLastSelectedRangeComponents(scene: Scenegraph) {
|
||||||
|
scene.selectedRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
||||||
|
const lastSelectId = key.split('-')[4];
|
||||||
|
if (lastSelectId === scene.lastSelectId) {
|
||||||
|
selectComp.rect.delete();
|
||||||
|
scene.selectedRangeComponents.delete(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteAllSelectBorder(scene: Scenegraph) {
|
||||||
|
scene.selectedRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
||||||
|
selectComp.rect.delete();
|
||||||
|
});
|
||||||
|
scene.selectedRangeComponents = new Map();
|
||||||
|
}
|
12
packages/vtable/src/scenegraph/select/move-select-border.ts
Normal file
12
packages/vtable/src/scenegraph/select/move-select-border.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import type { Scenegraph } from '../scenegraph';
|
||||||
|
|
||||||
|
export function moveSelectingRangeComponentsToSelectedRangeComponents(scene: Scenegraph) {
|
||||||
|
scene.selectingRangeComponents.forEach((rangeComponent, key) => {
|
||||||
|
if (scene.selectedRangeComponents.get(key)) {
|
||||||
|
scene.selectedRangeComponents.get(key).rect.delete();
|
||||||
|
}
|
||||||
|
scene.selectedRangeComponents.set(key, rangeComponent);
|
||||||
|
});
|
||||||
|
scene.selectingRangeComponents = new Map();
|
||||||
|
scene.updateNextFrame();
|
||||||
|
}
|
265
packages/vtable/src/scenegraph/select/update-select-border.ts
Normal file
265
packages/vtable/src/scenegraph/select/update-select-border.ts
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
import type { IRect } from '@visactor/vrender';
|
||||||
|
import type { Scenegraph } from '../scenegraph';
|
||||||
|
import type { CellType } from '../../ts-types';
|
||||||
|
import { getCellMergeInfo } from '../utils/get-cell-merge';
|
||||||
|
|
||||||
|
export function updateAllSelectComponent(scene: Scenegraph) {
|
||||||
|
scene.selectingRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
||||||
|
updateComponent(selectComp, key, scene);
|
||||||
|
});
|
||||||
|
scene.selectedRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
||||||
|
updateComponent(selectComp, key, scene);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateComponent(selectComp: { rect: IRect; role: CellType }, key: string, scene: Scenegraph) {
|
||||||
|
const [startColStr, startRowStr, endColStr, endRowStr] = key.split('-');
|
||||||
|
const startCol = parseInt(startColStr, 10);
|
||||||
|
const startRow = parseInt(startRowStr, 10);
|
||||||
|
const endCol = parseInt(endColStr, 10);
|
||||||
|
const endRow = parseInt(endRowStr, 10);
|
||||||
|
let cellsBounds;
|
||||||
|
for (let i = startCol; i <= endCol; i++) {
|
||||||
|
for (let j = startRow; j <= endRow; j++) {
|
||||||
|
const cellGroup = scene.highPerformanceGetCell(i, j);
|
||||||
|
if (cellGroup.role !== 'cell') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cellGroup.AABBBounds.width(); // hack: globalAABBBounds可能不会自动更新,这里强制更新一下
|
||||||
|
const bounds = cellGroup.globalAABBBounds;
|
||||||
|
if (!cellsBounds) {
|
||||||
|
cellsBounds = bounds;
|
||||||
|
} else {
|
||||||
|
cellsBounds.union(bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cellsBounds) {
|
||||||
|
// 选中区域在实际单元格区域外,不显示选择框
|
||||||
|
selectComp.rect.setAttributes({
|
||||||
|
visible: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
selectComp.rect.setAttributes({
|
||||||
|
x: cellsBounds.x1 - scene.tableGroup.attribute.x,
|
||||||
|
y: cellsBounds.y1 - scene.tableGroup.attribute.y,
|
||||||
|
width: cellsBounds.width(),
|
||||||
|
height: cellsBounds.height(),
|
||||||
|
visible: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//#region 判断是不是按着表头部分的选中框 因为绘制层级的原因 线宽会被遮住一半,因此需要动态调整层级
|
||||||
|
const isNearRowHeader =
|
||||||
|
// scene.table.scrollLeft === 0 &&
|
||||||
|
startCol === scene.table.frozenColCount;
|
||||||
|
const isNearColHeader =
|
||||||
|
// scene.table.scrollTop === 0 &&
|
||||||
|
startRow === scene.table.frozenRowCount;
|
||||||
|
if (
|
||||||
|
(isNearRowHeader && selectComp.rect.attribute.stroke[3]) ||
|
||||||
|
(isNearColHeader && selectComp.rect.attribute.stroke[0])
|
||||||
|
) {
|
||||||
|
if (isNearRowHeader) {
|
||||||
|
scene.tableGroup.insertAfter(
|
||||||
|
selectComp.rect,
|
||||||
|
selectComp.role === 'columnHeader' ? scene.cornerHeaderGroup : scene.rowHeaderGroup
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (isNearColHeader) {
|
||||||
|
scene.tableGroup.insertAfter(
|
||||||
|
selectComp.rect,
|
||||||
|
selectComp.role === 'rowHeader' ? scene.cornerHeaderGroup : scene.colHeaderGroup
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//#region 调整层级后 滚动情况下会出现绘制范围出界 如body的选中框 渲染在了rowheader上面,所有需要调整选中框rect的 边界
|
||||||
|
if (
|
||||||
|
selectComp.rect.attribute.x < scene.rowHeaderGroup.attribute.width &&
|
||||||
|
scene.table.scrollLeft > 0 &&
|
||||||
|
(selectComp.role === 'body' || selectComp.role === 'columnHeader')
|
||||||
|
) {
|
||||||
|
selectComp.rect.setAttributes({
|
||||||
|
x: selectComp.rect.attribute.x + (scene.rowHeaderGroup.attribute.width - selectComp.rect.attribute.x),
|
||||||
|
width: selectComp.rect.attribute.width - (scene.rowHeaderGroup.attribute.width - selectComp.rect.attribute.x)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
selectComp.rect.attribute.y < scene.colHeaderGroup.attribute.height &&
|
||||||
|
scene.table.scrollTop > 0 &&
|
||||||
|
(selectComp.role === 'body' || selectComp.role === 'rowHeader')
|
||||||
|
) {
|
||||||
|
selectComp.rect.setAttributes({
|
||||||
|
y: selectComp.rect.attribute.y + (scene.colHeaderGroup.attribute.height - selectComp.rect.attribute.y),
|
||||||
|
height: selectComp.rect.attribute.height - (scene.colHeaderGroup.attribute.height - selectComp.rect.attribute.y)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
} else {
|
||||||
|
scene.tableGroup.insertAfter(
|
||||||
|
selectComp.rect,
|
||||||
|
selectComp.role === 'body'
|
||||||
|
? scene.bodyGroup
|
||||||
|
: selectComp.role === 'columnHeader'
|
||||||
|
? scene.colHeaderGroup
|
||||||
|
: selectComp.role === 'rowHeader'
|
||||||
|
? scene.rowHeaderGroup
|
||||||
|
: scene.cornerHeaderGroup
|
||||||
|
);
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCellSelectBorder(
|
||||||
|
scene: Scenegraph,
|
||||||
|
newStartCol: number,
|
||||||
|
newStartRow: number,
|
||||||
|
newEndCol: number,
|
||||||
|
newEndRow: number
|
||||||
|
) {
|
||||||
|
let startCol = Math.min(newEndCol, newStartCol);
|
||||||
|
let startRow = Math.min(newEndRow, newStartRow);
|
||||||
|
let endCol = Math.max(newEndCol, newStartCol);
|
||||||
|
let endRow = Math.max(newEndRow, newStartRow);
|
||||||
|
//#region region 校验四周的单元格有没有合并的情况,如有则扩大范围
|
||||||
|
const extendSelectRange = () => {
|
||||||
|
let isExtend = false;
|
||||||
|
for (let col = startCol; col <= endCol; col++) {
|
||||||
|
if (col === startCol) {
|
||||||
|
for (let row = startRow; row <= endRow; row++) {
|
||||||
|
const mergeInfo = getCellMergeInfo(scene.table, col, row);
|
||||||
|
if (mergeInfo && mergeInfo.start.col < startCol) {
|
||||||
|
startCol = mergeInfo.start.col;
|
||||||
|
isExtend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isExtend && col === endCol) {
|
||||||
|
for (let row = startRow; row <= endRow; row++) {
|
||||||
|
const mergeInfo = getCellMergeInfo(scene.table, col, row);
|
||||||
|
if (mergeInfo && mergeInfo.end.col > endCol) {
|
||||||
|
endCol = mergeInfo.end.col;
|
||||||
|
isExtend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isExtend) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isExtend) {
|
||||||
|
for (let row = startRow; row <= endRow; row++) {
|
||||||
|
if (row === startRow) {
|
||||||
|
for (let col = startCol; col <= endCol; col++) {
|
||||||
|
const mergeInfo = getCellMergeInfo(scene.table, col, row);
|
||||||
|
if (mergeInfo && mergeInfo.start.row < startRow) {
|
||||||
|
startRow = mergeInfo.start.row;
|
||||||
|
isExtend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isExtend && row === endRow) {
|
||||||
|
for (let col = startCol; col <= endCol; col++) {
|
||||||
|
const mergeInfo = getCellMergeInfo(scene.table, col, row);
|
||||||
|
if (mergeInfo && mergeInfo.end.row > endRow) {
|
||||||
|
endRow = mergeInfo.end.row;
|
||||||
|
isExtend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isExtend) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isExtend) {
|
||||||
|
extendSelectRange();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
extendSelectRange();
|
||||||
|
//#endregion
|
||||||
|
scene.selectingRangeComponents.forEach((selectComp: { rect: IRect; role: CellType }, key: string) => {
|
||||||
|
selectComp.rect.delete();
|
||||||
|
});
|
||||||
|
scene.selectingRangeComponents = new Map();
|
||||||
|
|
||||||
|
let needRowHeader = false;
|
||||||
|
let needColumnHeader = false;
|
||||||
|
let needBody = false;
|
||||||
|
let needCornerHeader = false;
|
||||||
|
if (startCol <= scene.table.frozenColCount - 1 && startRow <= scene.table.frozenRowCount - 1) {
|
||||||
|
needCornerHeader = true;
|
||||||
|
}
|
||||||
|
if (startCol <= scene.table.frozenColCount - 1 && endRow >= scene.table.frozenRowCount) {
|
||||||
|
needRowHeader = true;
|
||||||
|
}
|
||||||
|
if (startRow <= scene.table.frozenRowCount - 1 && endCol >= scene.table.frozenColCount) {
|
||||||
|
needColumnHeader = true;
|
||||||
|
}
|
||||||
|
if (endCol >= scene.table.frozenColCount && endRow >= scene.table.frozenRowCount) {
|
||||||
|
needBody = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO 可以尝试不拆分三个表头和body【前提是theme中合并配置】 用一个SelectBorder 需要结合clip,并动态设置border的范围【依据区域范围 已经是否跨表头及body】
|
||||||
|
if (needCornerHeader) {
|
||||||
|
const cornerEndCol = Math.min(endCol, scene.table.frozenColCount - 1);
|
||||||
|
const cornerEndRow = Math.min(endRow, scene.table.frozenRowCount - 1);
|
||||||
|
const strokeArray = [true, !needColumnHeader, !needRowHeader, true];
|
||||||
|
scene.createCellSelectBorder(
|
||||||
|
startCol,
|
||||||
|
startRow,
|
||||||
|
cornerEndCol,
|
||||||
|
cornerEndRow,
|
||||||
|
'cornerHeader',
|
||||||
|
`${startCol}${startRow}${endCol}${endRow}`,
|
||||||
|
strokeArray
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (needColumnHeader) {
|
||||||
|
const columnHeaderStartCol = Math.max(startCol, scene.table.frozenColCount);
|
||||||
|
const columnHeaderEndRow = Math.min(endRow, scene.table.frozenRowCount - 1);
|
||||||
|
const strokeArray = [true, true, !needBody, !needCornerHeader];
|
||||||
|
scene.createCellSelectBorder(
|
||||||
|
columnHeaderStartCol,
|
||||||
|
startRow,
|
||||||
|
endCol,
|
||||||
|
columnHeaderEndRow,
|
||||||
|
'columnHeader',
|
||||||
|
`${startCol}${startRow}${endCol}${endRow}`,
|
||||||
|
strokeArray
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (needRowHeader) {
|
||||||
|
const columnHeaderStartRow = Math.max(startRow, scene.table.frozenRowCount);
|
||||||
|
const columnHeaderEndCol = Math.min(endCol, scene.table.frozenColCount - 1);
|
||||||
|
const strokeArray = [!needCornerHeader, !needBody, true, true];
|
||||||
|
scene.createCellSelectBorder(
|
||||||
|
startCol,
|
||||||
|
columnHeaderStartRow,
|
||||||
|
columnHeaderEndCol,
|
||||||
|
endRow,
|
||||||
|
'rowHeader',
|
||||||
|
`${startCol}${startRow}${endCol}${endRow}`,
|
||||||
|
strokeArray
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (needBody) {
|
||||||
|
const columnHeaderStartCol = Math.max(startCol, scene.table.frozenColCount);
|
||||||
|
const columnHeaderStartRow = Math.max(startRow, scene.table.frozenRowCount);
|
||||||
|
const strokeArray = [!needColumnHeader, true, true, !needRowHeader];
|
||||||
|
scene.createCellSelectBorder(
|
||||||
|
columnHeaderStartCol,
|
||||||
|
columnHeaderStartRow,
|
||||||
|
endCol,
|
||||||
|
endRow,
|
||||||
|
'body',
|
||||||
|
`${startCol}${startRow}${endCol}${endRow}`,
|
||||||
|
strokeArray
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import type { CellRange } from '../../ts-types';
|
import type { CellRange, TextColumnDefine } from '../../ts-types';
|
||||||
import type { BaseTableAPI } from '../../ts-types/base-table';
|
import type { BaseTableAPI } from '../../ts-types/base-table';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -9,6 +9,10 @@ import type { BaseTableAPI } from '../../ts-types/base-table';
|
|||||||
* @return {false | CellRange}
|
* @return {false | CellRange}
|
||||||
*/
|
*/
|
||||||
export function getCellMergeInfo(table: BaseTableAPI, col: number, row: number): false | CellRange {
|
export function getCellMergeInfo(table: BaseTableAPI, col: number, row: number): false | CellRange {
|
||||||
|
// 先判断非表头且非cellMerge配置,返回false
|
||||||
|
if (!table.isHeader(col, row) && (table.getBodyColumnDefine(col, row) as TextColumnDefine).mergeCell !== true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const range = table.getCellRange(col, row);
|
const range = table.getCellRange(col, row);
|
||||||
const isMerge = range.start.col !== range.end.col || range.start.row !== range.end.row;
|
const isMerge = range.start.col !== range.end.col || range.start.row !== range.end.row;
|
||||||
if (!isMerge) {
|
if (!isMerge) {
|
||||||
|
18
packages/vtable/src/tools/pixel-ratio.ts
Normal file
18
packages/vtable/src/tools/pixel-ratio.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { isNode } from './helper';
|
||||||
|
|
||||||
|
export let defaultPixelRatio = 1;
|
||||||
|
/*
|
||||||
|
* @Description: 设置像素比
|
||||||
|
*/
|
||||||
|
function setPixelRatio(): void {
|
||||||
|
if (isNode) {
|
||||||
|
defaultPixelRatio = 1;
|
||||||
|
} else {
|
||||||
|
defaultPixelRatio = Math.ceil(window.devicePixelRatio || 1);
|
||||||
|
if (defaultPixelRatio > 1 && defaultPixelRatio % 2 !== 0) {
|
||||||
|
// 非整数倍的像素比,向上取整
|
||||||
|
defaultPixelRatio += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setPixelRatio();
|
Loading…
Reference in New Issue
Block a user