Skip to content

Commit

Permalink
Merge pull request #769 from VisActor/681-refactor-100w-records-scrol…
Browse files Browse the repository at this point in the history
…l-performance-optimize

681 refactor 100w records scroll performance optimize
  • Loading branch information
fangsmile authored Dec 20, 2023
2 parents ad80604 + 8d35ee1 commit fdbcc86
Show file tree
Hide file tree
Showing 15 changed files with 157 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "refactor: 100W records scroll performance optimize when has select Cell #681\n\n",
"type": "none",
"packageName": "@visactor/vtable"
}
],
"packageName": "@visactor/vtable",
"email": "[email protected]"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"comment": "fix: stopPropagation effect doubletap\n\n",
"type": "none",
"packageName": "@visactor/vtable"
}
],
"packageName": "@visactor/vtable",
"email": "[email protected]"
}
4 changes: 4 additions & 0 deletions docs/assets/api/en/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ Get the selected cell information, and the returned result is a two-dimensional
getSelectedCellInfos(): CellInfo[][] | null;
```

## clearSelected(Function)

Clear the selection of all cells.

## getCellValue(Function)

Get cell display value
Expand Down
3 changes: 3 additions & 0 deletions docs/assets/api/zh/methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ tableInstance.renderWithRecreateCells();
/**获取选中区域的每个单元格详情 */
getSelectedCellInfos(): CellInfo[][] | null;
```
## clearSelected(Function)

清除所有单元格的选中状态。

## getCellValue(Function)

