@netless/window-manager 1.0.0-canary.7 → 1.0.0-canary.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +30 -6
  2. package/dist/index.js +13625 -0
  3. package/dist/index.mjs +13622 -0
  4. package/dist/index.umd.js +13620 -46
  5. package/dist/{App → src/App}/AppContext.d.ts +16 -14
  6. package/dist/{App → src/App}/AppPageStateImpl.d.ts +0 -0
  7. package/dist/{App → src/App}/AppProxy.d.ts +30 -11
  8. package/dist/{App → src/App}/MagixEvent/index.d.ts +0 -0
  9. package/dist/src/App/WhiteboardView.d.ts +27 -0
  10. package/dist/{App → src/App}/index.d.ts +1 -0
  11. package/dist/src/App/type.d.ts +21 -0
  12. package/dist/{AppListener.d.ts → src/AppListener.d.ts} +2 -2
  13. package/dist/{AppManager.d.ts → src/AppManager.d.ts} +12 -7
  14. package/dist/{AttributesDelegate.d.ts → src/AttributesDelegate.d.ts} +5 -2
  15. package/dist/{BoxEmitter.d.ts → src/BoxEmitter.d.ts} +0 -0
  16. package/dist/{BoxManager.d.ts → src/BoxManager.d.ts} +12 -6
  17. package/dist/{BuiltinApps.d.ts → src/BuiltinApps.d.ts} +3 -0
  18. package/dist/{Cursor → src/Cursor}/Cursor.d.ts +0 -0
  19. package/dist/{Cursor → src/Cursor}/icons.d.ts +0 -0
  20. package/dist/{Cursor → src/Cursor}/index.d.ts +4 -3
  21. package/dist/{Helper.d.ts → src/Helper.d.ts} +4 -8
  22. package/dist/{InternalEmitter.d.ts → src/InternalEmitter.d.ts} +1 -4
  23. package/dist/{Page → src/Page}/PageController.d.ts +3 -1
  24. package/dist/{Page → src/Page}/index.d.ts +0 -0
  25. package/dist/{PageState.d.ts → src/PageState.d.ts} +1 -0
  26. package/dist/{ReconnectRefresher.d.ts → src/ReconnectRefresher.d.ts} +0 -0
  27. package/dist/{RedoUndo.d.ts → src/RedoUndo.d.ts} +0 -0
  28. package/dist/{Register → src/Register}/index.d.ts +4 -2
  29. package/dist/{Register → src/Register}/loader.d.ts +1 -1
  30. package/dist/src/Register/storage.d.ts +11 -0
  31. package/dist/{Utils → src/Utils}/AppCreateQueue.d.ts +0 -0
  32. package/dist/{Utils → src/Utils}/Common.d.ts +0 -0
  33. package/dist/{Utils → src/Utils}/Reactive.d.ts +1 -1
  34. package/dist/{Utils → src/Utils}/RoomHacker.d.ts +0 -0
  35. package/dist/{Utils → src/Utils}/error.d.ts +4 -1
  36. package/dist/{Utils → src/Utils}/log.d.ts +0 -0
  37. package/dist/src/View/CameraSynchronizer.d.ts +21 -0
  38. package/dist/{View → src/View}/MainView.d.ts +25 -7
  39. package/dist/src/View/ScrollMode.d.ts +32 -0
  40. package/dist/{View → src/View}/ViewManager.d.ts +0 -0
  41. package/dist/src/View/ViewSync.d.ts +32 -0
  42. package/dist/{callback.d.ts → src/callback.d.ts} +12 -1
  43. package/dist/{constants.d.ts → src/constants.d.ts} +12 -5
  44. package/dist/src/image.d.ts +19 -0
  45. package/dist/{index.d.ts → src/index.d.ts} +63 -17
  46. package/dist/src/shim.d.ts +11 -0
  47. package/dist/src/storage.d.ts +7 -0
  48. package/dist/{typings.d.ts → src/typings.d.ts} +21 -8
  49. package/dist/style.css +810 -1
  50. package/docs/api.md +10 -0
  51. package/docs/app-context.md +155 -27
  52. package/docs/mirgrate-to-1.0.md +68 -0
  53. package/package.json +27 -22
  54. package/playwright.config.ts +29 -0
  55. package/src/App/AppContext.ts +81 -46
  56. package/src/App/AppPageStateImpl.ts +3 -0
  57. package/src/App/AppProxy.ts +249 -141
  58. package/src/App/WhiteboardView.ts +37 -14
  59. package/src/App/index.ts +1 -0
  60. package/src/App/type.ts +22 -0
  61. package/src/AppListener.ts +27 -21
  62. package/src/AppManager.ts +96 -50
  63. package/src/AttributesDelegate.ts +6 -3
  64. package/src/BoxManager.ts +76 -38
  65. package/src/BuiltinApps.ts +9 -8
  66. package/src/Cursor/Cursor.svelte +6 -2
  67. package/src/Cursor/Cursor.ts +15 -4
  68. package/src/Cursor/icons.ts +6 -0
  69. package/src/Cursor/index.ts +16 -11
  70. package/src/Helper.ts +25 -7
  71. package/src/InternalEmitter.ts +1 -4
  72. package/src/Page/PageController.ts +3 -1
  73. package/src/PageState.ts +8 -1
  74. package/src/ReconnectRefresher.ts +7 -3
  75. package/src/Register/index.ts +36 -14
  76. package/src/Register/loader.ts +20 -9
  77. package/src/Register/storage.ts +26 -5
  78. package/src/Utils/Common.ts +3 -0
  79. package/src/Utils/Reactive.ts +29 -27
  80. package/src/Utils/RoomHacker.ts +3 -0
  81. package/src/Utils/error.ts +6 -2
  82. package/src/View/CameraSynchronizer.ts +55 -36
  83. package/src/View/MainView.ts +163 -77
  84. package/src/View/ScrollMode.ts +240 -0
  85. package/src/View/ViewSync.ts +138 -6
  86. package/src/callback.ts +8 -1
  87. package/src/constants.ts +11 -3
  88. package/src/image/pencil-eraser-1.svg +3 -0
  89. package/src/image/pencil-eraser-2.svg +3 -0
  90. package/src/image/pencil-eraser-3.svg +3 -0
  91. package/src/index.ts +197 -60
  92. package/src/storage.ts +15 -0
  93. package/src/style.css +18 -47
  94. package/src/typings.ts +24 -7
  95. package/vite.config.js +12 -7
  96. package/dist/App/AppViewSync.d.ts +0 -11
  97. package/dist/App/Storage/StorageEvent.d.ts +0 -8
  98. package/dist/App/Storage/index.d.ts +0 -39
  99. package/dist/App/Storage/typings.d.ts +0 -22
  100. package/dist/App/Storage/utils.d.ts +0 -5
  101. package/dist/App/WhiteboardView.d.ts +0 -21
  102. package/dist/Register/storage.d.ts +0 -8
  103. package/dist/View/CameraSynchronizer.d.ts +0 -17
  104. package/dist/View/ViewSync.d.ts +0 -7
  105. package/dist/index.cjs.js +0 -46
  106. package/dist/index.es.js +0 -16161
  107. package/pnpm-lock.yaml +0 -6302
  108. package/src/App/AppViewSync.ts +0 -68
  109. package/src/App/Storage/StorageEvent.ts +0 -21
  110. package/src/App/Storage/index.ts +0 -295
  111. package/src/App/Storage/typings.ts +0 -23
  112. package/src/App/Storage/utils.ts +0 -17
