@netless/window-manager 1.0.0-canary.52 → 1.0.0-canary.54

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 (44) hide show
  1. package/dist/index.cjs.js +389 -445
  2. package/dist/index.es.js +434 -490
  3. package/dist/index.umd.js +389 -446
  4. package/dist/src/App/AppContext.d.ts +8 -8
  5. package/dist/src/AppManager.d.ts +5 -1
  6. package/dist/src/Cursor/index.d.ts +1 -0
  7. package/dist/src/Utils/Reactive.d.ts +1 -1
  8. package/dist/src/View/CameraSynchronizer.d.ts +4 -3
  9. package/dist/src/View/ScrollMode.d.ts +32 -0
  10. package/dist/src/View/ViewSync.d.ts +4 -2
  11. package/dist/src/callback.d.ts +3 -0
  12. package/dist/src/constants.d.ts +2 -0
  13. package/dist/src/index.d.ts +21 -11
  14. package/dist/src/storage.d.ts +7 -0
  15. package/dist/src/typings.d.ts +5 -4
  16. package/dist/style.css +2 -1
  17. package/docs/api.md +10 -0
  18. package/package.json +4 -3
  19. package/pnpm-lock.yaml +28 -73
  20. package/src/App/AppContext.ts +19 -8
  21. package/src/App/WhiteboardView.ts +4 -2
  22. package/src/AppListener.ts +1 -10
  23. package/src/AppManager.ts +19 -1
  24. package/src/Cursor/index.ts +6 -2
  25. package/src/Utils/Reactive.ts +2 -1
  26. package/src/View/CameraSynchronizer.ts +35 -23
  27. package/src/View/MainView.ts +1 -0
  28. package/src/View/ScrollMode.ts +229 -0
  29. package/src/View/ViewSync.ts +31 -18
  30. package/src/callback.ts +3 -0
  31. package/src/constants.ts +3 -0
  32. package/src/index.ts +56 -63
  33. package/src/storage.ts +15 -0
  34. package/src/style.css +1 -1
  35. package/src/typings.ts +6 -3
  36. package/vite.config.js +1 -1
  37. package/dist/src/App/Storage/StorageEvent.d.ts +0 -8
  38. package/dist/src/App/Storage/index.d.ts +0 -39
  39. package/dist/src/App/Storage/typings.d.ts +0 -22
  40. package/dist/src/App/Storage/utils.d.ts +0 -5
  41. package/src/App/Storage/StorageEvent.ts +0 -21
  42. package/src/App/Storage/index.ts +0 -295
  43. package/src/App/Storage/typings.ts +0 -23
  44. package/src/App/Storage/utils.ts +0 -17
package/src/AppManager.ts CHANGED
@@ -40,7 +40,7 @@ import type {
40
40
  ScenesCallbacksNode,
41
41
  SceneState,
42
42
  RoomState,
43
- } from "white-web-sdk";
43
+ Size} from "white-web-sdk";
44
44
  import type { AddAppParams, BaseInsertParams, TeleBoxRect } from "./index";
