Procházet zdrojové kódy

Don't reset cache while zooming using a gesture (#1103)

* Don't reset cache while zooming using a gesture

This reuses the cached canvas while the gesture is happening. Once it has stop updating, then recompute the cache with the proper zoom.

This should massively improve performance when panning on big scenes on mobile

Fixes #1056

* update snapshot tests
vanilla_orig
Christopher Chedeau před 5 roky
rodič
revize
24fa657093
Žádný účet není propojen s e-mailovou adresou tvůrce revize

+ 1
- 0
.watchmanconfig Zobrazit soubor

1
+{}

+ 1
- 0
src/appState.ts Zobrazit soubor

35
     lastPointerDownWith: "mouse",
35
     lastPointerDownWith: "mouse",
36
     selectedElementIds: {},
36
     selectedElementIds: {},
37
     collaborators: new Map(),
37
     collaborators: new Map(),
38
+    shouldCacheIgnoreZoom: false,
38
   };
39
   };
39
 }
40
 }
40
 
41
 

+ 7
- 0
src/components/App.tsx Zobrazit soubor

481
         viewBackgroundColor: this.state.viewBackgroundColor,
481
         viewBackgroundColor: this.state.viewBackgroundColor,
482
         zoom: this.state.zoom,
482
         zoom: this.state.zoom,
483
         remotePointerViewportCoords: pointerViewportCoords,
483
         remotePointerViewportCoords: pointerViewportCoords,
484
+        shouldCacheIgnoreZoom: this.state.shouldCacheIgnoreZoom,
484
       },
485
       },
485
       {
486
       {
486
         renderOptimizations: true,
487
         renderOptimizations: true,
1247
         scrollX: normalizeScroll(this.state.scrollX + deltaX / this.state.zoom),
1248
         scrollX: normalizeScroll(this.state.scrollX + deltaX / this.state.zoom),
1248
         scrollY: normalizeScroll(this.state.scrollY + deltaY / this.state.zoom),
1249
         scrollY: normalizeScroll(this.state.scrollY + deltaY / this.state.zoom),
1249
         zoom: getNormalizedZoom(gesture.initialScale! * scaleFactor),
1250
         zoom: getNormalizedZoom(gesture.initialScale! * scaleFactor),
1251
+        shouldCacheIgnoreZoom: true,
1250
       });
1252
       });
1253
+      this.resetShouldCacheIgnoreZoomDebounced();
1251
     } else {
1254
     } else {
1252
       gesture.lastCenter = gesture.initialDistance = gesture.initialScale = null;
1255
       gesture.lastCenter = gesture.initialDistance = gesture.initialScale = null;
1253
     }
1256
     }
2553
     this.socket && this.broadcastMouseLocation({ pointerCoords });
2556
     this.socket && this.broadcastMouseLocation({ pointerCoords });
2554
   };
2557
   };
2555
 
2558
 
2559
+  private resetShouldCacheIgnoreZoomDebounced = debounce(() => {
2560
+    this.setState({ shouldCacheIgnoreZoom: false });
2561
+  }, 1000);
2562
+
2556
   private saveDebounced = debounce(() => {
2563
   private saveDebounced = debounce(() => {
2557
     saveToLocalStorage(globalSceneState.getAllElements(), this.state);
2564
     saveToLocalStorage(globalSceneState.getAllElements(), this.state);
2558
   }, 300);
2565
   }, 300);

+ 11
- 7
src/renderer/renderElement.ts Zobrazit soubor

245
   }
245
   }
246
   const zoom = sceneState ? sceneState.zoom : 1;
246
   const zoom = sceneState ? sceneState.zoom : 1;
247
   const prevElementWithCanvas = elementWithCanvasCache.get(element);
247
   const prevElementWithCanvas = elementWithCanvasCache.get(element);