@@ -1,6 +1,7 @@
1
1
  import Emittery from "emittery";
2
2
  import { loadApp } from "./loader";
3
3
  import type { NetlessApp, RegisterEvents, RegisterParams } from "../typings";
4
+ import { removeItem } from "./storage";
4
5
 
5
6
  export type LoadAppEvent = {
6
7
  kind: string;
@@ -11,11 +12,12 @@ export type LoadAppEvent = {
11
12
  export type SyncRegisterAppPayload = { kind: string, src: string, name: string | undefined };
12
13
  export type SyncRegisterApp = (payload: SyncRegisterAppPayload) => void;
13
14
 
14
- class AppRegister {
15
+ export class AppRegister {
15
16
  public kindEmitters: Map<string, Emittery<RegisterEvents>> = new Map();
16
17
  public registered: Map<string, RegisterParams> = new Map();
17
18
  public appClassesCache: Map<string, Promise<NetlessApp>> = new Map();
18
19
  public appClasses: Map<string, () => Promise<NetlessApp>> = new Map();
20
+ public downloaded: Map<string, string> = new Map();
19
21
 
20
22
  private syncRegisterApp: SyncRegisterApp | null = null;
21
23
 
@@ -31,32 +33,39 @@ class AppRegister {
31
33
  this.appClassesCache.delete(params.kind);
32
34
  this.registered.set(params.kind, params);
33
35
 
34
- const srcOrAppOrFunction = params.src;
36
+ const paramSrc = params.src;
35
37
  let downloadApp: () => Promise<NetlessApp>;
36
38
 
37
- if (typeof srcOrAppOrFunction === "string") {
39
+ if (typeof paramSrc === "string") {
38
40
  downloadApp = async () => {
39
- let appClass = (await loadApp(srcOrAppOrFunction, params.kind, params.name)) as any;
41
+ const result = await loadApp(paramSrc, params.kind, params.name) as any;
42
+ if (result.__esModule) {
43
+ return result.default;
44
+ }
45
+ return result;
46
+ };
47
+ if (this.syncRegisterApp) {
48
+ this.syncRegisterApp({ kind: params.kind, src: paramSrc, name: params.name });
49
+ }
50
+ }
51
+ if (typeof paramSrc === "function") {
52
+ downloadApp = async () => {
53
+ let appClass = await paramSrc() as any;
40
54
  if (appClass) {
41
- if (appClass.__esModule) {
55
+ if (appClass.__esModule || appClass.default) {
42
56
  appClass = appClass.default;
43
57
  }
44
58
  return appClass;
45
59
  } else {
46
60
  throw new Error(
47
- `[WindowManager]: load remote script failed, ${srcOrAppOrFunction}`
61
+ `[WindowManager]: load remote script failed, ${paramSrc}`
48
62
  );
49
63
  }
50
64
  };
51
- if (this.syncRegisterApp) {
52
- this.syncRegisterApp({ kind: params.kind, src: srcOrAppOrFunction, name: params.name });
53
- }
54
- } else if (typeof srcOrAppOrFunction === "function") {
55
- downloadApp = srcOrAppOrFunction;
56
- } else {
57
- downloadApp = async () => srcOrAppOrFunction;
58
65
  }
59
-
66
+ if (typeof paramSrc === "object") {
67
+ downloadApp = async () => paramSrc;
68
+ }
60
69
  this.appClasses.set(params.kind, async () => {
61
70
  let app = this.appClassesCache.get(params.kind);
62
71
  if (!app) {
@@ -74,10 +83,23 @@ class AppRegister {
74
83
  }
75
84
  }
76
85
 
86
+ public downloadApp(kind: string) {
87
+ const src = this.registered.get(kind);
88
+ if (src && typeof src.src === "string") {
89
+ return loadApp(src.src, src.kind, src.name)
90
+ }
91
+ }
92
+
93
+ public async removeDownloaded(kind: string) {
94
+ await removeItem(kind);
95
+ this.downloaded.delete(kind);
96
+ }
97
+
77
98
  public unregister(kind: string) {
78
99
  this.appClasses.delete(kind);
79
100
  this.appClassesCache.delete(kind);
80
101
  this.registered.delete(kind);
102
+ this.removeDownloaded(kind);
81
103
  const kindEmitter = this.kindEmitters.get(kind);
82
104
  if (kindEmitter) {
83
105
  kindEmitter.clearListeners();
@@ -1,19 +1,20 @@
1
1
  import { callbacks } from "../callback";
2
2
  import { getItem, setItem } from "./storage";
3
3
  import type { NetlessApp } from "../typings";
4
+ import { appRegister } from ".";
4
5
 
5
6
  const Prefix = "NetlessApp";
6
7
 
7
8
  const TIMEOUT = 10000; // 下载 script 10 秒超时
8
9
 
9
- export const getScript = async (url: string): Promise<string> => {
10
- const item = await getItem(url);
10
+ export const getScript = async (kind: string, url: string): Promise<string> => {
11
+ const item = await getItem(kind);
11
12
  if (item) {
12
13
  return item.sourceCode;
13
14
  } else {
14
15
  const result = await fetchWithTimeout(url, { timeout: TIMEOUT });
15
16
  const text = await result.text();
16
- await setItem(url, text);
17
+ await setItem(kind, url, text);
17
18
  return text;
18
19
  }
19
20
  };
@@ -28,6 +29,15 @@ export const executeScript = (text: string, appName: string): NetlessApp => {
28
29
  return result;
29
30
  };
30
31
 
32
+ const emitSuccess = (kind: string, url: string) => {
33
+ callbacks.emit("loadApp", { kind, status: "success" });
34
+ appRegister.downloaded.set(kind, url);
35
+ };
36
+
37
+ const emitFailed = (kind: string, reason: string) => {
38
+ callbacks.emit("loadApp", { kind, status: "failed", reason, });
39
+ };
40
+
31
41
  export const loadApp = async (
32
42
  url: string,
33
43
  key: string,
@@ -36,14 +46,14 @@ export const loadApp = async (
36
46
  const appName = name || Prefix + key;
37
47
  callbacks.emit("loadApp", { kind: key, status: "start" });
38
48
  try {
39
- const text = await getScript(url);
49
+ const text = await getScript(key, url);
40
50
  if (!text || text.length === 0) {
41
- callbacks.emit("loadApp", { kind: key, status: "failed", reason: "script is empty." });
51
+ emitFailed(key, "script is empty");
42
52
  return;
43
53
  }
44
54
  try {
45
55
  const result = executeScript(text, appName);
46
- callbacks.emit("loadApp", { kind: key, status: "success" });
56
+ emitSuccess(key, url);
47
57
  return result;
48
58
  } catch (error: any) {
49
59
  if (error.message.includes("Can only have one anonymous define call per script file")) {
@@ -54,16 +64,17 @@ export const loadApp = async (
54
64
  delete define.amd;
55
65
  }
56
66
  const result = executeScript(text, appName);
57
- callbacks.emit("loadApp", { kind: key, status: "success" });
67
+ emitSuccess(key, url);
58
68
  return result;
59
69
  }
60
- callbacks.emit("loadApp", { kind: key, status: "failed", reason: error.message });
70
+ emitFailed(key, error.message);
61
71
  }
62
72
  } catch (error: any) {
63
- callbacks.emit("loadApp", { kind: key, status: "failed", reason: error.message });
73
+ emitFailed(key, error.message);
64
74
  }
65
75
  };
66
76
 
77
+
67
78
  async function fetchWithTimeout(resource: string, options: RequestInit & { timeout: number }) {
68
79
  const { timeout = 10000 } = options;
69
80
 
@@ -1,3 +1,5 @@
1
+ import type { AppRegister } from "./index";
2
+
1
3
  const DatabaseName = "__WindowManagerAppCache";
2
4
 
3
5
  let db: IDBDatabase;
@@ -5,21 +7,26 @@ let store: IDBObjectStore;
5
7
 
6
8
  export type Item = {
7
9
  kind: string;
10
+ url: string;
8
11
  sourceCode: string;
9
12
  }
10
13
 
11
- export const initDb = async () => {
14
+ export const initDb = async (appRegister: AppRegister) => {
12
15
  db = await createDb();
16
+ const items = await queryAll(db);
17
+ items.forEach(item => {
18
+ appRegister.downloaded.set(item.kind, item.url);
19
+ });
13
20
  }
14
21
 
15
- export const setItem = (key: string, val: any) => {
22
+ export const setItem = (kind: string, url: string, val: any) => {
16
23
  if (!db) return;
17
- return addRecord(db, { kind: key, sourceCode: val })
24
+ return addRecord(db, { kind, url, sourceCode: val })
18
25
  };
19
26
 
20
- export const getItem = async (key: string): Promise<Item | null> => {
27
+ export const getItem = async (kind: string): Promise<Item | null> => {
21
28
  if (!db) return null;
22
- return await query(db, key);
29
+ return await query(db, kind);
23
30
  };
24
31
 
25
32
  export const removeItem = (key: string) => {
@@ -27,6 +34,11 @@ export const removeItem = (key: string) => {
27
34
  return deleteRecord(db, key);
28
35
  };
29
36
 
37
+ export const getAll = () => {
38
+ if (!db) return;
39
+ return queryAll(db);
40
+ }
41
+
30
42
  function createDb(): Promise<IDBDatabase> {
31
43
  return new Promise((resolve, reject) => {
32
44
  const request = indexedDB.open(DatabaseName, 2);
@@ -64,6 +76,15 @@ function query<T>(db: IDBDatabase, val: string): Promise<T | null> {
64
76
  })
65
77
  }
66
78
 
79
+ function queryAll(db: IDBDatabase): Promise<Item[]> {
80
+ return new Promise((resolve, reject) => {
81
+ const index = db.transaction(["apps"]).objectStore("apps").index("kind");
82
+ const request = index.getAll();
83
+ request.onerror = e => reject(e);
84
+ request.onsuccess = () => resolve(request.result);
85
+ });
86
+ }
87
+
67
88
  function addRecord(db: IDBDatabase, payload: any): Promise<void> {
68
89
  return new Promise((resolve, reject) => {
69
90
  const request = db.transaction(["apps"], "readwrite").objectStore("apps").add(payload);
@@ -17,6 +17,9 @@ export const genAppId = async (kind: string) => {
17
17
  };
18
18
 
19
19
  export const setViewFocusScenePath = (view: View, focusScenePath: string) => {
20
+ if ((view as any).didRelease) {
21
+ return;
22
+ }
20
23
  if (view.focusScenePath !== focusScenePath) {
21
24
  view.focusScenePath = focusScenePath;
22
25
  return view;
@@ -1,6 +1,6 @@
1
- import { listenUpdated, unlistenUpdated, reaction, UpdateEventKind } from "white-web-sdk";
2
- import type { AkkoObjectUpdatedProperty , AkkoObjectUpdatedListener } from "white-web-sdk";
3
1
  import { isObject } from "lodash";
2
+ import { listenUpdated, reaction, unlistenUpdated, UpdateEventKind } from "white-web-sdk";
3
+ import type { AkkoObjectUpdatedProperty, AkkoObjectUpdatedListener } from "white-web-sdk";
4
4
 
5
5
  // 兼容 13 和 14 版本 SDK
6
6
  export const onObjectByEvent = (event: UpdateEventKind) => {
@@ -12,7 +12,7 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
12
12
  if (kinds.includes(event)) {
13
13
  func();
14
14
  }
15
- }
15
+ };
16
16
  listenUpdated(object, listener);
17
17
  func();
18
18
  return () => unlistenUpdated(object, listener);
@@ -21,43 +21,45 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
21
21
  () => object,
22
22
  () => {
23
23
  func();
24
- }, {
24
+ },
25
+ {
25
26
  fireImmediately: true,
26
27
  }
27
- )
28
+ );
28
29
  }
29
- }
30
- }
30
+ };
31
+ };
31
32
 
32
- export const safeListenPropsUpdated = <T>(
33
+ // eslint-disable-next-line @typescript-eslint/ban-types
34
+ export const safeListenPropsUpdated = <T extends Object>(
33
35
  getProps: () => T,
34
36
  callback: AkkoObjectUpdatedListener<T>,
35
37
  onDestroyed?: (props: unknown) => void
36
- ) => {
38
+ ) => {
37
39
  let disposeListenUpdated: (() => void) | null = null;
38
40
  const disposeReaction = reaction(
39
- getProps,
40
- () => {
41
- if (disposeListenUpdated) {
42
- disposeListenUpdated();
43
- disposeListenUpdated = null;
44
- }
45
- const props = getProps();
46
- if (isObject(props)) {
47
- disposeListenUpdated = () => unlistenUpdated(props, callback);
48
- listenUpdated(props, callback);
49
- } else {
50
- onDestroyed?.(props);
51
- }
52
- },
53
- { fireImmediately: true }
41
+ getProps,
42
+ () => {
43
+ if (disposeListenUpdated) {
44
+ disposeListenUpdated();
45
+ disposeListenUpdated = null;
46
+ }
47
+ const props = getProps();
48
+ if (isObject(props)) {
49
+ disposeListenUpdated = () => unlistenUpdated(props, callback);
50
+ listenUpdated(props, callback);
51
+ } else {
52
+ onDestroyed?.(props);
53
+ }
54
+ },
55
+ { fireImmediately: true }
54
56
  );
55
57
 
56
58
  return () => {
57
- disposeListenUpdated?.();
58
- disposeReaction();
59
+ disposeListenUpdated?.();
60
+ disposeReaction();
59
61
  };
60
- }
62
+ };
61
63
 
62
64
  export const onObjectRemoved = onObjectByEvent(UpdateEventKind.Removed);
63
65
  export const onObjectInserted = onObjectByEvent(UpdateEventKind.Inserted);
@@ -56,6 +56,9 @@ export const replaceRoomFunction = (room: Room | Player, manager: WindowManager)
56
56
  room.lockImages = (...args) => manager.lockImages(...args);
57
57
 
58
58
  delegateRemoveScenes(room, manager);
59
+ if (!(room as any).dynamicPpt.slideStateAdapter.pptHandler) {
60
+ (room as any).dynamicPpt.slideStateAdapter.pptHandler = manager.createPPTHandler();
61
+ }
59
62
  }
60
63
  };
61
64
 
@@ -31,10 +31,14 @@ export class InvalidScenePath extends Error {
31
31
  override message = `[WindowManager]: ScenePath should start with "/"`;
32
32
  }
33
33
 
34
- export class BoxManagerNotFoundError extends Error {
35
- override message = "[WindowManager]: boxManager not found";
34
+ export class BoxManagerNotInitializeError extends Error {
35
+ override message = "[WindowManager]: boxManager need initialize";
36
36
  }
37
37
 
38
38
  export class BindContainerRoomPhaseInvalidError extends Error {
39
39
  override message = "[WindowManager]: room phase only Connected can be bindContainer";
40
40
  }
41
+
42
+ export class InvalidViewModeError extends Error {
43
+ override message = "[WindowManager]: scroll mode cannot be switched to other viewMode";
44
+ }
@@ -1,22 +1,24 @@
1
1
  import { AnimationMode } from "white-web-sdk";
2
- import { delay, throttle } from "lodash";
2
+ import { isEmpty, isEqual, pick, throttle } from "lodash";
3
3
  import type { TeleBoxRect } from "@netless/telebox-insider";
4
- import type { Camera, View, Size } from "white-web-sdk";
5
- import type { ISize } from "../AttributesDelegate";
4
+ import type { View, Size } from "white-web-sdk";
5
+ import type { ICamera, ISize } from "../AttributesDelegate";
6
6
 
7
- export type SaveCamera = (camera: Camera) => void;
7
+ export type SaveCamera = (camera: ICamera) => void;
8
8
 
9
9
  export class CameraSynchronizer {
10
- public remoteCamera?: Camera;
10
+ public remoteCamera?: ICamera;
11
11
  public remoteSize?: ISize;
12
12
  protected rect?: TeleBoxRect;
13
13
  protected view?: View;
14
+ protected scale = 1;
15
+ protected cameraUpdating = false;
14
16
 
15
17
  constructor(protected saveCamera: SaveCamera) {}
16
18
 
17
- public setRect(rect: TeleBoxRect) {
19
+ public setRect = (rect: TeleBoxRect, updateCamera = true) => {
18
20
  this.rect = rect;
19
- if (this.remoteCamera && this.remoteSize) {
21
+ if (this.remoteCamera && this.remoteSize && updateCamera) {
20
22
  this.onRemoteUpdate(this.remoteCamera, this.remoteSize);
21
23
  }
22
24
  }
@@ -26,48 +28,65 @@ export class CameraSynchronizer {
26
28
  }
27
29
 
28
30
  // 远端 Camera 或者 size 更新
29
- public onRemoteUpdate = throttle((camera: Camera, size: ISize) => {
31
+ public onRemoteUpdate = throttle((camera: ICamera, size: ISize, skipUpdate = false) => {
30
32
  this.remoteCamera = camera;
31
33
  this.remoteSize = size;
32
- if (this.remoteSize && this.rect) {
33
- let scale: number;
34
- if (size.width < size.height) {
35
- scale = this.rect.width / size.width;
36
- } else {
37
- scale = this.rect.height / size.height;
34
+ if (skipUpdate) return;;
35
+ requestAnimationFrame(() => {
36
+ if (this.remoteSize && this.rect) {
37
+ this.moveCameraToContian(this.remoteSize);
38
+ this.moveCamera(camera);
38
39
  }
39
- const nextScale = camera.scale * scale;
40
- const moveCamera = () => this.view?.moveCamera({
41
- centerX: camera.centerX,
42
- centerY: camera.centerY,
43
- scale: nextScale,
40
+ });
41
+ }, 32);
42
+
43
+ public onRemoteSizeUpdate(size: ISize) {
44
+ this.remoteSize = size;
45
+ const needMoveCamera = !isEqual(pick(this.rect, ["width", "height"]), pick(size, ["width", "height"]));
46
+ if (this.rect && this.remoteCamera && needMoveCamera) {
47
+ if (!this.view) return;
48
+ const currentCamera = this.view.camera;
49
+ this.view?.moveCameraToContain({
50
+ width: size.width,
51
+ height: size.height,
52
+ originX: currentCamera.centerX - (size.width / 2),
53
+ originY: currentCamera.centerY - (size.height / 2),
44
54
  animationMode: AnimationMode.Immediately,
45
55
  });
46
- // TODO 直接调用 moveCamera 依然会出现 camera 错误的情况,这里暂时加一个 delay 保证 camera 是对的, 后续需要 SDK 进行修改
47
- delay(moveCamera, 50);
56
+ this.moveCamera(this.remoteCamera);
48
57
  }
49
- }, 50);
50
-
58
+ }
51
59
 
52
- public onLocalCameraUpdate(camera: Camera) {
60
+ public onLocalCameraUpdate(camera: ICamera) {
53
61
  this.saveCamera(camera);
54
62
  this.remoteCamera = camera;
55
63
  }
56
64
 
57
- // 本地 Size 更新, 先匹配 camera 到新的 size 然后再发送 camera 数据到远端
58
- public onLocalSizeUpdate = (size: Size) => {
59
- if (this.rect && this.view) {
60
- let scale: number;
61
- if (size.width < size.height) {
62
- scale = this.rect.width / size.width;
63
- } else {
64
- scale = this.rect.height / size.height;
65
- }
66
- const nextScale = this.view.camera.scale / scale;
65
+ private moveCameraToContian(size: Size): void {
66
+ if (!isEmpty(size) && this.view) {
67
+ this.view.moveCameraToContain({
68
+ width: size.width,
69
+ height: size.height,
70
+ originX: -size.width / 2,
71
+ originY: -size.height / 2,
72
+ animationMode: AnimationMode.Immediately,
73
+ });
74
+ this.scale = this.view.camera.scale;
75
+ }
76
+ }
77
+
78
+ private moveCamera(camera: ICamera): void {
79
+ if (!isEmpty(camera) && this.view && camera.centerX != null && camera.centerY != null) {
80
+ if (isEqual(camera, this.view.camera)) return;
81
+ const { centerX, centerY, scale } = camera;
82
+ const needScale = scale * (this.scale || 1);
67
83
  this.view.moveCamera({
68
- scale: nextScale,
69
- animationMode: AnimationMode.Immediately
84
+ centerX: centerX,
85
+ centerY: centerY,
86
+ scale: needScale,
87
+ animationMode: AnimationMode.Immediately,
70
88
  });
71
89
  }
72
90
  }
73
91
  }
92
+