From a98d3fd20ff5e3f817f1ac269504719ce7967f04 Mon Sep 17 00:00:00 2001 From: Jinke Li Date: Tue, 14 Feb 2023 18:04:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=202k=20=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=99=A8=E5=88=87=E6=8D=A2=E5=88=B0=20MacBook=20?= =?UTF-8?q?=E5=90=8E=E8=A1=A8=E6=A0=BC=E6=B8=B2=E6=9F=93=E6=A8=A1=E7=B3=8A?= =?UTF-8?q?=20close=20#2072=20(#2074)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 修复 2k 显示器切换到 MacBook 后表格渲染模糊 close #2072 * test: 增加测试 * test: 修复高清测试 * test: 修复测试 * test: 修复测试 --- .../s2-core/__tests__/bugs/issue-1191-spec.ts | 5 +- .../s2-core/__tests__/bugs/issue-1624-spec.ts | 1 + ...interaction-brush-selection-scroll-spec.ts | 1 + .../data-cell-multi-selection-spec.ts | 1 + .../__tests__/unit/interaction/root-spec.ts | 1 + .../unit/ui/hd-adapter/index-spec.ts | 97 +++++++++++++++---- packages/s2-core/__tests__/util/helpers.ts | 15 ++- packages/s2-core/src/ui/hd-adapter/index.ts | 20 +++- 8 files changed, 115 insertions(+), 26 deletions(-) diff --git a/packages/s2-core/__tests__/bugs/issue-1191-spec.ts b/packages/s2-core/__tests__/bugs/issue-1191-spec.ts index a55d6694a5..d6e4fb6b3f 100644 --- a/packages/s2-core/__tests__/bugs/issue-1191-spec.ts +++ b/packages/s2-core/__tests__/bugs/issue-1191-spec.ts @@ -7,7 +7,7 @@ */ import * as mockDataConfig from 'tests/data/simple-data.json'; import { getContainer } from 'tests/util/helpers'; -import type { S2Options } from '@/common/interface'; +import type { S2DataConfig, S2Options } from '@/common/interface'; import { PivotSheet, SpreadSheet } from '@/sheet-type'; const s2options: S2Options = { @@ -25,9 +25,10 @@ const s2options: S2Options = { interaction: { linkFields: ['province'], }, + hdAdapter: false, }; -const dataCfg = { +const dataCfg: S2DataConfig = { ...mockDataConfig, fields: { rows: ['province', 'city'], diff --git a/packages/s2-core/__tests__/bugs/issue-1624-spec.ts b/packages/s2-core/__tests__/bugs/issue-1624-spec.ts index 6d9f9f8f77..2589ed7966 100644 --- a/packages/s2-core/__tests__/bugs/issue-1624-spec.ts +++ b/packages/s2-core/__tests__/bugs/issue-1624-spec.ts @@ -12,6 +12,7 @@ import { PivotSheet } from '@/sheet-type'; const s2Options: S2Options = { width: 800, height: 600, + hdAdapter: false, }; describe('Data Cell Border Tests', () => { diff --git a/packages/s2-core/__tests__/spreadsheet/interaction-brush-selection-scroll-spec.ts b/packages/s2-core/__tests__/spreadsheet/interaction-brush-selection-scroll-spec.ts index e5c7d74b26..fc7c4093b9 100644 --- a/packages/s2-core/__tests__/spreadsheet/interaction-brush-selection-scroll-spec.ts +++ b/packages/s2-core/__tests__/spreadsheet/interaction-brush-selection-scroll-spec.ts @@ -43,6 +43,7 @@ const dataCfg: S2DataConfig = { const options: S2Options = { width: 800, height: 600, + hdAdapter: false, showSeriesNumber: true, placeholder: '', style: { diff --git a/packages/s2-core/__tests__/unit/interaction/data-cell-multi-selection-spec.ts b/packages/s2-core/__tests__/unit/interaction/data-cell-multi-selection-spec.ts index 03f0da2a1e..6420f5fdd3 100644 --- a/packages/s2-core/__tests__/unit/interaction/data-cell-multi-selection-spec.ts +++ b/packages/s2-core/__tests__/unit/interaction/data-cell-multi-selection-spec.ts @@ -11,6 +11,7 @@ import { } from '@/common/constant'; jest.mock('@/interaction/event-controller'); +jest.mock('@/ui/hd-adapter'); describe('Interaction Data Cell Multi Selection Tests', () => { let dataCellMultiSelection: DataCellMultiSelection; diff --git a/packages/s2-core/__tests__/unit/interaction/root-spec.ts b/packages/s2-core/__tests__/unit/interaction/root-spec.ts index e8ec1173bc..60502c7e71 100644 --- a/packages/s2-core/__tests__/unit/interaction/root-spec.ts +++ b/packages/s2-core/__tests__/unit/interaction/root-spec.ts @@ -33,6 +33,7 @@ import { getCellMeta } from '@/utils/interaction/select-event'; jest.mock('@/sheet-type'); jest.mock('@/interaction/event-controller'); +jest.mock('@/ui/hd-adapter'); jest.mock('@/utils/interaction/merge-cell', () => { return { mergeCell: jest.fn(), diff --git a/packages/s2-core/__tests__/unit/ui/hd-adapter/index-spec.ts b/packages/s2-core/__tests__/unit/ui/hd-adapter/index-spec.ts index 1546223122..46ea20c758 100644 --- a/packages/s2-core/__tests__/unit/ui/hd-adapter/index-spec.ts +++ b/packages/s2-core/__tests__/unit/ui/hd-adapter/index-spec.ts @@ -1,16 +1,23 @@ -import { createFakeSpreadSheet, sleep } from 'tests/util/helpers'; +/* eslint-disable @typescript-eslint/ban-ts-comment */ +/* eslint-disable jest/expect-expect */ +import { createPivotSheet, sleep } from 'tests/util/helpers'; +import type { S2Options } from '../../../../src'; import type { SpreadSheet } from '@/sheet-type/spread-sheet'; -import { HdAdapter } from '@/ui/hd-adapter'; jest.mock('@/interaction/event-controller'); jest.mock('@/interaction/root'); // eslint-disable-next-line jest/no-disabled-tests describe.skip('HD Adapter Tests', () => { - const DPR = 2; + const DPR = 1; + const s2Options: S2Options = { + width: 600, + height: 600, + devicePixelRatio: DPR, + hdAdapter: true, + }; let s2: SpreadSheet; - let hdAdapter: HdAdapter; let expectContainerSize: ( size?: [number, number], updatedSize?: [number, number], @@ -25,9 +32,8 @@ describe.skip('HD Adapter Tests', () => { configurable: true, }); - s2 = createFakeSpreadSheet(); - hdAdapter = new HdAdapter(s2); - hdAdapter.init(); + s2 = createPivotSheet(s2Options); + s2.render(); expectContainerSize = ( [width, height] = [s2.options.width, s2.options.height], @@ -36,7 +42,7 @@ describe.skip('HD Adapter Tests', () => { s2.options.height * DPR, ], ) => { - const canvas: HTMLCanvasElement = s2.container.get('el'); + const canvas = s2.getCanvasElement(); expect(canvas.style.width).toEqual(`${width}px`); expect(canvas.style.height).toEqual(`${height}px`); expect(canvas.width).toEqual(updatedWidth); @@ -45,7 +51,7 @@ describe.skip('HD Adapter Tests', () => { }); afterEach(() => { - hdAdapter.destroy(); + s2.destroy(); Object.defineProperty(visualViewport, 'scale', { value: 1, @@ -59,16 +65,16 @@ describe.skip('HD Adapter Tests', () => { }); test('should not be update container size when zoom scale changed, but scale less than current DPR', async () => { + const renderSpy = jest.spyOn(s2, 'render').mockImplementationOnce(() => {}); visualViewport.dispatchEvent(new Event('resize')); await sleep(500); expectContainerSize(); - expect(s2.render).not.toHaveBeenCalled(); + expect(renderSpy).not.toHaveBeenCalled(); }); - // eslint-disable-next-line jest/expect-expect test('should update container size when zoom scale changed, and scale more than current DPR', async () => { - const scale = 2; + const scale = 3; Object.defineProperty(visualViewport, 'scale', { value: scale, configurable: true, @@ -82,9 +88,23 @@ describe.skip('HD Adapter Tests', () => { [s2.options.width, s2.options.height], [s2.options.width * scale, s2.options.height * scale], ); + + // 双指缩放回原始比例, 还原为默认宽高 + Object.defineProperty(visualViewport, 'scale', { + value: 1, + configurable: true, + }); + visualViewport.dispatchEvent(new Event('resize')); + await sleep(500); + + expectContainerSize( + [s2.options.width, s2.options.height], + [s2.options.width * DPR, s2.options.height * DPR], + ); }); test('should use DPR for update container size when zoom scale changed, and scale less than current DPR', async () => { + const renderSpy = jest.spyOn(s2, 'render').mockImplementationOnce(() => {}); Object.defineProperty(visualViewport, 'scale', { value: 1, configurable: true, @@ -92,11 +112,14 @@ describe.skip('HD Adapter Tests', () => { visualViewport.dispatchEvent(new Event('resize')); await sleep(500); - expect(s2.render).not.toHaveBeenCalled(); + expect(renderSpy).not.toHaveBeenCalled(); }); test('should not rerender when zoom event destroyed', async () => { - hdAdapter.destroy(); + const renderSpy = jest.spyOn(s2, 'render').mockImplementationOnce(() => {}); + + s2.destroy(); + Object.defineProperty(visualViewport, 'scale', { value: 3, configurable: true, @@ -104,21 +127,59 @@ describe.skip('HD Adapter Tests', () => { visualViewport.dispatchEvent(new Event('resize')); await sleep(500); - expect(s2.render).not.toHaveBeenCalled(); + expect(renderSpy).not.toHaveBeenCalled(); }); test('should not rerender when zoom event destroyed on mobile device', async () => { - hdAdapter.destroy(); + const renderSpy = jest.spyOn(s2, 'render').mockImplementationOnce(() => {}); + + s2.hdAdapter.destroy(); + Object.defineProperty(navigator, 'userAgent', { value: 'iPhone', configurable: true, }); - hdAdapter.init(); visualViewport.dispatchEvent(new Event('resize')); await sleep(500); expectContainerSize(); - expect(s2.render).not.toHaveBeenCalled(); + expect(renderSpy).not.toHaveBeenCalled(); + }); + + test('should update canvas size if DPR changed', () => { + const ratio = 3; + Object.defineProperty(window, 'devicePixelRatio', { + value: ratio, + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore 模拟 matchMedia 触发 + s2.hdAdapter.renderByDevicePixelRatioChanged(); + + expectContainerSize( + [s2.options.width, s2.options.height], + [s2.options.width * ratio, s2.options.height * ratio], + ); + }); + + // https://github.com/antvis/S2/issues/2072 + test('should ignore visualViewport resize effect', () => { + const renderByDevicePixelRatioSpy = jest + .spyOn(s2.hdAdapter as any, 'renderByDevicePixelRatio') + .mockImplementationOnce(() => {}); + + const ratio = 3; + + Object.defineProperty(window, 'devicePixelRatio', { + value: ratio, + }); + + // @ts-ignore + s2.hdAdapter.renderByDevicePixelRatioChanged(); + visualViewport.dispatchEvent(new Event('resize')); + // @ts-ignore 模拟 matchMedia 触发后 visualViewport resize 事件触发 + s2.hdAdapter.isDevicePixelRatioChange = true; + + expect(renderByDevicePixelRatioSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/s2-core/__tests__/util/helpers.ts b/packages/s2-core/__tests__/util/helpers.ts index fbaf35bd06..0016101ced 100644 --- a/packages/s2-core/__tests__/util/helpers.ts +++ b/packages/s2-core/__tests__/util/helpers.ts @@ -53,7 +53,10 @@ export const createFakeSpreadSheet = () => { } const s2 = new FakeSpreadSheet() as unknown as SpreadSheet; - s2.options = DEFAULT_OPTIONS; + s2.options = { + ...DEFAULT_OPTIONS, + hdAdapter: false, + }; s2.dataCfg = { meta: null, data: [], @@ -180,7 +183,10 @@ export const createPivotSheet = ( return new PivotSheet( getContainer(), useSimpleData ? simpleDataConfig : dataConfig, - s2Options, + { + hdAdapter: false, + ...s2Options, + }, ); }; @@ -191,6 +197,9 @@ export const createTableSheet = ( return new TableSheet( getContainer(), useSimpleData ? simpleDataConfig : dataConfig, - s2Options, + { + hdAdapter: false, + ...s2Options, + }, ); }; diff --git a/packages/s2-core/src/ui/hd-adapter/index.ts b/packages/s2-core/src/ui/hd-adapter/index.ts index 9c3b925cd8..eabdef949a 100644 --- a/packages/s2-core/src/ui/hd-adapter/index.ts +++ b/packages/s2-core/src/ui/hd-adapter/index.ts @@ -10,6 +10,8 @@ export class HdAdapter { private spreadsheet: SpreadSheet; + private isDevicePixelRatioChange = false; + constructor(spreadsheet: SpreadSheet) { this.spreadsheet = spreadsheet; } @@ -60,7 +62,7 @@ export class HdAdapter { // VisualViewport support browser zoom & mac touch tablet this.viewport?.visualViewport?.addEventListener( 'resize', - this.renderByZoomScale, + this.renderByZoomScaleWithoutResizeEffect, ); }; @@ -70,11 +72,23 @@ export class HdAdapter { } this.viewport?.visualViewport?.removeEventListener( 'resize', - this.renderByZoomScale, + this.renderByZoomScaleWithoutResizeEffect, ); }; + /** + * DPR 改变也会触发 visualViewport 的 resize 事件, 预期是只监听双指缩放, 所以这里规避掉 + * @see https://github.com/antvis/S2/issues/2072 + */ + private renderByZoomScaleWithoutResizeEffect = ( + event: Event & { target: VisualViewport }, + ) => { + this.isDevicePixelRatioChange = false; + this.renderByZoomScale(event); + }; + private renderByDevicePixelRatioChanged = () => { + this.isDevicePixelRatioChange = true; this.renderByDevicePixelRatio(); }; @@ -107,7 +121,7 @@ export class HdAdapter { private renderByZoomScale = debounce( (event: Event & { target: VisualViewport }) => { const ratio = Math.ceil(event.target.scale); - if (ratio >= 1) { + if (ratio >= 1 && !this.isDevicePixelRatioChange) { this.renderByDevicePixelRatio(ratio); } },