248
-  if (!prevElementWithCanvas || prevElementWithCanvas.canvasZoom !== zoom) {
248
+  const shouldRegenerateBecauseZoom =
249
+    prevElementWithCanvas &&
250
+    prevElementWithCanvas.canvasZoom !== zoom &&
251
+    !sceneState?.shouldCacheIgnoreZoom;
252
+  if (!prevElementWithCanvas || shouldRegenerateBecauseZoom) {
249
     const elementWithCanvas = generateElementCanvas(element, zoom);
253
     const elementWithCanvas = generateElementCanvas(element, zoom);
250
     elementWithCanvasCache.set(element, elementWithCanvas);
254
     elementWithCanvasCache.set(element, elementWithCanvas);
251
     return elementWithCanvas;
255
     return elementWithCanvas;
261
 ) {
265
 ) {
262
   context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
266
   context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio);
263
   context.translate(
267
   context.translate(
264
-    -CANVAS_PADDING / sceneState.zoom,
265
-    -CANVAS_PADDING / sceneState.zoom,
268
+    -CANVAS_PADDING / elementWithCanvas.canvasZoom,
269
+    -CANVAS_PADDING / elementWithCanvas.canvasZoom,
266
   );
270
   );
267
   context.drawImage(
271
   context.drawImage(
268
     elementWithCanvas.canvas!,
272
     elementWithCanvas.canvas!,
276
         (Math.floor(elementWithCanvas.element.y) + sceneState.scrollY) *
280
         (Math.floor(elementWithCanvas.element.y) + sceneState.scrollY) *
277
           window.devicePixelRatio,
281
           window.devicePixelRatio,
278
     ),
282
     ),
279
-    elementWithCanvas.canvas!.width / sceneState.zoom,
280
-    elementWithCanvas.canvas!.height / sceneState.zoom,
283
+    elementWithCanvas.canvas!.width / elementWithCanvas.canvasZoom,
284
+    elementWithCanvas.canvas!.height / elementWithCanvas.canvasZoom,
281
   );
285
   );
282
   context.translate(
286
   context.translate(
283
-    CANVAS_PADDING / sceneState.zoom,
284
-    CANVAS_PADDING / sceneState.zoom,
287
+    CANVAS_PADDING / elementWithCanvas.canvasZoom,
288
+    CANVAS_PADDING / elementWithCanvas.canvasZoom,
285
   );
289
   );
286
   context.scale(window.devicePixelRatio, window.devicePixelRatio);
290
   context.scale(window.devicePixelRatio, window.devicePixelRatio);
287
 }
291
 }

+ 1
- 0
src/scene/export.ts Zobrazit soubor

50
       scrollY: normalizeScroll(-minY + exportPadding),
50
       scrollY: normalizeScroll(-minY + exportPadding),
51
       zoom: 1,
51
       zoom: 1,
52
       remotePointerViewportCoords: {},
52
       remotePointerViewportCoords: {},
53
+      shouldCacheIgnoreZoom: false,
53
     },
54
     },
54
     {
55
     {
55
       renderScrollbars: false,
56
       renderScrollbars: false,

+ 1
- 0
src/scene/types.ts Zobrazit soubor

7
   // null indicates transparent bg
7
   // null indicates transparent bg
8
   viewBackgroundColor: string | null;
8
   viewBackgroundColor: string | null;
9
   zoom: number;
9
   zoom: number;
10
+  shouldCacheIgnoreZoom: boolean;
10
   remotePointerViewportCoords: { [id: string]: { x: number; y: number } };
11
   remotePointerViewportCoords: { [id: string]: { x: number; y: number } };
11
 };
12
 };
12
 
13
 

+ 41
- 0
src/tests/__snapshots__/regressionTests.test.tsx.snap Zobrazit soubor

33
     "id2": true,
33
     "id2": true,
34
   },
34
   },
35
   "selectionElement": null,
35
   "selectionElement": null,
36
+  "shouldCacheIgnoreZoom": false,
36
   "viewBackgroundColor": "#ffffff",
37
   "viewBackgroundColor": "#ffffff",
37
   "zoom": 1,
38
   "zoom": 1,
38
 }
39
 }
211
     "id0": true,
212
     "id0": true,
212
   },
213
   },
213
   "selectionElement": null,
214
   "selectionElement": null,
215
+  "shouldCacheIgnoreZoom": false,
214
   "viewBackgroundColor": "#ffffff",
216
   "viewBackgroundColor": "#ffffff",
215
   "zoom": 1,
217
   "zoom": 1,
216
 }
218
 }
316
     "id0": true,
318
     "id0": true,
317
   },
319
   },
318
   "selectionElement": null,
320
   "selectionElement": null,
321
+  "shouldCacheIgnoreZoom": false,
319
   "viewBackgroundColor": "#ffffff",