Expand Down
2 changes: 1 addition & 1 deletion docs/assets/guide/en/interaction/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ tableInstance.selectCells([{start:{col:1,row:2},end:{col:4,row:2}},{start:{col:3
```

### Clear current selection
When the selectCell interface is called, it can be cleared by passing null, and a special interface clearSelectedCell will be added later.
call api `clearSelected`.

## Select style

Expand Down
2 changes: 1 addition & 1 deletion docs/assets/guide/zh/interaction/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ tableInstance.selectCells([{start:{col:1,row:2},end:{col:4,row:2}},{start:{col:3
```

### 清除当前选中
selectCell接口调用时传空可以清除,后面增加专门的接口clearSelectedCell。
调用接口`clearSelected`

## 选中样式

Expand Down
41 changes: 17 additions & 24 deletions packages/vtable/src/core/BaseTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2260,34 +2260,27 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI {
* @returns
*/
private computeTargetRowByY(absoluteY: number): number {
//此方式效率太低,借助缓存,或者大约计算出一个值
// this.rowHeightsMap.each(0, this.rowCount - 1, (height: number, row: number): boolean | void => {
// h += height || this.internalProps.defaultRowHeight;
// if (h > absoluteY) {
// targetRow = row;
// return false;
// }
// });
let defaultRowHeight = this.internalProps.defaultRowHeight;

//使用二分法计算出row
if (this._rowRangeHeightsMap.get(`$0$${this.rowCount - 1}`)) {
let startRow = 0;
let endRow = this.rowCount - 1;
while (endRow - startRow > 1) {
const midRow = Math.floor((startRow + endRow) / 2);
if (absoluteY < this._rowRangeHeightsMap.get(`$0$${midRow}`)) {
endRow = midRow;
} else if (absoluteY > this._rowRangeHeightsMap.get(`$0$${midRow}`)) {
startRow = midRow;
} else {
return midRow;
}
}
return endRow;
// if (this._rowRangeHeightsMap.get[`$0$${endRow}`] === absoluteY) return endRow;
// if (this._rowRangeHeightsMap.get[`$0$${startRow}`] === absoluteY) return startRow;
defaultRowHeight = this._rowRangeHeightsMap.get(`$0$${this.rowCount - 1}`) / this.rowCount;
// let startRow = 0;
// let endRow = this.rowCount - 1;
// while (endRow - startRow > 1) {
// const midRow = Math.floor((startRow + endRow) / 2);
// if (absoluteY < this._rowRangeHeightsMap.get(`$0$${midRow}`)) {
// endRow = midRow;
// } else if (absoluteY > this._rowRangeHeightsMap.get(`$0$${midRow}`)) {
// startRow = midRow;
// } else {
// return midRow;
// }
// }
// return endRow;
}
//否则使用defaultRowHeight大约计算一个row
return Math.min(Math.ceil(absoluteY / this.internalProps.defaultRowHeight), this.rowCount - 1);
return Math.min(Math.ceil(absoluteY / defaultRowHeight), this.rowCount - 1);
}
/**
* 根据x值(包括了scroll的)计算所在列 主要借助colRangeWidthsMap缓存来提高计算效率
Expand Down
7 changes: 7 additions & 0 deletions packages/vtable/src/event/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ export class EventManager {
touchMove: boolean; // is touch listener working, use to disable document touch scrolling function
gesture: Gesture;
handleTextStickBindId: number;

//鼠标事件记录
LastPointerXY: { x: number; y: number };
LastBodyPointerXY: { x: number; y: number };
isDown = false;
isDraging = false;

constructor(table: BaseTableAPI) {
this.table = table;
if (Env.mode === 'node') {
Expand Down
33 changes: 29 additions & 4 deletions packages/vtable/src/event/listener/scroll-bar.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import type { FederatedPointerEvent } from '@visactor/vrender';
import { throttle } from '../../tools/util';
import type { ListTableAPI } from '../../ts-types';
import { InteractionState } from '../../ts-types';
import type { EventManager } from '../event';
import type { SceneEvent } from '../util';
import { getCellEventArgsSet } from '../util';

export function bindScrollBarListener(eventManager: EventManager) {
const table = eventManager.table;
Expand All @@ -26,11 +30,21 @@ export function bindScrollBarListener(eventManager: EventManager) {
}
stateManager.hideHorizontalScrollBar();
});
// 目前ScrollBar的pointerdown事件回调内有e.stopPropagation,因此无法通过vScrollBar监听,先使用_slider监听
(scenegraph.component.vScrollBar as any)._slider.addEventListener('pointerdown', () => {
scenegraph.component.vScrollBar.addEventListener('scrollDown', (e: FederatedPointerEvent) => {
scenegraph.table.eventManager.LastBodyPointerXY = { x: e.x, y: e.y };
scenegraph.table.eventManager.isDown = true;
if (stateManager.interactionState !== InteractionState.scrolling) {
stateManager.updateInteractionState(InteractionState.scrolling);
}
const eventArgsSet: SceneEvent = getCellEventArgsSet(e);
if (
scenegraph.table.stateManager.menu.isShow &&
(eventArgsSet.eventArgs?.target as any) !== scenegraph.table.stateManager.residentHoverIcon?.icon
) {
scenegraph.table.stateManager.hideMenu();
}

(scenegraph.table as ListTableAPI).editorManager?.completeEdit();
});
scenegraph.component.vScrollBar.addEventListener('pointerup', () => {
stateManager.fastScrolling = false;
Expand All @@ -44,11 +58,22 @@ export function bindScrollBarListener(eventManager: EventManager) {
stateManager.updateInteractionState(InteractionState.default);
}
});
// 目前ScrollBar的pointerdown事件回调内有e.stopPropagation,因此无法通过hScrollBar监听,先使用_slider监听
(scenegraph.component.hScrollBar as any)._slider.addEventListener('pointerdown', () => {
scenegraph.component.hScrollBar.addEventListener('scrollDown', (e: FederatedPointerEvent) => {
scenegraph.table.eventManager.LastBodyPointerXY = { x: e.x, y: e.y };
scenegraph.table.eventManager.isDown = true;
if (stateManager.interactionState !== InteractionState.scrolling) {
stateManager.updateInteractionState(InteractionState.scrolling);
}

const eventArgsSet: SceneEvent = getCellEventArgsSet(e);
if (
scenegraph.table.stateManager.menu.isShow &&
(eventArgsSet.eventArgs?.target as any) !== scenegraph.table.stateManager.residentHoverIcon?.icon
) {
scenegraph.table.stateManager.hideMenu();
}

(scenegraph.table as ListTableAPI).editorManager?.completeEdit();
});
scenegraph.component.hScrollBar.addEventListener('pointerup', () => {
stateManager.fastScrolling = false;
Expand Down
51 changes: 29 additions & 22 deletions packages/vtable/src/event/listener/table-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,28 @@ import { getCellMergeInfo } from '../../scenegraph/utils/get-cell-merge';
import type { CheckBox, CheckboxAttributes } from '@visactor/vrender-components';

// PointerMove敏感度太高了 记录下上一个鼠标位置 在接收到PointerMove事件时做判断 是否到到触发框选或者移动表头操作的标准,防止误触
let LastPointerXY: { x: number; y: number };
let LastBodyPointerXY: { x: number; y: number };
let isDown = false;
let isDraging = false;

export function bindTableGroupListener(eventManager: EventManager) {
const table = eventManager.table;
const stateManager = table.stateManager;

document.body.addEventListener('pointerdown', e => {
LastBodyPointerXY = { x: e.x, y: e.y };
isDown = true;
console.log('body pointerdown');
table.eventManager.LastBodyPointerXY = { x: e.x, y: e.y };
table.eventManager.isDown = true;
});
document.addEventListener('pointerup', e => {
LastBodyPointerXY = null;
// console.log('body pointerup', isDown, isDraging);
isDown = false;
isDraging = false;
table.eventManager.LastBodyPointerXY = null;
console.log('body pointerup', table.eventManager.isDown, table.eventManager.isDraging);
table.eventManager.isDown = false;
table.eventManager.isDraging = false;
});
document.body.addEventListener('pointermove', (e: FederatedPointerEvent) => {
if (isDown && LastBodyPointerXY) {
const lastX = LastBodyPointerXY?.x ?? e.x;
const lastY = LastBodyPointerXY?.y ?? e.y;
if (table.eventManager.isDown && table.eventManager.LastBodyPointerXY) {
const lastX = table.eventManager.LastBodyPointerXY?.x ?? e.x;
const lastY = table.eventManager.LastBodyPointerXY?.y ?? e.y;
if (Math.abs(lastX - e.x) > 1 || Math.abs(lastY - e.y) > 1) {
isDraging = true;
table.eventManager.isDraging = true;
}
}
// 注释掉。因为: 这里pointermove太敏感了 点击快的时候 可能动了1px这里也会执行到 就影响到下面选中不触发的问题。下面pointermove就有这段逻辑,这里先去掉
Expand All @@ -72,9 +70,9 @@ export function bindTableGroupListener(eventManager: EventManager) {
}
});
table.scenegraph.tableGroup.addEventListener('pointermove', (e: FederatedPointerEvent) => {
const lastX = LastPointerXY?.x ?? e.x;
const lastY = LastPointerXY?.y ?? e.y;
LastPointerXY = { x: e.x, y: e.y };
const lastX = table.eventManager.LastPointerXY?.x ?? e.x;
const lastY = table.eventManager.LastPointerXY?.y ?? e.y;
table.eventManager.LastPointerXY = { x: e.x, y: e.y };
// const eventArgsSet: SceneEvent = (table as any).getCellEventArgsSet(e);
if (eventManager.touchSetTimeout) {
// 移动端事件特殊处理
Expand Down Expand Up @@ -286,7 +284,9 @@ export function bindTableGroupListener(eventManager: EventManager) {
const eventArgsSet: SceneEvent = getCellEventArgsSet(e);
if (stateManager.menu.isShow && (eventArgsSet.eventArgs?.target as any) !== stateManager.residentHoverIcon?.icon) {
setTimeout(() => {
stateManager.menu.isShow && stateManager.hideMenu();
if (!table.internalProps.menuHandler.pointInMenuElement(e.page.x, e.page.y)) {
stateManager.menu.isShow && stateManager.hideMenu();
}
}, 0);
}
// 同pointerup中的逻辑
Expand Down Expand Up @@ -340,7 +340,14 @@ export function bindTableGroupListener(eventManager: EventManager) {
});

table.scenegraph.tableGroup.addEventListener('pointerdown', (e: FederatedPointerEvent) => {
LastPointerXY = { x: e.x, y: e.y };
console.log('tableGroup pointerdown');
table.eventManager.isDown = true;
table.eventManager.LastBodyPointerXY = { x: e.x, y: e.y };
// 避免在调整列宽等拖拽操作触发外层组件的拖拽逻辑
// 如果鼠标位置在表格内(加调整列宽的热区),将mousedown事件阻止冒泡
e.stopPropagation();

table.eventManager.LastPointerXY = { x: e.x, y: e.y };
if (e.button !== 0) {
// 只处理左键
return;
Expand Down Expand Up @@ -606,7 +613,7 @@ export function bindTableGroupListener(eventManager: EventManager) {
const target = e.target;
if (
// 如果是鼠标点击到canvas空白区域 则取消选中状态
!isDraging &&
!table.eventManager.isDraging &&
target &&
!target.isDescendantsOf(table.scenegraph.tableGroup)
// &&
Expand All @@ -618,7 +625,7 @@ export function bindTableGroupListener(eventManager: EventManager) {
eventManager.dealTableSelect();
stateManager.updateCursor();
table.scenegraph.updateChartState(null);
} else if (isDraging) {
} else if (table.eventManager.isDraging) {
// 如果鼠标拖拽后是否 则结束选中
stateManager.endSelectCells();
}
Expand Down Expand Up @@ -694,7 +701,7 @@ export function bindTableGroupListener(eventManager: EventManager) {
}
export function bindGesture(eventManager: EventManager) {
const table = eventManager.table;
eventManager.gesture = new Gesture(table.scenegraph.stage as unknown as IEventTarget, {
eventManager.gesture = new Gesture(table.scenegraph.tableGroup as unknown as IEventTarget, {
tap: {
interval: 300
}
Expand Down
32 changes: 21 additions & 11 deletions packages/vtable/src/layout/pivot-header-layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -803,10 +803,10 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI {
// }

isHeader(col: number, row: number): boolean {
if (col < this.rowHeaderLevelCount) {
if (col >= 0 && col < this.rowHeaderLevelCount) {
return true;
}
if (row < this.columnHeaderLevelCount) {
if (row >= 0 && row < this.columnHeaderLevelCount) {
return true;
}
if (col >= this.colCount - this.rightHeaderColCount) {
Expand All @@ -818,19 +818,19 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI {
return false;
}
isCornerHeader(col: number, row: number): boolean {
if (col < this.rowHeaderLevelCount && row < this.columnHeaderLevelCount) {
if (col >= 0 && col < this.rowHeaderLevelCount && row >= 0 && row < this.columnHeaderLevelCount) {
return true;
}
return false;
}
isColumnHeader(col: number, row: number): boolean {
if (col >= this.rowHeaderLevelCount && row < this.columnHeaderLevelCount) {
if (col >= this.rowHeaderLevelCount && row >= 0 && row < this.columnHeaderLevelCount) {
return true;
}
return false;
}
isRowHeader(col: number, row: number): boolean {
if (col < this.rowHeaderLevelCount && row >= this.columnHeaderLevelCount) {
if (col >= 0 && col < this.rowHeaderLevelCount && row >= this.columnHeaderLevelCount) {
return true;
}
return false;
Expand All @@ -843,11 +843,16 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI {
*/
isFrozenColumn(col: number, row?: number): boolean {
if (isValid(row)) {
if (col < this.frozenColCount && row >= this.frozenRowCount && row < this.rowCount - this.bottomFrozenRowCount) {
if (
col >= 0 &&
col < this.frozenColCount &&
row >= this.frozenRowCount &&
row < this.rowCount - this.bottomFrozenRowCount
) {
return true;
}
} else {
if (this.frozenColCount > 0 && col < this.frozenColCount) {
if (this.frozenColCount > 0 && col >= 0 && col < this.frozenColCount) {
return true;
}
}
Expand Down Expand Up @@ -883,12 +888,17 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI {
*/
isFrozenRow(col: number, row?: number): boolean {
if (isValid(row)) {
if (row < this.frozenRowCount && col >= this.frozenColCount && col < this.colCount - this.rightFrozenColCount) {
if (
row >= 0 &&
row < this.frozenRowCount &&
col >= this.frozenColCount &&
col < this.colCount - this.rightFrozenColCount
) {
return true;
}
} else {
row = col;
if (this.frozenRowCount > 0 && row < this.frozenRowCount) {
if (this.frozenRowCount > 0 && row >= 0 && row < this.frozenRowCount) {
return true;
}
}
Expand Down Expand Up @@ -918,13 +928,13 @@ export class PivotHeaderLayoutMap implements LayoutMapAPI {
return false;
}
isLeftBottomCorner(col: number, row: number): boolean {
if (col < this.rowHeaderLevelCount && row >= this.rowCount - this.bottomFrozenRowCount) {
if (col >= 0 && col < this.rowHeaderLevelCount && row >= this.rowCount - this.bottomFrozenRowCount) {
return true;
}
return false;
}
isRightTopCorner(col: number, row: number): boolean {
if (col >= this.colCount - this.rightFrozenColCount && row < this.columnHeaderLevelCount) {
if (col >= this.colCount - this.rightFrozenColCount && row >= 0 && row < this.columnHeaderLevelCount) {
return true;
}
return false;
Expand Down
Loading

0 comments on commit fdbcc86

Please sign in to comment.