45
45
  import type {
46
46
  BoxClosePayload,
@@ -50,6 +50,7 @@ import type {
50
50
  BoxStateChangePayload,
51
51
  } from "./BoxEmitter";
52
52
  import type { Member } from "./Helper";
53
+ import { ScrollMode } from "./View/ScrollMode";
53
54
 
54
55
  export class AppManager {
55
56
  public displayer: Displayer;
@@ -64,12 +65,15 @@ export class AppManager {
64
65
 
65
66
  private appListeners: AppListeners;
66
67
  public boxManager?: BoxManager;
68
+ public scrollMode?: ScrollMode;
69
+ public scrollBaseSize$ = new Val<Size | null>(null);
67
70
 
68
71
  private callbacksNode: ScenesCallbacksNode | null = null;
69
72
  private appCreateQueue = new AppCreateQueue();
70
73
  private sceneIndex$ = new Val<number | undefined>(undefined);
71
74
  private focused$ = new Val<string | undefined>(undefined);
72
75
  public members$ = new Val<Member[]>([]);
76
+ public isWritable$ = new Val<boolean>(Boolean(this.room?.isWritable));
73
77
 
74
78
  private sideEffectManager = new SideEffectManager();
75
79
 
@@ -77,6 +81,7 @@ export class AppManager {
77
81
 
78
82
  public rootDirRemoving = false;
79
83
 
84
+
80
85
  constructor(public windowManger: WindowManager) {
81
86
  this.displayer = windowManger.displayer;
82
87
  this.store.setContext({
@@ -123,6 +128,17 @@ export class AppManager {
123
128
  this.safeUpdateAttributes([Fields.Registered, payload.kind], payload);
124
129
  });
125
130
  this.members$.setValue(serializeRoomMembers(this.displayer.state.roomMembers));
131
+
132
+ emitter.on("mainViewMounted", () => {
133
+ this.windowManger.viewMode$.subscribe(viewMode => {
134
+ const playground = this.windowManger.playground$.value;
135
+ if (viewMode === "scroll" && playground) {
136
+ const scrollMode = new ScrollMode(this);
137
+ this.scrollMode = scrollMode;
138
+ scrollMode.setRoot(playground);
139
+ }
140
+ });
141
+ });
126
142
  }
127
143
 
128
144
  private onRemoveScenes = async (params: RemoveSceneParams) => {
@@ -581,6 +597,7 @@ export class AppManager {
581
597
  public bindMainView(divElement: HTMLDivElement, disableCameraTransform: boolean) {
582
598
  const mainView = this.mainViewProxy.view;
583
599
  mainView.disableCameraTransform = disableCameraTransform;
600
+ console.log("bindMainView", mainView.disableCameraTransform);
584
601
  // 延迟挂载 mainView 的 dom, 避免因为同步 camera 的闪动
585
602
  wait(30).then(() => {
586
603
  mainView.divElement = divElement;
@@ -713,6 +730,7 @@ export class AppManager {
713
730
  }
714
731
  }
715
732
  emitter.emit("writableChange", isWritable);
733
+ this.isWritable$.setValue(isWritable);
716
734
  };
717
735
 
718
736
  public safeSetAttributes(attributes: any) {
@@ -32,9 +32,13 @@ export class CursorManager {
32
32
  private store = this.manager.store;
33
33
  public applianceIcons: ApplianceIcons = ApplianceMap;
34
34
 
35
+ public get playground$() {
36
+ return this.manager.windowManger.playground$;
37
+ }
38
+
35
39
  constructor(private manager: AppManager, private enableCursor: boolean, applianceIcons?: ApplianceIcons) {
36
40
  this.roomMembers = this.manager.room?.state.roomMembers;
37
- const playground = WindowManager.playground;
41
+ const playground = this.playground$.value;
38
42
  if (playground) {
39
43
  this.setupWrapper(playground);
40
44
  }
@@ -65,7 +69,7 @@ export class CursorManager {
65
69
  private initCursorInstance = (uid: string) => {
66
70
  let cursorInstance = this.cursorInstances.get(uid);
67
71
  if (!cursorInstance) {
68
- cursorInstance = new Cursor(this.manager, uid, this, WindowManager.playground);
72
+ cursorInstance = new Cursor(this.manager, uid, this, this.playground$.value);
69
73
  this.cursorInstances.set(uid, cursorInstance);
70
74
  }
71
75
  return cursorInstance;
@@ -30,7 +30,8 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
30
30
  };
31
31
  };
32
32
 
33
- export const safeListenPropsUpdated = <T>(
33
+ // eslint-disable-next-line @typescript-eslint/ban-types
34
+ export const safeListenPropsUpdated = <T extends Object>(
34
35
  getProps: () => T,
35
36
  callback: AkkoObjectUpdatedListener<T>,
36
37
  onDestroyed?: (props: unknown) => void
@@ -1,7 +1,7 @@
1
1
  import { AnimationMode } from "white-web-sdk";
2
- import { isEqual, pick, 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";
4
+ import type { View, Size } from "white-web-sdk";
5
5
  import type { ICamera, ISize } from "../AttributesDelegate";
6
6
 
7
7
  export type SaveCamera = (camera: ICamera) => void;
@@ -11,12 +11,13 @@ export class CameraSynchronizer {
11
11
  public remoteSize?: ISize;
12
12
  protected rect?: TeleBoxRect;
13
13
  protected view?: View;
14
+ protected scale = 1;
14
15
 
15
16
  constructor(protected saveCamera: SaveCamera) {}
16
17
 
17
- public setRect = (rect: TeleBoxRect) => {
18
+ public setRect = (rect: TeleBoxRect, updateCamera = true) => {
18
19
  this.rect = rect;
19
- if (this.remoteCamera && this.remoteSize) {
20
+ if (this.remoteCamera && this.remoteSize && updateCamera) {
20
21
  this.onRemoteUpdate(this.remoteCamera, this.remoteSize);
21
22
  }
22
23
  }
@@ -30,19 +31,12 @@ export class CameraSynchronizer {
30
31
  this.remoteCamera = camera;
31
32
  this.remoteSize = size;
32
33
  if (this.remoteSize && this.rect) {
33
- const nextScale = camera.scale * computedMinScale(size, this.rect);
34
- const config: Partial<Camera> = {
35
- scale: nextScale,
36
- }
37
- if (camera.centerX !== null) {
38
- config.centerX = camera.centerX;
39
- }
40
- if (camera.centerY !== null) {
41
- config.centerY = camera.centerY;
42
- }
43
- this.moveCamera(config);
34
+ requestAnimationFrame(() => {
35
+ this.moveCameraToContian(size);
36
+ this.moveCamera(camera);
37
+ });
44
38
  }
45
- }, 10);
39
+ }, 32);
46
40
 
47
41
  public onRemoteSizeUpdate(size: ISize) {
48
42
  this.remoteSize = size;
@@ -64,13 +58,31 @@ export class CameraSynchronizer {
64
58
  this.remoteCamera = camera;
65
59
  }
66
60
 
67
- private moveCamera(camera: Partial<Camera>) {
68
- this.view?.moveCamera({ ...camera, animationMode: AnimationMode.Immediately });
61
+ private moveCameraToContian(size: Size): void {
62
+ if (!isEmpty(size) && this.view) {
63
+ this.view.moveCameraToContain({
64
+ width: size.width,
65
+ height: size.height,
66
+ originX: -size.width / 2,
67
+ originY: -size.height / 2,
68
+ animationMode: AnimationMode.Immediately,
69
+ });
70
+ this.scale = this.view.camera.scale;
71
+ }
72
+ }
73
+
74
+ private moveCamera(camera: ICamera): void {
75
+ if (!isEmpty(camera) && this.view && camera.centerX && camera.centerY) {
76
+ if (isEqual(camera, this.view.camera)) return;
77
+ const { centerX, centerY, scale } = camera;
78
+ const needScale = scale * (this.scale || 1);
79
+ this.view.moveCamera({
80
+ centerX: centerX,
81
+ centerY: centerY,
82
+ scale: needScale,
83
+ animationMode: AnimationMode.Immediately,
84
+ });
85
+ }
69
86
  }
70
87
  }
71
88
 
72
- export const computedMinScale = (remoteSize: Size, currentSize: Size) => {
73
- const wScale = currentSize.width / remoteSize.width;
74
- const hScale = currentSize.height / remoteSize.height;
75
- return Math.min(wScale, hScale);
76
- }
@@ -93,6 +93,7 @@ export class MainViewProxy {
93
93
  id: this.manager.uid,
94
94
  ...this.view.camera
95
95
  });
96
+ // FIX 没有 mainViewSize 需要初始化一个 baseSize
96
97
  const stageRect = this.manager.boxManager?.stageRect;
97
98
  if (stageRect && !this.mainViewSize) {
98
99
  this.storeSize({
@@ -0,0 +1,229 @@
1
+ import { AnimationMode } from "white-web-sdk";
2
+ import { callbacks } from "../callback";
3
+ import { combine, derive, Val } from "value-enhancer";
4
+ import { createScrollStorage } from "../storage";
5
+ import { SCROLL_MODE_BASE_HEIGHT, SCROLL_MODE_BASE_WIDTH } from "../constants";
6
+ import { SideEffectManager } from "side-effect-manager";
7
+ import type { ReadonlyVal } from "value-enhancer";
8
+ import type { AppManager } from "../AppManager";
9
+ import type { ScrollStorage } from "../storage";
10
+ import type { Camera, Size, View } from "white-web-sdk";
11
+
12
+ function clamp(x: number, min: number, max: number): number {
13
+ return x < min ? min : x > max ? max : x;
14
+ }
15
+
16
+ export type ScrollState = {
17
+ scrollTop: number;
18
+ page: number;
19
+ maxScrollPage: number;
20
+ };
21
+
22
+ export class ScrollMode {
23
+ public readonly sideEffect = new SideEffectManager();
24
+
25
+ private readonly _root$: Val<HTMLElement | null>;
26
+ private readonly _whiteboard$: ReadonlyVal<HTMLElement | null>;
27
+ private readonly _scrollTop$: Val<number>;
28
+ public readonly _page$: ReadonlyVal<number>;
29
+ private readonly _scale$: ReadonlyVal<number>;
30
+ private readonly _size$: Val<Size>;
31
+ private readonly _mainView$: Val<View>;
32
+
33
+ private baseWidth = SCROLL_MODE_BASE_WIDTH;
34
+ private baseHeight = SCROLL_MODE_BASE_HEIGHT;
35
+
36
+ public scrollStorage: ScrollStorage;
37
+ public readonly scrollState$: ReadonlyVal<ScrollState>;
38
+
39
+ public setRoot(root: HTMLElement): void {
40
+ this._root$.setValue(root);
41
+ }
42
+
43
+ constructor(private manager: AppManager) {
44
+ this._root$ = new Val<HTMLElement | null>(null);
45
+ this._mainView$ = new Val<View>(this.manager.mainView);
46
+ this._mainView$.value.disableCameraTransform = true;
47
+
48
+ if (manager.scrollBaseSize$?.value) {
49
+ this.baseWidth = manager.scrollBaseSize$.value.width;
50
+ this.baseHeight = manager.scrollBaseSize$.value.height;
51
+ }
52
+
53
+ this.scrollStorage = createScrollStorage(manager);
54
+ const scrollTop$ = new Val(this.scrollStorage.state.scrollTop);
55
+ this._scrollTop$ = scrollTop$;
56
+
57
+ this.sideEffect.push(
58
+ this.scrollStorage.on("stateChanged", () => {
59
+ this._scrollTop$.setValue(this.scrollStorage.state.scrollTop);
60
+ })
61
+ );
62
+
63
+ const size$ = new Val<Size>(
64
+ { width: 0, height: 0 },
65
+ { compare: (a, b) => a.width === b.width && a.height === b.height }
66
+ );
67
+ this._size$ = size$;
68
+ this.sideEffect.add(() => {
69
+ const onSizeUpdated = size$.setValue.bind(size$);
70
+ onSizeUpdated(this._mainView$.value.size);
71
+ this._mainView$.value.callbacks.on("onSizeUpdated", onSizeUpdated);
72
+ return () => this._mainView$.value.callbacks.off("onSizeUpdated", onSizeUpdated);
73
+ });
74
+
75
+ this.sideEffect.add(() => {
76
+ const onCameraUpdated = (camera: Camera): void => {
77
+ const halfWbHeight = size$.value.height / 2 / scale$.value;
78
+ const scrollTop = camera.centerY;
79
+ this.scrollStorage.setState({
80
+ scrollTop: clamp(scrollTop, halfWbHeight, this.baseHeight - halfWbHeight),
81
+ });
82
+ callbacks.emit("userScroll");
83
+ };
84
+ this._mainView$.value.callbacks.on("onCameraUpdatedByDevice", onCameraUpdated);
85
+ return () =>
86
+ this._mainView$.value.callbacks.off("onCameraUpdatedByDevice", onCameraUpdated);
87
+ });
88
+
89
+ const scale$ = derive(size$, size => size.width / this.baseWidth);
90
+ this._scale$ = scale$;
91
+
92
+ const page$ = new Val(0);
93
+ this.sideEffect.push(
94
+ combine([scrollTop$, size$, scale$]).subscribe(([scrollTop, size, scale]) => {
95
+ if (scale > 0) {
96
+ const wbHeight = size.height / scale;
97
+ page$.setValue(Math.max(scrollTop / wbHeight - 0.5, 0));
98
+ }
99
+ })
100
+ );
101
+ this._page$ = page$;
102
+
103
+ // 5. bound$ = { contentMode: () => scale$, centerX: W / 2, centerY: H / 2, width: W, height: H }
104
+ this.sideEffect.push(
105
+ combine([scrollTop$, scale$]).subscribe(([scrollTop, scale]) => {
106
+ this.updateBound(scrollTop, size$.value, scale);
107
+ })
108
+ );
109
+
110
+ this.sideEffect.push(
111
+ size$.reaction(() => {
112
+ this.updateScroll(scrollTop$.value);
113
+ })
114
+ );
115
+
116
+ const whiteboard$ = derive(this._root$, this.getWhiteboardElement);
117
+ this._whiteboard$ = whiteboard$;
118
+ this.sideEffect.push(
119
+ whiteboard$.reaction(el => {
120
+ if (el?.parentElement) {
121
+ this.sideEffect.addEventListener(
122
+ el.parentElement,
123
+ "wheel",
124
+ this.onWheel,
125
+ { capture: true, passive: false },
126
+ "wheel"
127
+ );
128
+ }
129
+ })
130
+ );
131
+
132
+ this.sideEffect.push(
133
+ scale$.reaction(scale => {
134
+ if (scale > 0) {
135
+ this.sideEffect.flush("initScroll");
136
+ // XXX: wait window-manager's sync behavior then we reset the camera
137
+ this.sideEffect.setTimeout(this.initScroll, 0);
138
+ }
139
+ }),
140
+ "initScroll"
141
+ );
142
+
143
+ const maxScrollPage$ = combine([this._size$, this._scale$], ([size, scale]) => {
144
+ const halfWbHeight = size.height / 2 / scale;
145
+ return (this.baseHeight - halfWbHeight) / halfWbHeight / 2 - 0.51;
146
+ });
147
+
148
+ this.scrollState$ = combine(
149
+ [this._scrollTop$, this._page$, maxScrollPage$],
150
+ ([scrollTop, page, maxScrollPage]) => {
151
+ return {
152
+ scrollTop,
153
+ page,
154
+ maxScrollPage,
155
+ };
156
+ }
157
+ );
158
+
159
+ this.updateScroll(scrollTop$.value);
160
+ this.sideEffect.push(
161
+ this.scrollState$.subscribe(state => callbacks.emit("scrollStateChange", state))
162
+ );
163
+ }
164
+
165
+ private initScroll = (): void => {
166
+ const halfWbHeight = this._size$.value.height / 2 / this._scale$.value;
167
+ const scrollTop = this._scrollTop$.value;
168
+ // HACK: set a different value (+0.01) to trigger all effects above
169
+ this._scrollTop$.setValue(
170
+ clamp(scrollTop, halfWbHeight, this.baseHeight - halfWbHeight) - 0.01
171
+ );
172
+ };
173
+
174
+ private updateScroll(scrollTop: number): void {
175
+ this._mainView$.value.moveCamera({
176
+ centerY: scrollTop,
177
+ animationMode: AnimationMode.Immediately,
178
+ });
179
+ }
180
+
181
+ private updateBound(scrollTop: number, { height }: Size, scale: number): void {
182
+ if (scale > 0) {
183
+ this._mainView$.value.moveCameraToContain({
184
+ originX: 0,
185
+ originY: scrollTop - height / scale / 2,
186
+ width: this.baseWidth,
187
+ height: height / scale,
188
+ animationMode: AnimationMode.Immediately,
189
+ });
190
+
191
+ this._mainView$.value.setCameraBound({
192
+ damping: 1,
193
+ maxContentMode: () => scale,
194
+ minContentMode: () => scale,
195
+ centerX: this.baseWidth / 2,
196
+ centerY: this.baseHeight / 2,
197
+ width: this.baseWidth,
198
+ height: this.baseHeight,
199
+ });
200
+ }
201
+ }
202
+
203
+ public dispose(): void {
204
+ this.sideEffect.flushAll();
205
+ }
206
+
207
+ private getWhiteboardElement = (root: HTMLElement | null): HTMLElement | null => {
208
+ const className = ".netless-window-manager-main-view";
209
+ return root && root.querySelector(className);
210
+ };
211
+
212
+ private onWheel = (ev: WheelEvent): void => {
213
+ const target = ev.target as HTMLElement | null;
214
+ if (this.manager.canOperate && this._whiteboard$.value?.contains(target)) {
215
+ ev.preventDefault();
216
+ ev.stopPropagation();
217
+ const dy = ev.deltaY || 0;
218
+ const { width } = this._size$.value;
219
+ if (dy && width > 0) {
220
+ const halfWbHeight = this._size$.value.height / 2 / this._scale$.value;
221
+ const scrollTop = this._scrollTop$.value + dy / this._scale$.value;
222
+ this.scrollStorage.setState({
223
+ scrollTop: clamp(scrollTop, halfWbHeight, this.baseHeight - halfWbHeight),
224
+ });
225
+ callbacks.emit("userScroll");
226
+ }
227
+ }
228
+ };
229
+ }
@@ -1,11 +1,13 @@
1
- import { ViewMode, AnimationMode } from "white-web-sdk";
2
- import { CameraSynchronizer, computedMinScale } from "./CameraSynchronizer";
3
- import { combine } from "value-enhancer";
1
+ import { AnimationMode, ViewMode } from "white-web-sdk";
2
+ import { CameraSynchronizer } from "./CameraSynchronizer";
3
+ import { combine, derive } from "value-enhancer";
4
+ import { isEqual } from "lodash";
4
5
  import { SideEffectManager } from "side-effect-manager";
5
6
  import type { Camera, View } from "white-web-sdk";
6
7
  import type { Val, ReadonlyVal } from "value-enhancer";
7
8
  import type { ICamera, ISize } from "../AttributesDelegate";
8
9
  import type { TeleBoxRect } from "@netless/telebox-insider";
10
+ import type { ManagerViewMode } from "../typings";
9
11
 
10
12
  export type ViewSyncContext = {
11
13
  uid: string;
@@ -16,7 +18,7 @@ export type ViewSyncContext = {
16
18
 
17
19
  stageRect$: ReadonlyVal<TeleBoxRect>;
18
20
 
19
- viewMode$?: Val<ViewMode>;
21
+ viewMode$?: Val<ManagerViewMode>;
20
22
 
21
23
  storeCamera: (camera: ICamera) => void;
22
24
 
@@ -28,6 +30,7 @@ export type ViewSyncContext = {
28
30
  export class ViewSync {
29
31
  private sem = new SideEffectManager();
30
32
  private synchronizer: CameraSynchronizer;
33
+ private needRecoverCamera$?: ReadonlyVal<boolean>;
31
34
 
32
35
  constructor(private context: ViewSyncContext) {
33
36
  this.synchronizer = this.createSynchronizer();
@@ -38,15 +41,22 @@ export class ViewSync {
38
41
  this.subscribeSize(),
39
42
  this.subscribeStageRect(),
40
43
  ]);
44
+ if (context.viewMode$) {
45
+ this.needRecoverCamera$ = derive(context.viewMode$, mode => mode !== "scroll");
46
+ }
41
47
  const camera$size$ = combine([this.context.camera$, this.context.size$]);
42
48
  camera$size$.reaction(([camera, size]) => {
43
- if (camera && size) {
49
+ if (camera && size && this.needRecoverCamera$?.value) {
44
50
  this.synchronizer.onRemoteUpdate(camera, size);
45
51
  camera$size$.destroy();
46
52
  }
47
53
  });
48
54
  }
49
55
 
56
+ private get isBroadcastMode() {
57
+ return this.context.viewMode$?.value === ViewMode.Broadcaster;
58
+ }
59
+
50
60
  private createSynchronizer = () => {
51
61
  return new CameraSynchronizer((camera: ICamera) => {
52
62
  this.context.camera$.setValue(camera, true);
@@ -58,12 +68,12 @@ export class ViewSync {
58
68
  this.context.storeCamera(camera);
59
69
  }
60
70
  });
61
- }
71
+ };
62
72
 
63
73
  private subscribeView = () => {
64
74
  return this.context.view$.subscribe(view => {
65
75
  const currentCamera = this.context.camera$.value;
66
- if (currentCamera && this.context.size$.value) {
76
+ if (currentCamera && this.context.size$.value && this.needRecoverCamera$?.value) {
67
77
  view?.moveCamera({
68
78
  scale: 1,
69
79
  animationMode: AnimationMode.Immediately,
@@ -72,7 +82,7 @@ export class ViewSync {
72
82
  }
73
83
  this.bindView(view);
74
84
  });
75
- }
85
+ };
76
86
 
77
87
  private subscribeCamera = () => {
78
88
  return this.context.camera$.subscribe((camera, skipUpdate) => {
@@ -82,23 +92,23 @@ export class ViewSync {
82
92
  this.synchronizer.onRemoteUpdate(camera, size);
83
93
  }
84
94
  });
85
- }
95
+ };
86
96
 
87
97
  private subscribeSize = () => {
88
98
  return this.context.size$.subscribe(size => {
89
- if (size) {
99
+ if (size && this.isBroadcastMode) {
90
100
  this.synchronizer.onRemoteSizeUpdate(size);
91
101
  }
92
102
  });
93
- }
103
+ };
94
104
 
95
105
  private subscribeStageRect = () => {
96
106
  return this.context.stageRect$.subscribe(rect => {
97
107
  if (rect) {
98
- this.synchronizer.setRect(rect);
108
+ this.synchronizer.setRect(rect, this.isBroadcastMode);
99
109
  }
100
110
  });
101
- }
111
+ };
102
112
 
103
113
  public bindView = (view?: View) => {
104
114
  if (!view) return;
@@ -113,11 +123,14 @@ export class ViewSync {
113
123
 
114
124
  private onCameraUpdatedByDevice = (camera: Camera) => {
115
125
  if (!camera) return;
116
- if (this.context.size$.value && this.context.stageRect$.value) {
117
- // 始终以远端的 size 为标准, 设置到 attributes 时根据尺寸的大小还原回去
118
- const diffScale = computedMinScale(this.context.size$.value, this.context.stageRect$.value);
119
- const remoteScale = camera.scale / diffScale;
120
- this.synchronizer.onLocalCameraUpdate({ ...camera, scale: remoteScale, id: this.context.uid });
126
+ if (!this.isBroadcastMode) return;
127
+ const { size$, stageRect$, view$ } = this.context;
128
+ if (size$.value && stageRect$.value && view$.value) {
129
+ this.synchronizer.onLocalCameraUpdate({ ...camera, id: this.context.uid });
130
+ const newSize = { ...view$.value.size, id: this.context.uid };
131
+ if (!isEqual(size$.value, newSize)) {
132
+ this.context.storeSize(newSize);
133
+ }
121
134
  }
122
135
  };
123
136
 
package/src/callback.ts CHANGED
@@ -4,6 +4,7 @@ import type { CameraState, SceneState, ViewVisionMode } from "white-web-sdk";
4
4
  import type { LoadAppEvent } from "./Register";
5
5
  import type { PageState } from "./Page";
6
6
  import type { ICamera, ISize } from "./AttributesDelegate";
7
+ import type { ScrollState } from "./View/ScrollMode";
7
8
 
8
9
  export type PublicEvent = {
9
10
  mainViewModeChange: ViewVisionMode;
@@ -25,6 +26,8 @@ export type PublicEvent = {
25
26
  baseCameraChange: ICamera;
26
27
  baseSizeChange: ISize;
27
28
  fullscreenChange: TeleBoxFullscreen;
29
+ userScroll: undefined;
30
+ scrollStateChange: ScrollState;
28
31
  };
29
32
 
30
33
  export type CallbacksType = Emittery<PublicEvent>;
package/src/constants.ts CHANGED
@@ -64,3 +64,6 @@ export const INIT_DIR = "/init";
64
64
  export const SETUP_APP_DELAY = 50;
65
65
 
66
66
  export const MAX_PAGE_SIZE = 500;
67
+
68
+ export const SCROLL_MODE_BASE_WIDTH = 1600;
69
+ export const SCROLL_MODE_BASE_HEIGHT = SCROLL_MODE_BASE_WIDTH * 3;