322
   "viewBackgroundColor": "#ffffff",
320
   "zoom": 1,
323
   "zoom": 1,
321
 }
324
 }
558
     "id1": true,
561
     "id1": true,
559
   },
562
   },
560
   "selectionElement": null,
563
   "selectionElement": null,
564
+  "shouldCacheIgnoreZoom": false,
561
   "viewBackgroundColor": "#ffffff",
565
   "viewBackgroundColor": "#ffffff",
562
   "zoom": 1,
566
   "zoom": 1,
563
 }
567
 }
699
     "id2": true,
703
     "id2": true,
700
   },
704
   },
701
   "selectionElement": null,
705
   "selectionElement": null,
706
+  "shouldCacheIgnoreZoom": false,
702
   "viewBackgroundColor": "#ffffff",
707
   "viewBackgroundColor": "#ffffff",
703
   "zoom": 1,
708
   "zoom": 1,
704
 }
709
 }
873
     "id2": true,
878
     "id2": true,
874
   },
879
   },
875
   "selectionElement": null,
880
   "selectionElement": null,
881
+  "shouldCacheIgnoreZoom": false,
876
   "viewBackgroundColor": "#ffffff",
882
   "viewBackgroundColor": "#ffffff",
877
   "zoom": 1,
883
   "zoom": 1,
878
 }
884
 }
1053
     "id3": true,
1059
     "id3": true,
1054
   },
1060
   },
1055
   "selectionElement": null,
1061
   "selectionElement": null,
1062
+  "shouldCacheIgnoreZoom": false,
1056
   "viewBackgroundColor": "#ffffff",
1063
   "viewBackgroundColor": "#ffffff",
1057
   "zoom": 1,
1064
   "zoom": 1,
1058
 }
1065
 }
1320
   "scrolledOutside": false,
1327
   "scrolledOutside": false,
1321
   "selectedElementIds": Object {},
1328
   "selectedElementIds": Object {},
1322
   "selectionElement": null,
1329
   "selectionElement": null,
1330
+  "shouldCacheIgnoreZoom": false,
1323
   "viewBackgroundColor": "#ffffff",
1331
   "viewBackgroundColor": "#ffffff",
1324
   "zoom": 1,
1332
   "zoom": 1,
1325
 }
1333
 }
1895
     "id0": true,
1903
     "id0": true,
1896
   },
1904
   },
1897
   "selectionElement": null,
1905
   "selectionElement": null,
1906
+  "shouldCacheIgnoreZoom": false,
1898
   "viewBackgroundColor": "#ffffff",
1907
   "viewBackgroundColor": "#ffffff",
1899
   "zoom": 1,
1908
   "zoom": 1,
1900
 }
1909
 }
2000
     "id0": true,
2009
     "id0": true,
2001
   },
2010
   },
2002
   "selectionElement": null,
2011
   "selectionElement": null,
2012
+  "shouldCacheIgnoreZoom": false,
2003
   "viewBackgroundColor": "#ffffff",
2013
   "viewBackgroundColor": "#ffffff",
2004
   "zoom": 1,
2014
   "zoom": 1,
2005
 }
2015
 }
2105
     "id0": true,
2115
     "id0": true,
2106
   },
2116
   },
2107
   "selectionElement": null,
2117
   "selectionElement": null,
2118
+  "shouldCacheIgnoreZoom": false,
2108
   "viewBackgroundColor": "#ffffff",
2119
   "viewBackgroundColor": "#ffffff",
2109
   "zoom": 1,
2120
   "zoom": 1,
2110
 }
2121
 }
2210
     "id0": true,
2221
     "id0": true,
2211
   },
2222
   },
2212
   "selectionElement": null,
2223
   "selectionElement": null,
2224
+  "shouldCacheIgnoreZoom": false,
2213
   "viewBackgroundColor": "#ffffff",
2225
   "viewBackgroundColor": "#ffffff",
2214
   "zoom": 1,
2226
   "zoom": 1,
2215
 }
2227
 }
2337
     "id0": true,
2349
     "id0": true,
2338
   },
2350
   },
2339
   "selectionElement": null,
2351
   "selectionElement": null,
2352
+  "shouldCacheIgnoreZoom": false,
2340
   "viewBackgroundColor": "#ffffff",
2353
   "viewBackgroundColor": "#ffffff",
