From 232b7d0523eec7d86a5918459f8d0daf0123cb0c Mon Sep 17 00:00:00 2001 From: lijinke666 Date: Tue, 27 Feb 2024 17:08:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=88=B6=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E5=AD=98=E5=9C=A8=20transform=20=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E6=97=B6=E5=8D=95=E5=85=83=E6=A0=BC=E5=88=B7=E9=80=89=E5=81=8F?= =?UTF-8?q?=E7=A7=BB=20close=20#2553?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unit/interaction/event-controller-spec.ts | 37 +++++++- packages/s2-core/__tests__/util/helpers.ts | 1 + .../brush-selection/col-brush-selection.ts | 6 +- .../data-cell-brush-selection.ts | 6 +- .../brush-selection/row-brush-selection.ts | 6 +- .../src/interaction/event-controller.ts | 24 +++++ .../s2-core/src/sheet-type/spread-sheet.ts | 9 ++ .../playground/components/CustomTree.tsx | 5 + .../docs/api/basic-class/interaction.zh.md | 92 ++++++++++++++++++- .../docs/api/basic-class/spreadsheet.zh.md | 1 + 10 files changed, 170 insertions(+), 17 deletions(-) diff --git a/packages/s2-core/__tests__/unit/interaction/event-controller-spec.ts b/packages/s2-core/__tests__/unit/interaction/event-controller-spec.ts index a884abaad5..57ba406a56 100644 --- a/packages/s2-core/__tests__/unit/interaction/event-controller-spec.ts +++ b/packages/s2-core/__tests__/unit/interaction/event-controller-spec.ts @@ -1004,9 +1004,9 @@ describe('Interaction Event Controller Tests', () => { spreadsheet.on(S2Event.GLOBAL_RESET, reset); const pointInCanvas = getClientPointOnCanvas(spreadsheet.container, 10, 10); - const evt = new window.CustomEvent('click'); + const event = new window.CustomEvent('click'); - Object.defineProperties(evt, { + Object.defineProperties(event, { clientX: { value: pointInCanvas.clientX, }, @@ -1014,10 +1014,41 @@ describe('Interaction Event Controller Tests', () => { value: pointInCanvas.clientY, }, }); - spreadsheet.getCanvasElement().dispatchEvent(evt); + spreadsheet.getCanvasElement().dispatchEvent(event); expect(eventController.isCanvasEffect).toBe(true); expect(reset).not.toHaveBeenCalled(); expect(spreadsheet.interaction.reset).not.toHaveBeenCalled(); }); + + // https://github.com/antvis/S2/issues/2553 + test('should use offset point if enable supportsCSSTransform', () => { + jest.spyOn(spreadsheet, 'getCanvasConfig').mockImplementationOnce(() => { + return { + supportsCSSTransform: true, + }; + }); + + const event = new MouseEvent('click'); + + Object.defineProperties(event, { + offsetX: { + value: 100, + }, + offsetY: { + value: 200, + }, + clientX: { + value: 300, + }, + clientY: { + value: 400, + }, + }); + + expect(eventController.getViewportPoint(event)).toStrictEqual({ + x: 100, + y: 200, + }); + }); }); diff --git a/packages/s2-core/__tests__/util/helpers.ts b/packages/s2-core/__tests__/util/helpers.ts index 736ba8a3db..eace8c462d 100644 --- a/packages/s2-core/__tests__/util/helpers.ts +++ b/packages/s2-core/__tests__/util/helpers.ts @@ -173,6 +173,7 @@ export const createFakeSpreadSheet = (config?: { s2.facet.getCells = jest.fn().mockReturnValue([]); s2.getCanvasElement = () => s2.container.getContextService().getDomElement() as HTMLCanvasElement; + s2.getCanvasConfig = () => s2.container.getConfig(); s2.isCustomHeaderFields = jest.fn(() => false); s2.isCustomRowFields = jest.fn(() => false); s2.isCustomColumnFields = jest.fn(() => false); diff --git a/packages/s2-core/src/interaction/brush-selection/col-brush-selection.ts b/packages/s2-core/src/interaction/brush-selection/col-brush-selection.ts index 3892a326c5..f7f154d97c 100644 --- a/packages/s2-core/src/interaction/brush-selection/col-brush-selection.ts +++ b/packages/s2-core/src/interaction/brush-selection/col-brush-selection.ts @@ -54,10 +54,8 @@ export class ColCellBrushSelection extends BaseBrushSelection { this.setBrushSelectionStage(InteractionBrushSelectionStage.DRAGGED); - const pointInCanvas = this.spreadsheet.container.client2Viewport({ - x: event.clientX, - y: event.clientY, - }); + const pointInCanvas = + this.spreadsheet.interaction.eventController.getViewportPoint(event); if (!this.isPointInCanvas(pointInCanvas)) { return; diff --git a/packages/s2-core/src/interaction/brush-selection/data-cell-brush-selection.ts b/packages/s2-core/src/interaction/brush-selection/data-cell-brush-selection.ts index d3ff5b802a..d5ce6472a2 100644 --- a/packages/s2-core/src/interaction/brush-selection/data-cell-brush-selection.ts +++ b/packages/s2-core/src/interaction/brush-selection/data-cell-brush-selection.ts @@ -40,10 +40,8 @@ export class DataCellBrushSelection extends BaseBrushSelection { } this.setBrushSelectionStage(InteractionBrushSelectionStage.DRAGGED); - const pointInCanvas = this.spreadsheet.container.client2Viewport({ - x: event.clientX, - y: event.clientY, - }); + const pointInCanvas = + this.spreadsheet.interaction.eventController.getViewportPoint(event); if (this.autoBrushScroll(pointInCanvas)) { return; diff --git a/packages/s2-core/src/interaction/brush-selection/row-brush-selection.ts b/packages/s2-core/src/interaction/brush-selection/row-brush-selection.ts index 14b30eabe1..7584263d39 100644 --- a/packages/s2-core/src/interaction/brush-selection/row-brush-selection.ts +++ b/packages/s2-core/src/interaction/brush-selection/row-brush-selection.ts @@ -52,10 +52,8 @@ export class RowCellBrushSelection extends BaseBrushSelection { this.setBrushSelectionStage(InteractionBrushSelectionStage.DRAGGED); - const pointInCanvas = this.spreadsheet.container.client2Viewport({ - x: event.clientX, - y: event.clientY, - }); + const pointInCanvas = + this.spreadsheet.interaction.eventController.getViewportPoint(event); if (this.autoBrushScroll(pointInCanvas, true)) { return; diff --git a/packages/s2-core/src/interaction/event-controller.ts b/packages/s2-core/src/interaction/event-controller.ts index 56e753b24f..a40fbc8fd2 100644 --- a/packages/s2-core/src/interaction/event-controller.ts +++ b/packages/s2-core/src/interaction/event-controller.ts @@ -3,6 +3,7 @@ import { DisplayObject, type FederatedPointerEvent as CanvasEvent, type Group, + type PointLike, } from '@antv/g'; import { each, get, hasIn, isEmpty, isNil } from 'lodash'; import { GuiIcon } from '../common'; @@ -701,4 +702,27 @@ export class EventController { this.s2EventHandlers = []; this.domEventListeners = []; } + + public getViewportPoint( + event: MouseEvent | PointerEvent | CanvasEvent, + ): PointLike { + const config = this.spreadsheet.getCanvasConfig(); + + // https://github.com/antvis/G/blob/a43c19a662684945d0bf9dc1876af43ac26b1243/packages/g-lite/src/plugins/EventPlugin.ts#L216 + if ( + config.supportsCSSTransform && + !isNil(event.offsetX) && + !isNil(event.offsetY) + ) { + return { + x: event.offsetX, + y: event.offsetY, + }; + } + + return this.spreadsheet.container.client2Viewport({ + x: event.clientX, + y: event.clientY, + }); + } } diff --git a/packages/s2-core/src/sheet-type/spread-sheet.ts b/packages/s2-core/src/sheet-type/spread-sheet.ts index 6d68ab317f..db2e7e56fa 100644 --- a/packages/s2-core/src/sheet-type/spread-sheet.ts +++ b/packages/s2-core/src/sheet-type/spread-sheet.ts @@ -599,11 +599,20 @@ export abstract class SpreadSheet extends EE { /** * 获取 G Canvas 实例 + * @see https://g.antv.antgroup.com/api/renderer/canvas */ public getCanvas(): Canvas { return this.container; } + /** + * 获取 G Canvas 配置 + * @see https://g.antv.antgroup.com/api/canvas/options + */ + public getCanvasConfig(): Partial { + return this.getCanvas().getConfig(); + } + /** * 获取 HTML 元素 */ diff --git a/packages/s2-react/playground/components/CustomTree.tsx b/packages/s2-react/playground/components/CustomTree.tsx index cf805a9394..66bf8732c0 100644 --- a/packages/s2-react/playground/components/CustomTree.tsx +++ b/packages/s2-react/playground/components/CustomTree.tsx @@ -28,6 +28,11 @@ export const CustomTreeOptions: SheetComponentOptions = { width: 600, height: 480, hierarchyType: 'tree', + transformCanvasConfig() { + return { + supportsCSSTransform: true, + }; + }, showDefaultHeaderActionIcon: false, interaction: { copy: { diff --git a/s2-site/docs/api/basic-class/interaction.zh.md b/s2-site/docs/api/basic-class/interaction.zh.md index c0d674ee39..a30eef44bf 100644 --- a/s2-site/docs/api/basic-class/interaction.zh.md +++ b/s2-site/docs/api/basic-class/interaction.zh.md @@ -14,7 +14,8 @@ s2.interaction.reset() | --- |--------------------------------------------------| --- | | spreadsheet | 表格实例 | [SpreadSheet](/docs/api/basic-class/spreadsheet) | | interactions | 当前已注册的交互 | `Map` | -| intercept | 当前拦截的交互,防止不同交互之间冲突 | `Set` | +| intercepts | 当前拦截的交互,防止不同交互之间冲突 ([查看示例](/examples/interaction/advanced/#intercepts)) | `Set` | +| eventController | 事件控制器 | [EventController](#eventcontroller) | | destroy | 卸载所有交互实例,并重置为初始状态 | `() => void` | | reset | 重置所有交互 | `() => void` | | setState | 设置状态 | (data: [InteractionStateInfo](#interactionstateinfo)) => void | @@ -25,7 +26,7 @@ s2.interaction.reset() | setInteractedCells | 设置当前发生改变的单元格 | (cell: [S2CellType](#s2celltype)) => void | | getInteractedCells | 获取当前发生改变的单元格 | () => [S2CellType](#s2celltype)[] | | getCurrentStateName | 获取当前状态名 | `() => void` | -| isEqualStateName | 是否是相同的状态名 | `(name: InteractionStateName) => void` | +| isEqualStateName | 是否是相同的状态名 | (name: [InteractionStateName](#interactionstatename)) => void | | isSelectedState | 是否是选中状态 | `() => void` | | isHoverState | 是否是悬停状态 | `() => void` | | isHoverFocusState | 是否是悬停聚焦状态 (悬停在单元格 `focusTime`: 默认 800ms 后) | `() => void` | @@ -121,6 +122,48 @@ interface MergedCellInfo { } ``` +### CellType + +```ts +enum CellType { + DATA_CELL = 'dataCell', + ROW_CELL = 'rowCell', + COL_CELL = 'colCell', + SERIES_NUMBER_CELL = 'seriesNumberCell', + CORNER_CELL = 'cornerCell', + MERGED_CELL = 'mergedCell', +} +``` + +### CellMeta + +```ts +interface CellMeta { + id: string; + colIndex: number; + rowIndex: number; + type: CellType; + rowQuery?: Record; + [key: string]: unknown; +} +``` + +### InteractionStateName + +```ts +enum InteractionStateName { + ALL_SELECTED = 'allSelected', + SELECTED = 'selected', + BRUSH_SELECTED = 'brushSelected', + UNSELECTED = 'unselected', + HOVER = 'hover', + HOVER_FOCUS = 'hoverFocus', + HIGHLIGHT = 'highlight', + SEARCH_RESULT = 'searchResult', + PREPARE_SELECT = 'prepareSelect', +} +``` + ### InteractionStateInfo ```ts @@ -133,4 +176,49 @@ interface InteractionStateInfo { } ``` +### EventController + +| 参数 | 说明 | 类型 | +| --- |--------| --- | +| spreadsheet | 表格实例 | [SpreadSheet](/docs/api/basic-class/spreadsheet) | +| canvasEventHandlers | 当前已注册的交互 | [EventHandler](#eventhandler)[] | +| s2EventHandlers | 当前已注册的交互 | [S2EventHandler](#s2eventhandler)[] | +| domEventListeners | 当前已注册的交互 | [EventHandler](#eventhandler)[] | +| isCanvasEffect | 是否是图表内部引起的事件 | boolean | +| canvasMousemoveEvent | 表格鼠标移动事件 | CanvasEvent | +| isMatchElement | 是否是表格内部的元素 | (event: MouseEvent) => void | +| isMatchPoint | 是否是表格内部的坐标 | (event: MouseEvent) => void | +| bindEvents | 绑定交互事件 | `() => void`) | +| clear | 清空交互事件 | `() => void`) | +| getViewportPoint | 获取表格内的鼠标坐标 (兼容 `supportsCSSTransform`) | `(event: MouseEvent \| PointerEvent \| WheelEvent) => PointLike` | + +### EventListener + +```ts +interface EventListener { + target: EventTarget; + type: string; + handler: EventListenerOrEventListenerObject; + options?: AddEventListenerOptions | boolean; +} +``` + +### S2EventHandler + +```ts +interface S2EventHandler { + type: keyof EmitterType; + handler: EmitterType[keyof EmitterType]; +} +``` + +### EventHandler + +```ts +interface EventHandler { + type: string; + handler: (event: CanvasEvent) => void; +} +``` + diff --git a/s2-site/docs/api/basic-class/spreadsheet.zh.md b/s2-site/docs/api/basic-class/spreadsheet.zh.md index 5212e5984c..04cba372c2 100644 --- a/s2-site/docs/api/basic-class/spreadsheet.zh.md +++ b/s2-site/docs/api/basic-class/spreadsheet.zh.md @@ -60,6 +60,7 @@ s2.isPivotMode() | getInitColLeafNodes | 获取初次渲染的列头叶子节点 (比如:隐藏列头前) | () => [Node[]](/docs/api/basic-class/node/) | | | getCanvasElement | 获取表格对应的 `` HTML 元素 | () => [HTMLCanvasElement](https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement) | | | getCanvas | 获取 G Canvas 实例 | () => [Canvas](https://g.antv.antgroup.com/api/renderer/canvas) | | +| getCanvasConfig | 获取 G Canvas 配置 | () => Partial<[CanvasConfig](https://g.antv.antgroup.com/api/canvas/options)> | | | clearInitColLeafNodes | 清空存储在 store 中的初始叶子节点 | () => void | | | updateSortMethodMap | 更新存储在 store 中的节点排序方式 map, replace 为是否覆盖上一次的值 | (nodeId: string, sortMethod: string, replace?: boolean) => void | | | getMenuDefaultSelectedKeys | 获取 tooltip 中选中的菜单项 key 值 | `(nodeId: string) => string[]` | |