@netless/window-manager 1.0.0-canary.53 → 1.0.0-canary.55

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 +375 -444
  2. package/dist/index.es.js +421 -490
  3. package/dist/index.umd.js +375 -445
  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 +3 -2
  9. package/dist/src/View/ScrollMode.d.ts +32 -0
  10. package/dist/src/View/ViewSync.d.ts +3 -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 +18 -1
  24. package/src/Cursor/index.ts +6 -2
  25. package/src/Utils/Reactive.ts +2 -1
  26. package/src/View/CameraSynchronizer.ts +33 -22
  27. package/src/View/MainView.ts +1 -0
  28. package/src/View/ScrollMode.ts +219 -0
  29. package/src/View/ViewSync.ts +24 -16
  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) => {
@@ -713,6 +729,7 @@ export class AppManager {
713
729
  }
714
730
  }
715
731
  emitter.emit("writableChange", isWritable);
732
+ this.isWritable$.setValue(isWritable);
716
733
  };
717
734
 
718
735
  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,6 +11,7 @@ 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
 
@@ -30,20 +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
- console.trace("moveCamera");
44
- this.moveCamera(config);
34
+ requestAnimationFrame(() => {
35
+ this.moveCameraToContian(size);
36
+ this.moveCamera(camera);
37
+ });
45
38
  }
46
- }, 10);
39
+ }, 32);
47
40
 
48
41
  public onRemoteSizeUpdate(size: ISize) {
49
42
  this.remoteSize = size;
@@ -65,13 +58,31 @@ export class CameraSynchronizer {
65
58
  this.remoteCamera = camera;
66
59
  }
67
60
 
68
- private moveCamera(camera: Partial<Camera>) {
69
- 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
+ }
70
86
  }
71
87
  }
72
88
 
73
- export const computedMinScale = (remoteSize: Size, currentSize: Size) => {
74
- const wScale = currentSize.width / remoteSize.width;
75
- const hScale = currentSize.height / remoteSize.height;
76
- return Math.min(wScale, hScale);
77
- }
@@ -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,219 @@
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
+ const maxScrollPage$ = combine([this._size$, this._scale$], ([size, scale]) => {
133
+ const halfWbHeight = size.height / 2 / scale;
134
+ return (this.baseHeight - halfWbHeight) / halfWbHeight / 2 - 0.51;
135
+ });
136
+
137
+ this.scrollState$ = combine(
138
+ [this._scrollTop$, this._page$, maxScrollPage$],
139
+ ([scrollTop, page, maxScrollPage]) => {
140
+ return {
141
+ scrollTop,
142
+ page,
143
+ maxScrollPage,
144
+ };
145
+ }
146
+ );
147
+
148
+ this.updateScroll(scrollTop$.value);
149
+ this.sideEffect.push(
150
+ this.scrollState$.subscribe(state => callbacks.emit("scrollStateChange", state))
151
+ );
152
+ this.initScroll();
153
+ }
154
+
155
+ private initScroll = (): void => {
156
+ const halfWbHeight = this._size$.value.height / 2 / this._scale$.value;
157
+ const scrollTop = this._scrollTop$.value;
158
+ // HACK: set a different value (+0.01) to trigger all effects above
159
+ this._scrollTop$.setValue(
160
+ clamp(scrollTop, halfWbHeight, this.baseHeight - halfWbHeight) - 0.01
161
+ );
162
+ };
163
+
164
+ private updateScroll(scrollTop: number): void {
165
+ this._mainView$.value.moveCamera({
166
+ centerY: scrollTop,
167
+ animationMode: AnimationMode.Immediately,
168
+ });
169
+ }
170
+
171
+ private updateBound(scrollTop: number, { height }: Size, scale: number): void {
172
+ if (scale > 0) {
173
+ this._mainView$.value.moveCameraToContain({
174
+ originX: 0,
175
+ originY: scrollTop - height / scale / 2,
176
+ width: this.baseWidth,
177
+ height: height / scale,
178
+ animationMode: AnimationMode.Immediately,
179
+ });
180
+
181
+ this._mainView$.value.setCameraBound({
182
+ damping: 1,
183
+ maxContentMode: () => scale,
184
+ minContentMode: () => scale,
185
+ centerX: this.baseWidth / 2,
186
+ centerY: this.baseHeight / 2,
187
+ width: this.baseWidth,
188
+ height: this.baseHeight,
189
+ });
190
+ }
191
+ }
192
+
193
+ public dispose(): void {
194
+ this.sideEffect.flushAll();
195
+ }
196
+
197
+ private getWhiteboardElement = (root: HTMLElement | null): HTMLElement | null => {
198
+ const className = ".netless-window-manager-main-view";
199
+ return root && root.querySelector(className);
200
+ };
201
+
202
+ private onWheel = (ev: WheelEvent): void => {
203
+ const target = ev.target as HTMLElement | null;
204
+ if (this.manager.canOperate && this._whiteboard$.value?.contains(target)) {
205
+ ev.preventDefault();
206
+ ev.stopPropagation();
207
+ const dy = ev.deltaY || 0;
208
+ const { width } = this._size$.value;
209
+ if (dy && width > 0) {
210
+ const halfWbHeight = this._size$.value.height / 2 / this._scale$.value;
211
+ const scrollTop = this._scrollTop$.value + dy / this._scale$.value;
212
+ this.scrollStorage.setState({
213
+ scrollTop: clamp(scrollTop, halfWbHeight, this.baseHeight - halfWbHeight),
214
+ });
215
+ callbacks.emit("userScroll");
216
+ }
217
+ }
218
+ };
219
+ }
@@ -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,9 +41,12 @@ 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
  }