2341
   "zoom": 1,
2354
   "zoom": 1,
2342
 }
2355
 }
2464
     "id0": true,
2477
     "id0": true,
2465
   },
2478
   },
2466
   "selectionElement": null,
2479
   "selectionElement": null,
2480
+  "shouldCacheIgnoreZoom": false,
2467
   "viewBackgroundColor": "#ffffff",
2481
   "viewBackgroundColor": "#ffffff",
2468
   "zoom": 1,
2482
   "zoom": 1,
2469
 }
2483
 }
2591
     "id0": true,
2605
     "id0": true,
2592
   },
2606
   },
2593
   "selectionElement": null,
2607
   "selectionElement": null,
2608
+  "shouldCacheIgnoreZoom": false,
2594
   "viewBackgroundColor": "#ffffff",
2609
   "viewBackgroundColor": "#ffffff",
2595
   "zoom": 1,
2610
   "zoom": 1,
2596
 }
2611
 }
2696
     "id0": true,
2711
     "id0": true,
2697
   },
2712
   },
2698
   "selectionElement": null,
2713
   "selectionElement": null,
2714
+  "shouldCacheIgnoreZoom": false,
2699
   "viewBackgroundColor": "#ffffff",
2715
   "viewBackgroundColor": "#ffffff",
2700
   "zoom": 1,
2716
   "zoom": 1,
2701
 }
2717
 }
2801
     "id0": true,
2817
     "id0": true,
2802
   },
2818
   },
2803
   "selectionElement": null,
2819
   "selectionElement": null,
2820
+  "shouldCacheIgnoreZoom": false,
2804
   "viewBackgroundColor": "#ffffff",
2821
   "viewBackgroundColor": "#ffffff",
2805
   "zoom": 1,
2822
   "zoom": 1,
2806
 }
2823
 }
2928
     "id0": true,
2945
     "id0": true,
2929
   },
2946
   },
2930
   "selectionElement": null,
2947
   "selectionElement": null,
2948
+  "shouldCacheIgnoreZoom": false,
2931
   "viewBackgroundColor": "#ffffff",
2949
   "viewBackgroundColor": "#ffffff",
2932
   "zoom": 1,
2950
   "zoom": 1,
2933
 }
2951
 }
3033
     "id0": true,
3051
     "id0": true,
3034
   },
3052
   },
3035
   "selectionElement": null,
3053
   "selectionElement": null,
3054
+  "shouldCacheIgnoreZoom": true,
3036
   "viewBackgroundColor": "#ffffff",
3055
   "viewBackgroundColor": "#ffffff",
3037
   "zoom": 1,
3056
   "zoom": 1,
3038
 }
3057
 }
3098
     "id9": true,
3117
     "id9": true,
3099
   },
3118
   },
3100
   "selectionElement": null,
3119
   "selectionElement": null,
3120
+  "shouldCacheIgnoreZoom": false,
3101
   "viewBackgroundColor": "#ffffff",
3121
   "viewBackgroundColor": "#ffffff",
3102
   "zoom": 1,
3122
   "zoom": 1,
3103
 }
3123
 }
3754
     "id7": true,
3774
     "id7": true,
3755
   },
3775
   },
3756
   "selectionElement": null,
3776
   "selectionElement": null,
3777
+  "shouldCacheIgnoreZoom": false,
3757
   "viewBackgroundColor": "#ffffff",
3778
   "viewBackgroundColor": "#ffffff",
3758
   "zoom": 1,
3779
   "zoom": 1,
3759
 }
3780
 }
4102
     "id5": true,
4123
     "id5": true,
4103
   },
4124
   },
4104
   "selectionElement": null,
4125
   "selectionElement": null,
4126
+  "shouldCacheIgnoreZoom": false,
4105
   "viewBackgroundColor": "#ffffff",
4127
   "viewBackgroundColor": "#ffffff",
4106
   "zoom": 1,
4128
   "zoom": 1,
4107
 }
4129
 }
4380
     "id3": true,
4402
     "id3": true,
4381
   },
4403
   },
4382
   "selectionElement": null,
4404
   "selectionElement": null,
4405
+  "shouldCacheIgnoreZoom": false,
4383
   "viewBackgroundColor": "#ffffff",
4406
   "viewBackgroundColor": "#ffffff",
4384
   "zoom": 1,
