@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.
- package/dist/index.cjs.js +375 -444
- package/dist/index.es.js +421 -490
- package/dist/index.umd.js +375 -445
- package/dist/src/App/AppContext.d.ts +8 -8
- package/dist/src/AppManager.d.ts +5 -1
- package/dist/src/Cursor/index.d.ts +1 -0
- package/dist/src/Utils/Reactive.d.ts +1 -1
- package/dist/src/View/CameraSynchronizer.d.ts +3 -2
- package/dist/src/View/ScrollMode.d.ts +32 -0
- package/dist/src/View/ViewSync.d.ts +3 -2
- package/dist/src/callback.d.ts +3 -0
- package/dist/src/constants.d.ts +2 -0
- package/dist/src/index.d.ts +21 -11
- package/dist/src/storage.d.ts +7 -0
- package/dist/src/typings.d.ts +5 -4
- package/dist/style.css +2 -1
- package/docs/api.md +10 -0
- package/package.json +4 -3
- package/pnpm-lock.yaml +28 -73
- package/src/App/AppContext.ts +19 -8
- package/src/App/WhiteboardView.ts +4 -2
- package/src/AppListener.ts +1 -10
- package/src/AppManager.ts +18 -1
- package/src/Cursor/index.ts +6 -2
- package/src/Utils/Reactive.ts +2 -1
- package/src/View/CameraSynchronizer.ts +33 -22
- package/src/View/MainView.ts +1 -0
- package/src/View/ScrollMode.ts +219 -0
- package/src/View/ViewSync.ts +24 -16
- package/src/callback.ts +3 -0
- package/src/constants.ts +3 -0
- package/src/index.ts +56 -63
- package/src/storage.ts +15 -0
- package/src/style.css +1 -1
- package/src/typings.ts +6 -3
- package/vite.config.js +1 -1
- package/dist/src/App/Storage/StorageEvent.d.ts +0 -8
- package/dist/src/App/Storage/index.d.ts +0 -39
- package/dist/src/App/Storage/typings.d.ts +0 -22
- package/dist/src/App/Storage/utils.d.ts +0 -5
- package/src/App/Storage/StorageEvent.ts +0 -21
- package/src/App/Storage/index.ts +0 -295
- package/src/App/Storage/typings.ts +0 -23
- 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) {
|
package/src/Cursor/index.ts
CHANGED
@@ -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 =
|
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,
|
72
|
+
cursorInstance = new Cursor(this.manager, uid, this, this.playground$.value);
|
69
73
|
this.cursorInstances.set(uid, cursorInstance);
|
70
74
|
}
|
71
75
|
return cursorInstance;
|
package/src/Utils/Reactive.ts
CHANGED
@@ -30,7 +30,8 @@ export const onObjectByEvent = (event: UpdateEventKind) => {
|
|
30
30
|
};
|
31
31
|
};
|
32
32
|
|
33
|
-
|
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 {
|
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
|
-
|
34
|
-
|
35
|
-
|
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
|
-
},
|
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
|
69
|
-
|
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
|
-
}
|
package/src/View/MainView.ts
CHANGED
@@ -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
|
+
}
|
package/src/View/ViewSync.ts
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
import {
|
2
|
-
import { 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<
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
const
|
125
|
-
|
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>;
|