@netless/app-slide 0.2.93 → 0.2.94
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/README-zh.md +3 -3
- package/README.md +3 -3
- package/dist/index.d.ts +11 -3
- package/dist/main.cjs.js +56 -56
- package/dist/main.cjs.js.map +1 -1
- package/dist/main.es.js +116 -17
- package/dist/main.es.js.map +1 -1
- package/dist/main.iife.js +55 -55
- package/dist/main.iife.js.map +1 -1
- package/package.json +8 -8
- package/src/DocsViewer/ResizableContainer.ts +52 -32
- package/src/DocsViewer/ScrollBar.ts +57 -51
- package/src/SlideController/index.ts +88 -5
- package/src/SlideDocsViewer/index.ts +24 -11
- package/src/index.ts +12 -5
- package/LICENSE +0 -21
package/package.json
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netless/app-slide",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.94",
|
|
4
4
|
"main": "dist/main.cjs.js",
|
|
5
5
|
"module": "dist/main.es.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"types": "node scripts/build-types.mjs",
|
|
9
|
+
"build": "vite build && npm run types",
|
|
10
|
+
"build:dev": "vite build --mode development && npm run types",
|
|
11
|
+
"cleanup": "rimraf ./dist"
|
|
12
|
+
},
|
|
7
13
|
"files": [
|
|
8
14
|
"src",
|
|
9
15
|
"dist",
|
|
@@ -24,11 +30,5 @@
|
|
|
24
30
|
"jspdf": {
|
|
25
31
|
"optional": true
|
|
26
32
|
}
|
|
27
|
-
},
|
|
28
|
-
"scripts": {
|
|
29
|
-
"types": "node scripts/build-types.mjs",
|
|
30
|
-
"build": "vite build && npm run types",
|
|
31
|
-
"build:dev": "vite build --mode development && npm run types",
|
|
32
|
-
"cleanup": "rimraf ./dist"
|
|
33
33
|
}
|
|
34
|
-
}
|
|
34
|
+
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {Slide} from "@netless/slide";
|
|
2
|
-
import {ScrollBar} from "./ScrollBar";
|
|
3
|
-
import type {AppContext} from "@netless/window-manager";
|
|
4
|
-
import type {Attributes, MagixEvents} from "../typings";
|
|
5
|
-
import type {AppOptions} from "../index";
|
|
1
|
+
import { Slide } from "@netless/slide";
|
|
2
|
+
import { ScrollBar } from "./ScrollBar";
|
|
3
|
+
import type { AppContext } from "@netless/window-manager";
|
|
4
|
+
import type { Attributes, MagixEvents } from "../typings";
|
|
5
|
+
import type { AppOptions } from "../index";
|
|
6
6
|
|
|
7
7
|
export class ResizableContainer {
|
|
8
|
-
|
|
9
8
|
private root: HTMLDivElement;
|
|
10
9
|
public container: HTMLDivElement;
|
|
11
10
|
private scrollContainer: HTMLDivElement;
|
|
@@ -35,19 +34,19 @@ export class ResizableContainer {
|
|
|
35
34
|
this.enableResize = enableResize;
|
|
36
35
|
this.parent = parent;
|
|
37
36
|
this.context = context;
|
|
38
|
-
this.root = document.createElement(
|
|
39
|
-
this.root.style.width =
|
|
40
|
-
this.root.style.height =
|
|
41
|
-
this.root.style.overflow =
|
|
37
|
+
this.root = document.createElement("div");
|
|
38
|
+
this.root.style.width = "100%";
|
|
39
|
+
this.root.style.height = "100%";
|
|
40
|
+
this.root.style.overflow = "hidden";
|
|
42
41
|
this.parent.appendChild(this.root);
|
|
43
|
-
this.scrollContainer = document.createElement(
|
|
42
|
+
this.scrollContainer = document.createElement("div");
|
|
44
43
|
this.scrollContainer.setAttribute("data-resizable-scroll", "true");
|
|
45
|
-
this.scrollContainer.style.width =
|
|
46
|
-
this.scrollContainer.style.height =
|
|
47
|
-
this.scrollContainer.style.position =
|
|
48
|
-
this.scrollContainer.style.overflow =
|
|
49
|
-
this.container = document.createElement(
|
|
50
|
-
this.container.style.position =
|
|
44
|
+
this.scrollContainer.style.width = "100%";
|
|
45
|
+
this.scrollContainer.style.height = "100%";
|
|
46
|
+
this.scrollContainer.style.position = "relative";
|
|
47
|
+
this.scrollContainer.style.overflow = "hidden";
|
|
48
|
+
this.container = document.createElement("div");
|
|
49
|
+
this.container.style.position = "relative";
|
|
51
50
|
this.container.setAttribute("data-resizable-container", "true");
|
|
52
51
|
this.scrollContainer.appendChild(this.container);
|
|
53
52
|
this.root.appendChild(this.scrollContainer);
|
|
@@ -63,7 +62,7 @@ export class ResizableContainer {
|
|
|
63
62
|
this.resizeObserver.observe(this.scrollContainer);
|
|
64
63
|
}
|
|
65
64
|
|
|
66
|
-
public getTranslate(): {x: number
|
|
65
|
+
public getTranslate(): { x: number; y: number } {
|
|
67
66
|
return { x: this.translateX, y: this.translateY };
|
|
68
67
|
}
|
|
69
68
|
|
|
@@ -71,7 +70,12 @@ export class ResizableContainer {
|
|
|
71
70
|
return this.scale;
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
private renderScrollBar(
|
|
73
|
+
private renderScrollBar(
|
|
74
|
+
width: number,
|
|
75
|
+
overflowWidth: number,
|
|
76
|
+
height: number,
|
|
77
|
+
overflowHeight: number
|
|
78
|
+
): void {
|
|
75
79
|
if (this.scrollBar) {
|
|
76
80
|
this.scrollBar.render(width, height, overflowWidth, overflowHeight);
|
|
77
81
|
}
|
|
@@ -105,12 +109,15 @@ export class ResizableContainer {
|
|
|
105
109
|
const whiteboardBounds = this.whiteboardContainer.getBoundingClientRect();
|
|
106
110
|
if (whiteboardBounds.width / whiteboardBounds.height > this.slideWidth / this.slideHeight) {
|
|
107
111
|
// 裁剪两边
|
|
108
|
-
const renderWidth = (whiteboardBounds.height * this.slideWidth / this.slideHeight
|
|
112
|
+
const renderWidth = (whiteboardBounds.height * this.slideWidth) / this.slideHeight;
|
|
109
113
|
const padding = (whiteboardBounds.width - renderWidth) / 2;
|
|
110
114
|
this.whiteboardContainer.style.clipPath = `inset(0px ${padding}px 0px ${padding}px)`;
|
|
111
|
-
} else if (
|
|
115
|
+
} else if (
|
|
116
|
+
whiteboardBounds.width / whiteboardBounds.height <
|
|
117
|
+
this.slideWidth / this.slideHeight
|
|
118
|
+
) {
|
|
112
119
|
// 裁剪上下
|
|
113
|
-
const renderHeight = (whiteboardBounds.width * this.slideHeight / this.slideWidth
|
|
120
|
+
const renderHeight = (whiteboardBounds.width * this.slideHeight) / this.slideWidth;
|
|
114
121
|
const padding = (whiteboardBounds.height - renderHeight) / 2;
|
|
115
122
|
this.whiteboardContainer.style.clipPath = `inset(${padding}px 0px ${padding}px 0px)`;
|
|
116
123
|
}
|
|
@@ -119,7 +126,12 @@ export class ResizableContainer {
|
|
|
119
126
|
this.translateX = 0.5;
|
|
120
127
|
this.translateY = 0.5;
|
|
121
128
|
|
|
122
|
-
this.renderScrollBar(
|
|
129
|
+
this.renderScrollBar(
|
|
130
|
+
parentBounds.width,
|
|
131
|
+
parentBounds.width * this.scale,
|
|
132
|
+
parentBounds.height,
|
|
133
|
+
parentBounds.height * this.scale
|
|
134
|
+
);
|
|
123
135
|
this.handleNormalizeTranslate(this.translateX, this.translateY, {
|
|
124
136
|
triggerScrollBar: true,
|
|
125
137
|
triggerSync: true,
|
|
@@ -127,11 +139,19 @@ export class ResizableContainer {
|
|
|
127
139
|
}
|
|
128
140
|
|
|
129
141
|
// x, y 范围 0 ~ 1
|
|
130
|
-
public handleNormalizeTranslate(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
public handleNormalizeTranslate(
|
|
143
|
+
x: number,
|
|
144
|
+
y: number,
|
|
145
|
+
options: {
|
|
146
|
+
triggerScrollBar: boolean;
|
|
147
|
+
triggerSync: boolean;
|
|
148
|
+
}
|
|
149
|
+
) {
|
|
150
|
+
if (
|
|
151
|
+
Math.abs(x - this.translateX) < 0.001 &&
|
|
152
|
+
Math.abs(y - this.translateY) < 0.001 &&
|
|
153
|
+
!options.triggerSync
|
|
154
|
+
) {
|
|
135
155
|
return;
|
|
136
156
|
}
|
|
137
157
|
const parentBounds = this.scrollContainer.getBoundingClientRect();
|
|
@@ -154,11 +174,11 @@ export class ResizableContainer {
|
|
|
154
174
|
return;
|
|
155
175
|
}
|
|
156
176
|
if (applyScale > 1.0) {
|
|
157
|
-
this.scrollContainer.style.width =
|
|
158
|
-
this.scrollContainer.style.height =
|
|
177
|
+
this.scrollContainer.style.width = "calc(100% - 6px)";
|
|
178
|
+
this.scrollContainer.style.height = "calc(100% - 6px)";
|
|
159
179
|
} else {
|
|
160
|
-
this.scrollContainer.style.width =
|
|
161
|
-
this.scrollContainer.style.height =
|
|
180
|
+
this.scrollContainer.style.width = "100%";
|
|
181
|
+
this.scrollContainer.style.height = "100%";
|
|
162
182
|
}
|
|
163
183
|
applyScale = this.enableResize ? applyScale : 1;
|
|
164
184
|
if (this.onScaleChanged && this.enableResize) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {ResizableContainer} from "./ResizableContainer";
|
|
1
|
+
import { ResizableContainer } from "./ResizableContainer";
|
|
2
2
|
|
|
3
3
|
export class ScrollBar {
|
|
4
4
|
private container: HTMLElement;
|
|
@@ -44,8 +44,6 @@ export class ScrollBar {
|
|
|
44
44
|
this.eventListeners = [];
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
47
|
/**
|
|
50
48
|
* 渲染滚动条
|
|
51
49
|
* @param containerWidth 容器宽度
|
|
@@ -79,7 +77,7 @@ export class ScrollBar {
|
|
|
79
77
|
|
|
80
78
|
// 更新水平滚动条 thumb 位置
|
|
81
79
|
if (this.horizontalScrollBar) {
|
|
82
|
-
const thumb = this.horizontalScrollBar.querySelector(
|
|
80
|
+
const thumb = this.horizontalScrollBar.querySelector(".horizontal-thumb") as HTMLElement;
|
|
83
81
|
if (thumb) {
|
|
84
82
|
const trackWidth = this.horizontalScrollBar.offsetWidth;
|
|
85
83
|
const thumbWidth = thumb.offsetWidth;
|
|
@@ -93,7 +91,7 @@ export class ScrollBar {
|
|
|
93
91
|
|
|
94
92
|
// 更新垂直滚动条 thumb 位置
|
|
95
93
|
if (this.verticalScrollBar) {
|
|
96
|
-
const thumb = this.verticalScrollBar.querySelector(
|
|
94
|
+
const thumb = this.verticalScrollBar.querySelector(".vertical-thumb") as HTMLElement;
|
|
97
95
|
if (thumb) {
|
|
98
96
|
const trackHeight = this.verticalScrollBar.offsetHeight;
|
|
99
97
|
const thumbHeight = thumb.offsetHeight;
|
|
@@ -106,7 +104,6 @@ export class ScrollBar {
|
|
|
106
104
|
}
|
|
107
105
|
}
|
|
108
106
|
|
|
109
|
-
|
|
110
107
|
private clearScrollBars(): void {
|
|
111
108
|
// 清理所有事件监听器
|
|
112
109
|
this.removeAllListeners();
|
|
@@ -121,17 +118,17 @@ export class ScrollBar {
|
|
|
121
118
|
}
|
|
122
119
|
}
|
|
123
120
|
|
|
124
|
-
private createHorizontalScrollBar(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
): void {
|
|
128
|
-
const scrollBar = document.createElement('div');
|
|
129
|
-
scrollBar.className = 'scroll-bar horizontal';
|
|
121
|
+
private createHorizontalScrollBar(containerWidth: number, contentWidth: number): void {
|
|
122
|
+
const scrollBar = document.createElement("div");
|
|
123
|
+
scrollBar.className = "scroll-bar horizontal";
|
|
130
124
|
|
|
131
125
|
// 计算滚动条thumb宽度
|
|
132
126
|
const availableWidth = containerWidth;
|
|
133
127
|
const trackWidth = availableWidth;
|
|
134
|
-
const thumbWidth = Math.max(
|
|
128
|
+
const thumbWidth = Math.max(
|
|
129
|
+
30,
|
|
130
|
+
Math.min(trackWidth, (trackWidth * availableWidth) / contentWidth)
|
|
131
|
+
);
|
|
135
132
|
|
|
136
133
|
// 滚动条轨道
|
|
137
134
|
scrollBar.style.cssText = `
|
|
@@ -149,8 +146,8 @@ export class ScrollBar {
|
|
|
149
146
|
// 滚动条始终显示,无需 hover 效果
|
|
150
147
|
|
|
151
148
|
// 滚动条thumb
|
|
152
|
-
const thumb = document.createElement(
|
|
153
|
-
thumb.className =
|
|
149
|
+
const thumb = document.createElement("div");
|
|
150
|
+
thumb.className = "scroll-bar-thumb horizontal-thumb";
|
|
154
151
|
thumb.style.cssText = `
|
|
155
152
|
position: absolute;
|
|
156
153
|
bottom: 0px;
|
|
@@ -164,24 +161,24 @@ export class ScrollBar {
|
|
|
164
161
|
`;
|
|
165
162
|
|
|
166
163
|
// 添加拖动事件
|
|
167
|
-
this.addThumbDragListener(thumb,
|
|
164
|
+
this.addThumbDragListener(thumb, "horizontal", thumbWidth, trackWidth);
|
|
168
165
|
|
|
169
166
|
scrollBar.appendChild(thumb);
|
|
170
167
|
this.container.appendChild(scrollBar);
|
|
171
168
|
this.horizontalScrollBar = scrollBar;
|
|
172
169
|
}
|
|
173
170
|
|
|
174
|
-
private createVerticalScrollBar(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
): void {
|
|
178
|
-
const scrollBar = document.createElement('div');
|
|
179
|
-
scrollBar.className = 'scroll-bar vertical';
|
|
171
|
+
private createVerticalScrollBar(containerHeight: number, contentHeight: number): void {
|
|
172
|
+
const scrollBar = document.createElement("div");
|
|
173
|
+
scrollBar.className = "scroll-bar vertical";
|
|
180
174
|
|
|
181
175
|
// 计算滚动条thumb高度
|
|
182
176
|
const availableHeight = containerHeight;
|
|
183
177
|
const trackHeight = availableHeight;
|
|
184
|
-
const thumbHeight = Math.max(
|
|
178
|
+
const thumbHeight = Math.max(
|
|
179
|
+
30,
|
|
180
|
+
Math.min(trackHeight, (trackHeight * availableHeight) / contentHeight)
|
|
181
|
+
);
|
|
185
182
|
|
|
186
183
|
// 滚动条轨道
|
|
187
184
|
scrollBar.style.cssText = `
|
|
@@ -199,8 +196,8 @@ export class ScrollBar {
|
|
|
199
196
|
// 滚动条始终显示,无需 hover 效果
|
|
200
197
|
|
|
201
198
|
// 滚动条thumb
|
|
202
|
-
const thumb = document.createElement(
|
|
203
|
-
thumb.className =
|
|
199
|
+
const thumb = document.createElement("div");
|
|
200
|
+
thumb.className = "scroll-bar-thumb vertical-thumb";
|
|
204
201
|
thumb.style.cssText = `
|
|
205
202
|
position: absolute;
|
|
206
203
|
top: 1px;
|
|
@@ -214,7 +211,7 @@ export class ScrollBar {
|
|
|
214
211
|
`;
|
|
215
212
|
|
|
216
213
|
// 添加拖动事件
|
|
217
|
-
this.addThumbDragListener(thumb,
|
|
214
|
+
this.addThumbDragListener(thumb, "vertical", thumbHeight, trackHeight);
|
|
218
215
|
|
|
219
216
|
scrollBar.appendChild(thumb);
|
|
220
217
|
this.container.appendChild(scrollBar);
|
|
@@ -226,7 +223,7 @@ export class ScrollBar {
|
|
|
226
223
|
*/
|
|
227
224
|
private addThumbDragListener(
|
|
228
225
|
thumb: HTMLElement,
|
|
229
|
-
orientation:
|
|
226
|
+
orientation: "horizontal" | "vertical",
|
|
230
227
|
thumbSize: number,
|
|
231
228
|
trackSize: number
|
|
232
229
|
): void {
|
|
@@ -234,24 +231,26 @@ export class ScrollBar {
|
|
|
234
231
|
let startThumbPos = 0;
|
|
235
232
|
|
|
236
233
|
const handleStart = (clientX: number, clientY: number) => {
|
|
237
|
-
if (orientation ===
|
|
234
|
+
if (orientation === "horizontal") {
|
|
238
235
|
this.isDraggingHorizontal = true;
|
|
239
236
|
} else {
|
|
240
237
|
this.isDraggingVertical = true;
|
|
241
238
|
}
|
|
242
|
-
startPos = orientation ===
|
|
243
|
-
startThumbPos =
|
|
244
|
-
|
|
245
|
-
|
|
239
|
+
startPos = orientation === "horizontal" ? clientX : clientY;
|
|
240
|
+
startThumbPos =
|
|
241
|
+
orientation === "horizontal"
|
|
242
|
+
? parseFloat(thumb.style.left) || 0
|
|
243
|
+
: parseFloat(thumb.style.top) || 0;
|
|
246
244
|
|
|
247
|
-
thumb.style.cursor =
|
|
245
|
+
thumb.style.cursor = "grabbing";
|
|
248
246
|
};
|
|
249
247
|
|
|
250
248
|
const handleMove = (clientX: number, clientY: number) => {
|
|
251
|
-
const isDragging =
|
|
249
|
+
const isDragging =
|
|
250
|
+
orientation === "horizontal" ? this.isDraggingHorizontal : this.isDraggingVertical;
|
|
252
251
|
if (!isDragging) return;
|
|
253
252
|
|
|
254
|
-
const currentPos = orientation ===
|
|
253
|
+
const currentPos = orientation === "horizontal" ? clientX : clientY;
|
|
255
254
|
const delta = currentPos - startPos;
|
|
256
255
|
|
|
257
256
|
// 计算新的thumb位置
|
|
@@ -259,7 +258,7 @@ export class ScrollBar {
|
|
|
259
258
|
newThumbPos = Math.max(1, Math.min(trackSize - thumbSize - 1, newThumbPos));
|
|
260
259
|
|
|
261
260
|
// 更新thumb位置
|
|
262
|
-
if (orientation ===
|
|
261
|
+
if (orientation === "horizontal") {
|
|
263
262
|
thumb.style.left = `${newThumbPos}px`;
|
|
264
263
|
} else {
|
|
265
264
|
thumb.style.top = `${newThumbPos}px`;
|
|
@@ -274,26 +273,34 @@ export class ScrollBar {
|
|
|
274
273
|
}
|
|
275
274
|
|
|
276
275
|
// 调用ResizableContainer的handleNormalizeTranslate方法,传入 false 避免循环调用
|
|
277
|
-
if (orientation ===
|
|
278
|
-
this.resizableContainer.handleNormalizeTranslate(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
276
|
+
if (orientation === "horizontal") {
|
|
277
|
+
this.resizableContainer.handleNormalizeTranslate(
|
|
278
|
+
scrollRatio,
|
|
279
|
+
this.resizableContainer["translateY"] ?? 0.5,
|
|
280
|
+
{
|
|
281
|
+
triggerScrollBar: false,
|
|
282
|
+
triggerSync: true,
|
|
283
|
+
}
|
|
284
|
+
);
|
|
282
285
|
} else {
|
|
283
|
-
this.resizableContainer.handleNormalizeTranslate(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
286
|
+
this.resizableContainer.handleNormalizeTranslate(
|
|
287
|
+
this.resizableContainer["translateX"] ?? 0.5,
|
|
288
|
+
scrollRatio,
|
|
289
|
+
{
|
|
290
|
+
triggerScrollBar: false,
|
|
291
|
+
triggerSync: true,
|
|
292
|
+
}
|
|
293
|
+
);
|
|
287
294
|
}
|
|
288
295
|
};
|
|
289
296
|
|
|
290
297
|
const handleEnd = () => {
|
|
291
|
-
if (orientation ===
|
|
298
|
+
if (orientation === "horizontal") {
|
|
292
299
|
this.isDraggingHorizontal = false;
|
|
293
300
|
} else {
|
|
294
301
|
this.isDraggingVertical = false;
|
|
295
302
|
}
|
|
296
|
-
thumb.style.cursor =
|
|
303
|
+
thumb.style.cursor = "grab";
|
|
297
304
|
};
|
|
298
305
|
|
|
299
306
|
// 鼠标事件
|
|
@@ -312,9 +319,9 @@ export class ScrollBar {
|
|
|
312
319
|
};
|
|
313
320
|
|
|
314
321
|
// 添加事件监听器
|
|
315
|
-
this.addTrackedListener(thumb,
|
|
316
|
-
this.addTrackedListener(document,
|
|
317
|
-
this.addTrackedListener(document,
|
|
322
|
+
this.addTrackedListener(thumb, "mousedown", handleMouseDown);
|
|
323
|
+
this.addTrackedListener(document, "mousemove", handleMouseMove);
|
|
324
|
+
this.addTrackedListener(document, "mouseup", handleMouseUp);
|
|
318
325
|
}
|
|
319
326
|
|
|
320
327
|
/**
|
|
@@ -324,4 +331,3 @@ export class ScrollBar {
|
|
|
324
331
|
this.clearScrollBars();
|
|
325
332
|
}
|
|
326
333
|
}
|
|
327
|
-
|
|
@@ -58,7 +58,6 @@ type MagixEventListener = Parameters<
|
|
|
58
58
|
AppContext<Attributes, MagixEvents>["addMagixEventListener"]
|
|
59
59
|
>[1];
|
|
60
60
|
|
|
61
|
-
|
|
62
61
|
export class SlideControllerBase {
|
|
63
62
|
public readonly context: SlideControllerOptions["context"];
|
|
64
63
|
public slide!: Slide;
|
|
@@ -87,7 +86,18 @@ export class SlideControllerBase {
|
|
|
87
86
|
public previewList: string[] = [];
|
|
88
87
|
|
|
89
88
|
public constructor(props: SlideControllerOptions) {
|
|
90
|
-
const {
|
|
89
|
+
const {
|
|
90
|
+
context,
|
|
91
|
+
onRenderStart,
|
|
92
|
+
onPageChanged,
|
|
93
|
+
onTransitionStart,
|
|
94
|
+
onTransitionEnd,
|
|
95
|
+
onNavigate,
|
|
96
|
+
onError,
|
|
97
|
+
onRenderError,
|
|
98
|
+
showRenderError,
|
|
99
|
+
invisibleBehavior,
|
|
100
|
+
} = props;
|
|
91
101
|
this.invisibleBehavior = invisibleBehavior ?? "frozen";
|
|
92
102
|
this.onRenderStart = onRenderStart;
|
|
93
103
|
this.onPageChanged = onPageChanged;
|
|
@@ -209,7 +219,20 @@ export class SlideControllerBase {
|
|
|
209
219
|
|
|
210
220
|
this.sideEffect.add(() => {
|
|
211
221
|
document.addEventListener("visibilitychange", this.onVisibilityChange);
|
|
212
|
-
|
|
222
|
+
this.bindAppStateChangeEvent();
|
|
223
|
+
return () => {
|
|
224
|
+
document.removeEventListener("visibilitychange", this.onVisibilityChange);
|
|
225
|
+
try {
|
|
226
|
+
this.context.emitter.off("boxStatusChange", this.onAppStatusChangeHandler);
|
|
227
|
+
this.context
|
|
228
|
+
.getWindowManager()
|
|
229
|
+
.emitter.off("boxStateChange", this.onAppStateChangeHandler);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
log(
|
|
232
|
+
"[Slide] unbind app state change event failed, because should update window manager to latest version"
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
213
236
|
});
|
|
214
237
|
}
|
|
215
238
|
|
|
@@ -387,14 +410,74 @@ export class SlideControllerBase {
|
|
|
387
410
|
}
|
|
388
411
|
};
|
|
389
412
|
|
|
413
|
+
protected bindAppStateChangeEvent = () => {
|
|
414
|
+
try {
|
|
415
|
+
const boxStatus = this.context.getBoxStatus();
|
|
416
|
+
if (boxStatus) {
|
|
417
|
+
const appProxy = this.context.getAppProxy();
|
|
418
|
+
if (appProxy) {
|
|
419
|
+
this.context.emitter.on("boxStatusChange", this.onAppStatusChangeHandler);
|
|
420
|
+
}
|
|
421
|
+
} else {
|
|
422
|
+
const windowManager = this.context.getWindowManager();
|
|
423
|
+
if (windowManager) {
|
|
424
|
+
windowManager.emitter.on("boxStateChange", this.onAppStateChangeHandler);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
} catch (error) {
|
|
428
|
+
log(
|
|
429
|
+
"[Slide] bind app state change event failed, because should update window manager to latest version"
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
protected onAppStateChangeHandler = (state: "normal" | "minimized" | "maximized") => {
|
|
435
|
+
if (state === "minimized") {
|
|
436
|
+
log("[Slide] freeze because app state is minimized");
|
|
437
|
+
this.freeze();
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
protected onAppStatusChangeHandler = (payload: {
|
|
442
|
+
appId: string;
|
|
443
|
+
status: "normal" | "minimized" | "maximized";
|
|
444
|
+
}) => {
|
|
445
|
+
const { appId, status } = payload;
|
|
446
|
+
if (appId === this.context.appId && status === "minimized") {
|
|
447
|
+
log("[Slide] freeze because app status is minimized");
|
|
448
|
+
this.freeze();
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
protected getAppStatus = (): "normal" | "minimized" | "maximized" | undefined => {
|
|
453
|
+
try {
|
|
454
|
+
const boxStatus = this.context.getBoxStatus();
|
|
455
|
+
// 如果boxStatus存在, 则使用boxStatus(单独窗口状态)
|
|
456
|
+
if (boxStatus) {
|
|
457
|
+
return boxStatus;
|
|
458
|
+
}
|
|
459
|
+
// 如果boxStatus不存在,则检查boxState是否存在(所有窗口统一状态)
|
|
460
|
+
const windowManager = this.context.getWindowManager();
|
|
461
|
+
const boxstate = windowManager.boxState;
|
|
462
|
+
return boxstate;
|
|
463
|
+
} catch (error) {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
390
468
|
protected onVisibilityChange = async () => {
|
|
469
|
+
const appStatus = this.getAppStatus();
|
|
470
|
+
if (appStatus === "minimized") {
|
|
471
|
+
log("[Slide] do nothing because app state is minimized");
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
391
474
|
if (!(this.visible = document.visibilityState === "visible")) {
|
|
392
475
|
this.savedIsFrozen = this.isFrozen;
|
|
393
476
|
log("[Slide] freeze because tab becomes invisible");
|
|
394
477
|
this.freeze();
|
|
395
478
|
} else {
|
|
396
|
-
log("[Slide] unfreeze because tab becomes visible", { savedIsFrozen: this.savedIsFrozen });
|
|
397
479
|
if (!this.savedIsFrozen) {
|
|
480
|
+
log("[Slide] unfreeze because tab becomes visible", { savedIsFrozen: this.savedIsFrozen });
|
|
398
481
|
this.unfreeze();
|
|
399
482
|
}
|
|
400
483
|
}
|
|
@@ -403,7 +486,7 @@ export class SlideControllerBase {
|
|
|
403
486
|
|
|
404
487
|
export class SlideController extends SlideControllerBase {
|
|
405
488
|
public constructor(props: SlideControllerOptions) {
|
|
406
|
-
super(props)
|
|
489
|
+
super(props);
|
|
407
490
|
this.slide = this.createSlide(props.anchor, {
|
|
408
491
|
whiteTracker: getRoomTracker(props.context.getDisplayer()),
|
|
409
492
|
});
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
ReadonlyTeleBox,
|
|
4
|
+
AnimationMode,
|
|
5
|
+
View,
|
|
6
|
+
AppContext,
|
|
7
|
+
StorageStateChangedListener,
|
|
8
|
+
} from "@netless/window-manager";
|
|
3
9
|
import type { SlideController, SlideControllerOptions } from "../SlideController";
|
|
4
10
|
|
|
5
11
|
import { SideEffectManager } from "side-effect-manager";
|
|
@@ -116,18 +122,21 @@ export class SlideDocsViewer {
|
|
|
116
122
|
};
|
|
117
123
|
|
|
118
124
|
// 记录初始状态
|
|
119
|
-
console.log(
|
|
120
|
-
console.log(
|
|
125
|
+
console.log("[SlideDocsViewer] Initial storage state:", this.context.storage.state);
|
|
126
|
+
console.log("[SlideDocsViewer] Initial slideScale:", this.context.storage.state.slideScale);
|
|
121
127
|
|
|
122
128
|
// 应用初始的 slideScale 值(现在 ResizableContainer 应该已经创建)
|
|
123
129
|
if (this.context.storage.state.slideScale !== undefined) {
|
|
124
|
-
console.log(
|
|
130
|
+
console.log(
|
|
131
|
+
"[SlideDocsViewer] Applying initial slideScale:",
|
|
132
|
+
this.context.storage.state.slideScale
|
|
133
|
+
);
|
|
125
134
|
applyScale(this.context.storage.state.slideScale);
|
|
126
135
|
} else {
|
|
127
|
-
console.log(
|
|
136
|
+
console.log("[SlideDocsViewer] No initial slideScale found");
|
|
128
137
|
}
|
|
129
138
|
|
|
130
|
-
const handler: StorageStateChangedListener<Attributes> =
|
|
139
|
+
const handler: StorageStateChangedListener<Attributes> = diff => {
|
|
131
140
|
if (diff.slideScale !== undefined) {
|
|
132
141
|
// slideScale 是一个包含 newValue 和 oldValue 的对象
|
|
133
142
|
const newScale = diff.slideScale.newValue;
|
|
@@ -135,8 +144,8 @@ export class SlideDocsViewer {
|
|
|
135
144
|
}
|
|
136
145
|
if (diff.translateX !== undefined || diff.translateY !== undefined) {
|
|
137
146
|
const currentTranslate = this.resizableContainer.getTranslate();
|
|
138
|
-
const translateX = diff.translateX ?
|
|
139
|
-
const translateY = diff.translateY ?
|
|
147
|
+
const translateX = diff.translateX ? diff.translateX.newValue ?? 0.5 : currentTranslate.x;
|
|
148
|
+
const translateY = diff.translateY ? diff.translateY.newValue ?? 0.5 : currentTranslate.y;
|
|
140
149
|
if (this.resizableContainer) {
|
|
141
150
|
this.resizableContainer.handleNormalizeTranslate(translateX, translateY, {
|
|
142
151
|
triggerScrollBar: true,
|
|
@@ -146,11 +155,11 @@ export class SlideDocsViewer {
|
|
|
146
155
|
}
|
|
147
156
|
};
|
|
148
157
|
|
|
149
|
-
console.log(
|
|
158
|
+
console.log("[SlideDocsViewer] Adding storage state listener");
|
|
150
159
|
this.context.storage.onStateChanged.addListener(handler);
|
|
151
160
|
|
|
152
161
|
return () => {
|
|
153
|
-
console.log(
|
|
162
|
+
console.log("[SlideDocsViewer] Removing storage state listener");
|
|
154
163
|
this.context.storage.onStateChanged.removeListener(handler);
|
|
155
164
|
};
|
|
156
165
|
});
|
|
@@ -169,7 +178,11 @@ export class SlideDocsViewer {
|
|
|
169
178
|
public render() {
|
|
170
179
|
// 创建 ResizableContainer 来管理 slide 和 whiteboardView
|
|
171
180
|
if (!this.resizableContainer) {
|
|
172
|
-
this.resizableContainer = new ResizableContainer(
|
|
181
|
+
this.resizableContainer = new ResizableContainer(
|
|
182
|
+
this.viewer.$content,
|
|
183
|
+
this.context,
|
|
184
|
+
this.enableScale
|
|
185
|
+
);
|
|
173
186
|
}
|
|
174
187
|
|
|
175
188
|
// 创建元素
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
EmptyAttributes,
|
|
13
13
|
syncSceneWithSlide,
|
|
14
14
|
SlideController,
|
|
15
|
-
SlideControllerBase
|
|
15
|
+
SlideControllerBase,
|
|
16
16
|
} from "./SlideController";
|
|
17
17
|
import { SlideDocsViewer } from "./SlideDocsViewer";
|
|
18
18
|
import { apps, FreezerLength, addHooks, useFreezer } from "./utils/freezer";
|
|
@@ -24,13 +24,20 @@ export { SlidePreviewer, default as previewSlide } from "./SlidePreviewer";
|
|
|
24
24
|
export { DocsViewer } from "./DocsViewer";
|
|
25
25
|
|
|
26
26
|
export type { Attributes, AddHooks, FreezableSlide };
|
|
27
|
-
export {
|
|
27
|
+
export {
|
|
28
|
+
Slide,
|
|
29
|
+
SlideController,
|
|
30
|
+
SlideDocsViewer,
|
|
31
|
+
syncSceneWithSlide,
|
|
32
|
+
SideEffectManager,
|
|
33
|
+
SlideControllerBase,
|
|
34
|
+
};
|
|
28
35
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
36
|
export const usePlugin: (plugin: any) => any = /* @__PURE__ */ Slide.usePlugin.bind(Slide);
|
|
30
37
|
|
|
31
38
|
export const version = __APP_VERSION__;
|
|
32
39
|
|
|
33
|
-
export { DefaultUrl, apps, FreezerLength, addHooks, useFreezer, log, logger
|
|
40
|
+
export { DefaultUrl, apps, FreezerLength, addHooks, useFreezer, log, logger };
|
|
34
41
|
|
|
35
42
|
export { setFreezerLength, getFreezerLength, onCreated, onDestroyed } from "./utils/freezer";
|
|
36
43
|
|
|
@@ -286,8 +293,8 @@ const SlideApp: NetlessApp<Attributes, MagixEvents, AppOptions, AppResult> = {
|
|
|
286
293
|
|
|
287
294
|
if (maxScrollX > 0 || maxScrollY > 0) {
|
|
288
295
|
// 获取当前标准化位置 (0 ~ 1)
|
|
289
|
-
const currentX = container[
|
|
290
|
-
const currentY = container[
|
|
296
|
+
const currentX = container["translateX"] ?? 0.5;
|
|
297
|
+
const currentY = container["translateY"] ?? 0.5;
|
|
291
298
|
|
|
292
299
|
// 将像素偏移转换为标准化偏移
|
|
293
300
|
const normalizedDeltaX = maxScrollX > 0 ? offsetX / maxScrollX : 0;
|