4407
   "zoom": 1,
4385
 }
4408
 }
4588
     "id1": true,
4611
     "id1": true,
4589
   },
4612
   },
4590
   "selectionElement": null,
4613
   "selectionElement": null,
4614
+  "shouldCacheIgnoreZoom": false,
4591
   "viewBackgroundColor": "#ffffff",
4615
   "viewBackgroundColor": "#ffffff",
4592
   "zoom": 1,
4616
   "zoom": 1,
4593
 }
4617
 }
4742
     "id9": true,
4766
     "id9": true,
4743
   },
4767
   },
4744
   "selectionElement": null,
4768
   "selectionElement": null,
4769
+  "shouldCacheIgnoreZoom": false,
4745
   "viewBackgroundColor": "#ffffff",
4770
   "viewBackgroundColor": "#ffffff",
4746
   "zoom": 1,
4771
   "zoom": 1,
4747
 }
4772
 }
5370
     "id9": true,
5395
     "id9": true,
5371
   },
5396
   },
5372
   "selectionElement": null,
5397
   "selectionElement": null,
5398
+  "shouldCacheIgnoreZoom": false,
5373
   "viewBackgroundColor": "#ffffff",
5399
   "viewBackgroundColor": "#ffffff",
5374
   "zoom": 1,
5400
   "zoom": 1,
5375
 }
5401
 }
5928
     "id9": true,
5954
     "id9": true,
5929
   },
5955
   },
5930
   "selectionElement": null,
5956
   "selectionElement": null,
5957
+  "shouldCacheIgnoreZoom": false,
5931
   "viewBackgroundColor": "#ffffff",
5958
   "viewBackgroundColor": "#ffffff",
5932
   "zoom": 1,
5959
   "zoom": 1,
5933
 }
5960
 }
6416
     "id9": true,
6443
     "id9": true,
6417
   },
6444
   },
6418
   "selectionElement": null,
6445
   "selectionElement": null,
6446
+  "shouldCacheIgnoreZoom": false,
6419
   "viewBackgroundColor": "#ffffff",
6447
   "viewBackgroundColor": "#ffffff",
6420
   "zoom": 1,
6448
   "zoom": 1,
6421
 }
6449
 }
6835
     "id8": true,
6863
     "id8": true,
6836
   },
6864
   },
6837
   "selectionElement": null,
6865
   "selectionElement": null,
6866
+  "shouldCacheIgnoreZoom": false,
6838
   "viewBackgroundColor": "#ffffff",
6867
   "viewBackgroundColor": "#ffffff",
6839
   "zoom": 1,
6868
   "zoom": 1,
6840
 }
6869
 }
7218
     "id6": true,
7247
     "id6": true,
7219
   },
7248
   },
7220
   "selectionElement": null,
7249
   "selectionElement": null,
7250
+  "shouldCacheIgnoreZoom": false,
7221
   "viewBackgroundColor": "#ffffff",
7251
   "viewBackgroundColor": "#ffffff",
7222
   "zoom": 1,
7252
   "zoom": 1,
7223
 }
7253
 }
7531
     "id4": true,
7561
     "id4": true,
7532
   },
7562
   },
7533
   "selectionElement": null,
7563
   "selectionElement": null,
7564
+  "shouldCacheIgnoreZoom": false,
7534
   "viewBackgroundColor": "#ffffff",
7565
   "viewBackgroundColor": "#ffffff",
7535
   "zoom": 1,
7566
   "zoom": 1,
7536
 }
7567
 }
7774
     "id2": true,
7805
     "id2": true,
7775
   },
7806
   },
7776
   "selectionElement": null,
7807
   "selectionElement": null,
7808
+  "shouldCacheIgnoreZoom": false,
7777
   "viewBackgroundColor": "#ffffff",
7809
   "viewBackgroundColor": "#ffffff",
7778
   "zoom": 1,
7810
   "zoom": 1,
7779
 }
7811
 }
7963
     "id9": true,
7995
     "id9": true,
7964
   },
7996
   },
7965
   "selectionElement": null,
7997
   "selectionElement": null,
7998
+  "shouldCacheIgnoreZoom": false,
7966
   "viewBackgroundColor": "#ffffff",
7999
   "viewBackgroundColor": "#ffffff",
7967
   "zoom": 1,
8000
   "zoom": 1,