@@ -62,12 +68,12 @@ export class ViewSync {
62
68
  this.context.storeCamera(camera);
63
69
  }
64
70
  });
65
- }
71
+ };
66
72
 
67
73
  private subscribeView = () => {
68
74
  return this.context.view$.subscribe(view => {
69
75
  const currentCamera = this.context.camera$.value;
70
- if (currentCamera && this.context.size$.value) {
76
+ if (currentCamera && this.context.size$.value && this.needRecoverCamera$?.value) {
71
77
  view?.moveCamera({
72
78
  scale: 1,
73
79
  animationMode: AnimationMode.Immediately,
@@ -76,7 +82,7 @@ export class ViewSync {
76
82
  }
77
83
  this.bindView(view);
78
84
  });
79
- }
85
+ };
80
86
 
81
87
  private subscribeCamera = () => {
82
88
  return this.context.camera$.subscribe((camera, skipUpdate) => {
@@ -86,7 +92,7 @@ export class ViewSync {
86
92
  this.synchronizer.onRemoteUpdate(camera, size);
87
93
  }
88
94
  });
89
- }
95
+ };
90
96
 
91
97
  private subscribeSize = () => {
92
98
  return this.context.size$.subscribe(size => {
@@ -94,7 +100,7 @@ export class ViewSync {
94
100
  this.synchronizer.onRemoteSizeUpdate(size);
95
101
  }
96
102
  });
97
- }
103
+ };
98
104
 
99
105
  private subscribeStageRect = () => {
100
106
  return this.context.stageRect$.subscribe(rect => {
@@ -102,7 +108,7 @@ export class ViewSync {
102
108
  this.synchronizer.setRect(rect, this.isBroadcastMode);
103
109
  }
104
110
  });
105
- }
111
+ };
106
112
 
107
113
  public bindView = (view?: View) => {
108
114
  if (!view) return;
@@ -118,11 +124,13 @@ export class ViewSync {
118
124
  private onCameraUpdatedByDevice = (camera: Camera) => {
119
125
  if (!camera) return;
120
126
  if (!this.isBroadcastMode) return;
121
- if (this.context.size$.value && this.context.stageRect$.value) {
122
- // 始终以远端的 size 为标准, 设置到 attributes 时根据尺寸的大小还原回去
123
- const diffScale = computedMinScale(this.context.size$.value, this.context.stageRect$.value);
124
- const remoteScale = camera.scale / diffScale;
125
- this.synchronizer.onLocalCameraUpdate({ ...camera, scale: remoteScale, id: this.context.uid });
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
+ }
126
134
  }
127
135
  };
128
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;