7968
 }
8001
 }
8626
     "id9": true,
8659
     "id9": true,
8627
   },
8660
   },
8628
   "selectionElement": null,
8661
   "selectionElement": null,
8662
+  "shouldCacheIgnoreZoom": false,
8629
   "viewBackgroundColor": "#ffffff",
8663
   "viewBackgroundColor": "#ffffff",
8630
   "zoom": 1,
8664
   "zoom": 1,
8631
 }
8665
 }
9219
     "id9": true,
9253
     "id9": true,
9220
   },
9254
   },
9221
   "selectionElement": null,
9255
   "selectionElement": null,
9256
+  "shouldCacheIgnoreZoom": false,
9222
   "viewBackgroundColor": "#ffffff",
9257
   "viewBackgroundColor": "#ffffff",
9223
   "zoom": 1,
9258
   "zoom": 1,
9224
 }
9259
 }
9742
     "id9": true,
9777
     "id9": true,
9743
   },
9778
   },
9744
   "selectionElement": null,
9779
   "selectionElement": null,
9780
+  "shouldCacheIgnoreZoom": false,
9745
   "viewBackgroundColor": "#ffffff",
9781
   "viewBackgroundColor": "#ffffff",
9746
   "zoom": 1,
9782
   "zoom": 1,
9747
 }
9783
 }
10191
     "id4": true,
10227
     "id4": true,
10192
   },
10228
   },
10193
   "selectionElement": null,
10229
   "selectionElement": null,
10230
+  "shouldCacheIgnoreZoom": false,
10194
   "viewBackgroundColor": "#ffffff",
10231
   "viewBackgroundColor": "#ffffff",
10195
   "zoom": 1,
10232
   "zoom": 1,
10196
 }
10233
 }
10419
   "scrolledOutside": false,
10456
   "scrolledOutside": false,
10420
   "selectedElementIds": Object {},
10457
   "selectedElementIds": Object {},
10421
   "selectionElement": null,
10458
   "selectionElement": null,
10459
+  "shouldCacheIgnoreZoom": false,
10422
   "viewBackgroundColor": "#ffffff",
10460
   "viewBackgroundColor": "#ffffff",
10423
   "zoom": 1,
10461
   "zoom": 1,
10424
 }
10462
 }
10468
     "id1": true,
10506
     "id1": true,
10469
   },
10507
   },
10470
   "selectionElement": null,
10508
   "selectionElement": null,
10509
+  "shouldCacheIgnoreZoom": true,
10471
   "viewBackgroundColor": "#ffffff",
10510
   "viewBackgroundColor": "#ffffff",
10472
   "zoom": 1,
10511
   "zoom": 1,
10473
 }
10512
 }
10517
     "id2": true,
10556
     "id2": true,
10518
   },
10557
   },
10519
   "selectionElement": null,
10558
   "selectionElement": null,
10559
+  "shouldCacheIgnoreZoom": false,
10520
   "viewBackgroundColor": "#ffffff",
10560
   "viewBackgroundColor": "#ffffff",
10521
   "zoom": 1,
10561
   "zoom": 1,
10522
 }
10562
 }
10785
   "scrolledOutside": false,
10825
   "scrolledOutside": false,
10786
   "selectedElementIds": Object {},
10826
   "selectedElementIds": Object {},
10787
   "selectionElement": null,
10827
   "selectionElement": null,
10828
+  "shouldCacheIgnoreZoom": false,
10788
   "viewBackgroundColor": "#ffffff",
10829
   "viewBackgroundColor": "#ffffff",
10789
   "zoom": 1,
10830
   "zoom": 1,
10790
 }
10831
 }

+ 1
- 0
src/types.ts Zobrazit soubor

42
   lastPointerDownWith: PointerType;
42
   lastPointerDownWith: PointerType;
43
   selectedElementIds: { [id: string]: boolean };
43
   selectedElementIds: { [id: string]: boolean };
44
   collaborators: Map<string, { pointer?: { x: number; y: number } }>;
44
   collaborators: Map<string, { pointer?: { x: number; y: number } }>;
45
+  shouldCacheIgnoreZoom: boolean;
45
 };
46
 };
46
 
47
 
47
 export type PointerCoords = Readonly<{
48
 export type PointerCoords = Readonly<{

Načítá se…
Zrušit
Uložit