@netless/slide 0.3.5 → 0.3.8
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.md +469 -27
- package/lib/Lock.d.ts +2 -0
- package/lib/Lock.js +12 -1
- package/lib/Slide.d.ts +12 -0
- package/lib/Slide.js +14 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,54 +2,496 @@
|
|
|
2
2
|
|
|
3
3
|
## 基本使用
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### 初始化 `Slide` 对象
|
|
6
|
+
|
|
7
|
+
要初始化 `Slide` 对象, 至少要指定三项配置
|
|
8
|
+
|
|
9
|
+
| key | type | description |
|
|
10
|
+
| ---- | ---- | --- |
|
|
11
|
+
| anchor | HTMLElement | 作为 `Slide` 渲染出的 `canvas` 元素的挂载点 |
|
|
12
|
+
| interactive | boolean |ppt 是否可交互, 不可交互的 ppt 无法响应用户的事件|
|
|
13
|
+
| mode | "local" | "interactive" |local: 单机模式, Slide 对象不会触发任意同步事件。<br/>interactive: 互动模式, 所有客户端都可以交互|
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
6
16
|
import { Slide } from "@netless/slide";
|
|
7
17
|
|
|
8
|
-
// 初始化
|
|
9
|
-
// 初始化可以传入可选的 renderOptions, 参考 d.ts 类型定义
|
|
10
18
|
const slide = new Slide({
|
|
11
|
-
anchor:
|
|
19
|
+
anchor: someDivElement,
|
|
12
20
|
interactive: true,
|
|
21
|
+
mode: "local",
|
|
13
22
|
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 设置转换资源
|
|
26
|
+
|
|
27
|
+
`Slide` 对象创建之后, 下一步需要设置转换后的资源。`taskId` 为一串 hash 字符串代表一次转换任务 id, `prefixUrl` 为一段 url 地址, 指向转换后的资源根路径。这两个参数都可以从 [转码服务的进度查询 api](https://developer.netless.link/server-zh/home/server-conversion) 中获取.
|
|
28
|
+
|
|
29
|
+
`注意`: 你需要保证访问 prefixUrl 路径里的资源不会跨域。
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
14
32
|
|
|
15
|
-
|
|
16
|
-
slide.setResource(`id`, "url-prefix");
|
|
33
|
+
slide.setResource("06415a307f2011ec8bdc15d18ec9acc7", "https://convertcdn.netless.group/dynamicConvert");
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 渲染 ppt 页面
|
|
38
|
+
|
|
39
|
+
设置好转换资源后,就可以调用 `renderSlide` 渲染页面了, 传入参数是 ppt 页码, 页码从 1 开始。你也可以调用 `renderSlide` 跳转到任意页码。
|
|
40
|
+
你需要确保传入的页码在原始 ppt 页数范围内, 访问 `slide.slideCount` 可以获取总页数
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
// 渲染第一页
|
|
19
44
|
slide.renderSlide(1);
|
|
45
|
+
|
|
46
|
+
// 渲染最后一页
|
|
47
|
+
slide.slideCount(slide.slideCount);
|
|
20
48
|
```
|
|
49
|
+
## 可选配置项
|
|
50
|
+
|
|
51
|
+
### `Slide` 配置
|
|
21
52
|
|
|
22
|
-
|
|
53
|
+
初始化 `Slide` 还有一些可选的配置项,说明如下
|
|
23
54
|
|
|
55
|
+
```javascript
|
|
56
|
+
const slide = new Slide({
|
|
57
|
+
anchor: someDivElement,
|
|
58
|
+
interactive: true,
|
|
59
|
+
mode: "local",
|
|
60
|
+
// 以下为可选配置
|
|
61
|
+
resize: false,
|
|
62
|
+
enableGlobalClick: false,
|
|
63
|
+
timestamp: Date.now,
|
|
64
|
+
renderOptions: {
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
});
|
|
24
68
|
```
|
|
25
|
-
import { Slide, SLIDE_EVENTS } from "@netless/slide";
|
|
26
69
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
70
|
+
| key | type | description |
|
|
71
|
+
| ---- | ---- | --- |
|
|
72
|
+
| resize | boolean | **默认值:** false <br/> 设置是否根据窗口大小自动调整分辨率。<br/> 默认情况下 ppt 的 css 尺寸会随着 anchor 元素的大小而变化, 但是 canvas 元素的渲染分辨率不会变化。将此值设置为 true, 会使 canvas 的分辨率也跟随缩放比例缩放,这样能获得更好的性能,但是当 anchor 的 css 尺寸太小的情况下,也会导致画面模糊。<br /> 除非遇到性能问题,一般不建议设置为 true 。|
|
|
73
|
+
| enableGlobalClick | boolean |**默认值:** false <br/> 用于控制是否可以通过点击 ppt 画面执行下一步功能。<br /> 建议移动端开启,移动端受限于屏幕尺寸,交互 UI 较小,如果开启此功能会比较方便执行下一步。|
|
|
74
|
+
| timestamp | () => number |**默认值:** Date.now <br/> 此函数用于获取当前时间, 在同步及互动场景下,ppt 内部需要知道当前时间,这个时间对于参与同步(互动)的多个客户端应该是一致的,这个时间越精确,画面同步也越精确。<br />建议通过后端服务,为多个客户端下发相同的时间。|
|
|
75
|
+
| rtcAudio | RtcAudioClazz |**默认值:** null <br/> 用于 rtc 混音, 具体用法见下文 |
|
|
76
|
+
| logger | ILogger |**默认值:** null <br/> 用于接受日志, 具体用法见下文 |
|
|
77
|
+
| useLocalCache | boolean |**默认值:** true <br/> 是否启用本地缓存,启用后会将 ppt 远程资源缓存在 indexDB 中 |
|
|
78
|
+
| renderOptions | ISlideRenderOptions 对象 | 见下表 |
|
|
79
|
+
|
|
80
|
+
### ISlideRenderOptions 配置
|
|
81
|
+
|
|
82
|
+
| key | type | description |
|
|
83
|
+
| ---- | ---- | --- |
|
|
84
|
+
| minFPS | number | **默认值:** 30 <br/> 设置最小 fps, 应用会尽量保证实际 fps 高于此值, 此值越小, cpu 开销越小。 |
|
|
85
|
+
| maxFPS | number | **默认值:** 40 <br/> 设置最大 fps, 应用会保证实际 fps 低于此值, 此值越小, cpu 开销越小。 |
|
|
86
|
+
| resolution | number | **默认值:** pc 浏览器为 window.devicePixelRatio; 移动端浏览器为 1 。<br/> 设置渲染分辨倍率, 原始 ppt 有自己的像素尺寸,当在 2k 或者 4k 屏幕下,如果按原始 ppt 分辨率显示,画面会比较模糊。可以调整此值,使画面更清晰,同时性能开销也变高。<br /> 建议保持默认值就行,或者固定为 1。 |
|
|
87
|
+
| autoResolution | boolean | **默认值:** false, 控制是否根据运行时实际 fps 自动缩放渲染分辨率, 使得运行时 fps 保持在 minFPS 和 masFPS 之间 |
|
|
88
|
+
| autoFPS | boolean | **默认值:** false, 控制开启动态 fps, 开启后, 会根据 cpu 效率动态提升和降低 fps |
|
|
89
|
+
| transactionBgColor | string | number | **默认值:** 0x000000, 设置切页动画的背景色, 接受 css 颜色字符串或者 16进制颜色值("#ffffff",0xffffff) |
|
|
90
|
+
|
|
91
|
+
### 互动模式
|
|
92
|
+
|
|
93
|
+
互动模式下, 各个客户端都可以自由操作 ppt. 与同步模式一样, `@netless/slide` 库通过事件将各个客户端的操作通知给 `@netless/slide` 的调用方, 调用方负责将这些事件传递给所有客户端(包括自己). 与同步模式不同的是, 互动模式下, 发送事件的客户端也同时需要处理接收事件.
|
|
94
|
+
|
|
95
|
+
要使用互动模式需要将上述的 `mode` 参数设置为 `"interactive"`.
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
// client A
|
|
99
|
+
slideA.on(SLIDE_EVENTS.syncDispatch, (event) => {
|
|
100
|
+
// event 为可序列化的 js 对象, 你无需关心 event 具体信息
|
|
101
|
+
// 需要将序列化后的 event 广播给所有参与互动的客户端(包括 slideA 自己)
|
|
102
|
+
socket.boardcast("slide-sync", JSON.stringify(event));
|
|
103
|
+
});
|
|
104
|
+
// 与同步模式不同, 互动模式下, slideA 自己也需要监听来自 socket
|
|
105
|
+
// 的事件, 并将事件派发给 slideA 对象.
|
|
106
|
+
socket.on("slide-sync", msg => {
|
|
107
|
+
const event = JSON.parse(msg);
|
|
108
|
+
slideA.emit(SLIDE_EVENTS.syncReceive, event);
|
|
31
109
|
});
|
|
32
110
|
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
111
|
+
// client B 执行与 clientA 一样的逻辑, 监听 SLIDE_EVENTS.syncDispatch 事件并广播出去
|
|
112
|
+
// 同时自己处理来自 socket 的事件
|
|
113
|
+
slideB.on(SLIDE_EVENTS.syncDispatch, (event) => {
|
|
114
|
+
socket.boardcast("slide-sync", JSON.stringify(event));
|
|
115
|
+
});
|
|
116
|
+
socket.on("slide-sync", msg => {
|
|
117
|
+
const event = JSON.parse(msg);
|
|
118
|
+
slideB.emit(SLIDE_EVENTS.syncReceive, event);
|
|
119
|
+
});
|
|
36
120
|
```
|
|
37
121
|
|
|
38
|
-
|
|
122
|
+
### 整体同步
|
|
123
|
+
|
|
124
|
+
在某些情况下, 需要一种机制将客户端 A 的状态一次性整体同步给客户端 B, 而不是通过一条一条事件完成同步。例如: 客户端 B 断线后重新连接至 socket 房间, 此时需要将客户端 A 的当前状态一次性同步给 B.
|
|
125
|
+
|
|
126
|
+
为此 `@netless/slide` 提供了获取和设置应用整体状态的机制.
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
// 访问 slideState 可以获取 slide 状态快照
|
|
130
|
+
const snapshot = slideA.slideState;
|
|
131
|
+
|
|
132
|
+
// 将 slideB 的状态同步到 slideA 当前状态
|
|
133
|
+
slideB.setSlideState(snapshot);
|
|
134
|
+
|
|
39
135
|
```
|
|
40
|
-
// 客户端 A
|
|
41
|
-
// 获取 Slide 应用状态
|
|
42
|
-
const args = slide.slideState;
|
|
43
136
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
137
|
+
在同步模式下, 被同步的客户端 B 可以在断线重连后询问客户端 A 的当前状态, 客户端 A 收到询问后可以使用上述 API 获取状态快照.
|
|
138
|
+
但是在互动模式下, 这种询问的机制就不适用了, 互动模式下所有客户端应该共享同一个状态, 要做到这种效果, 可以在某处(一般是 socket 房间信息上)记录这个状态快照, `@netless/slide` 会在状态变更后通知给你,此时可以将最新的状态记录下来.
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
slideA.on(SLIDE_EVENTS.stateChange, snapshot => {
|
|
142
|
+
socket.room.slideState = snapshot;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// 客户端 B 重新连接后, 获取房间信息上的状态并设置
|
|
146
|
+
socket.on("connect", () => {
|
|
147
|
+
slideB.setSlideState(socket.room.slideState);
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 竞态处理
|
|
152
|
+
|
|
153
|
+
在互动模式下, 由于每个客户端都可以独立的与 ppt 交互,因此存在竞态条件。例如, 客户端 A 执行翻到下一页(记为事件 A),与此同时客户端 B 执行切换到下一个动画(记为事件 B). 这两个事件执行的顺序会影响最终的状态(假设执行事件之前, 处于 ppt 第一页的第一个动画):
|
|
154
|
+
|
|
155
|
+
**A-B:** 先翻页, 再播放下一个动画, 最终状态为第二页的第一个动画
|
|
156
|
+
**B-A:** 先播放下一个动画, 再执行下一页, 最终状态为第二页第 0 个动画
|
|
157
|
+
|
|
158
|
+
这两个事件都会传递到 socket 服务器。socket 服务器是否是按事件产生的真实时间来下发这两个事件并不重要, 重要的是两个客户端接收事件的顺序必须一致(A-B或者B-A),如此才能保证两个客户端最终状态一致。因此, 你需要保证参与互动的每个客户端收到的事件顺序是一致的。
|
|
159
|
+
|
|
160
|
+
### rtc 混音
|
|
161
|
+
|
|
162
|
+
**注意: `@netless/slide@0.2.9` 版本才开始支持。**
|
|
163
|
+
|
|
164
|
+
ppt 里设计的音频和视频, 默认是用浏览器的 api 来播放, 如果有 rtc 混音需要可以提供自定义的播放器类来替换掉内置播放器。自定义的播放器需要实现下面的 `RtcAudioClazz` 接口。
|
|
165
|
+
```typescript
|
|
166
|
+
export interface RtcAudio {
|
|
167
|
+
/**
|
|
168
|
+
* 开始播放音频.
|
|
169
|
+
*/
|
|
170
|
+
play(): void;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 暂停音频播放, 且音频当前播放时间不变
|
|
174
|
+
*/
|
|
175
|
+
pause(): void;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 当音频对象不再使用时候被调用
|
|
179
|
+
*/
|
|
180
|
+
destroy(): void;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 获取音频当前播放时间, 单位为:秒
|
|
184
|
+
*/
|
|
185
|
+
get currentTime(): number;
|
|
186
|
+
/**
|
|
187
|
+
* 设置音频当前播放时间, 单位为:秒。需注意, 无论音频是否正在播放, 都需要确保能设置成功。
|
|
188
|
+
* 如果音频暂停状态下, 设置此值, 那么需保证, 下次恢复播放是从此值位置开始播放。
|
|
189
|
+
*/
|
|
190
|
+
set currentTime(time: number);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 返回音频是否暂停状态
|
|
194
|
+
*/
|
|
195
|
+
get isPaused(): boolean;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 返回音频时长
|
|
199
|
+
*/
|
|
200
|
+
get duration(): number;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 当音频加载完成时触发, 例如: 音频 meta 数据加载完成, 这时候知道了音频实际时长, 就需要触发此事件, 需要保证此事件触发时,
|
|
204
|
+
* 能通过 duration 属性获取到更新后的音频时长
|
|
205
|
+
* @param event
|
|
206
|
+
* @param listener
|
|
207
|
+
*/
|
|
208
|
+
on(event: "load", listener: () => void): this;
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 当音频暂停时候触发
|
|
212
|
+
* @param event
|
|
213
|
+
* @param listener
|
|
214
|
+
*/
|
|
215
|
+
on(event: "pause", listener: () => void): this;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 当音频开始播放时候触发
|
|
219
|
+
* @param event
|
|
220
|
+
* @param listener
|
|
221
|
+
*/
|
|
222
|
+
on(event: "play", listener: () => void): this;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* 移除参数指定事件的所有监听器
|
|
226
|
+
*/
|
|
227
|
+
removeAllListeners(event: string): void;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface RtcAudioClazz {
|
|
231
|
+
/**
|
|
232
|
+
* 创建 rtc 播放器, url 为音频地址
|
|
233
|
+
* @param url
|
|
234
|
+
*/
|
|
235
|
+
new(url: string): RtcAudio;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
js 实现的自定义播放器示例代码可以[参考](./src/RtcAudioPlayer.js)。将自定义的播放器类传递给 `Slide` 的构造函数, 即可替换默认的音频播放器。
|
|
240
|
+
```typescript
|
|
241
|
+
import { Slide } from "@netless/slide";
|
|
242
|
+
|
|
243
|
+
const slide = new Slide({
|
|
244
|
+
anchor: someDivElement,
|
|
245
|
+
interactive: true,
|
|
246
|
+
mode: "local",
|
|
247
|
+
rtcAudio: RtcAudioPlayer,
|
|
248
|
+
});
|
|
47
249
|
```
|
|
48
250
|
|
|
49
|
-
|
|
251
|
+
对于 mp3 文件, `@netless/slide` 直接调用自定义的播放器播放音频。
|
|
252
|
+
|
|
253
|
+
对于 mp4 文件, 转码服务已经将 mp4 的音频单独提取出一个 mp3 文件, `@netless/slide` 将 mp4 静音, 同时用提供的自定义播放器播放对应的 mp3。
|
|
254
|
+
|
|
255
|
+
### 错误处理与日志
|
|
256
|
+
|
|
257
|
+
`@netless/slide@0.3.3` 版本开始, 会捕获当前页面的所有错误, 并通过 `SLIDE_EVENTS.renderError` 事件通知出来,
|
|
258
|
+
你可以在此事件的回调函数里跳转到下一页.
|
|
259
|
+
|
|
260
|
+
#### 错误类型说明
|
|
261
|
+
|
|
262
|
+
`@netless/slide` 导出有 `ErrorType` 枚举类型, 指示了 `SLIDE_EVENTS.renderError` 事件对应的错误类型,说明如下
|
|
263
|
+
|
|
264
|
+
| 名称 | 触发时机 | 恢复手段 |
|
|
265
|
+
| ---- | ---- | --- |
|
|
266
|
+
| ResourceError | 在 ppt 依赖的远程资源(json,png)不可用时触发, 触发后当前页无法交互 | 重新渲染当前页或者跳转下一页 |
|
|
267
|
+
| RuntimeError | 未知的异常, 触发后当前页无法交互 | 跳转下一页 |
|
|
268
|
+
| RuntimeWarn | 未知的警告, 在动画过程中出现,触发后动画当前帧表现异常,不影响下一帧和页面交互 | 无需特殊处理 |
|
|
269
|
+
|CanvasCrash| 由于内存不足,或者 canvas 被意外的移除(没有调用 slide.destroy() 的情况下移除 canvas 元素为意外移除), 触发后 canvas 元素白屏 | 刷新网页 |
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { SLIDE_EVENTS, ErrorType } from "@netless/slide";
|
|
273
|
+
|
|
274
|
+
// SlideError 继承自 Error, 除了 message, stack 等属性外
|
|
275
|
+
// 还添加了 errorType 及 errorMsg 属性,
|
|
276
|
+
interface SlideError extends Error {
|
|
277
|
+
errorType: ErrorType;
|
|
278
|
+
errorMsg: string;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
slide.on(SLIDE_EVENTS.renderError, (err: SlideError, index: number) => {
|
|
282
|
+
console.log(`第 ${index} 页出错`);
|
|
283
|
+
if (err.errorType === ErrorType.ResourceError) {
|
|
284
|
+
// 跳转到下一页, 可以根据具体需求选择如何恢复, 例如弹窗确认后再做跳转动作
|
|
285
|
+
slide.renderSlide(index + 1);
|
|
286
|
+
} else if (err.errorType === ErrorType.CanvasCrash) {
|
|
287
|
+
// 需要刷新页面
|
|
288
|
+
} else if (err.errorType === ErrorType.RuntimeError) {
|
|
289
|
+
// 跳转到下一页
|
|
290
|
+
slide.renderSlide(index + 1);
|
|
291
|
+
} else if (err.errorType === ErrorType.RuntimeWarn) {
|
|
292
|
+
// 无需特殊处理, 可以记录日志
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 日志
|
|
298
|
+
|
|
299
|
+
`@netless/slide@0.3.3` 版本开始, `ISlideConfig` 中添加了可选的 logger 属性, 需要传入一个符合如下接口的对象
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
interface ILogger {
|
|
303
|
+
info?(msg: string): void;
|
|
304
|
+
error?(msg: string): void;
|
|
305
|
+
warn?(msg: string): void;
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
如此便可以接收 ppt 运行日志.
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
import { Slide } from "@netless/slide";
|
|
313
|
+
|
|
314
|
+
const slide = new Slide({
|
|
315
|
+
anchor: someDivElement,
|
|
316
|
+
interactive: true,
|
|
317
|
+
mode: "local",
|
|
318
|
+
logger: {
|
|
319
|
+
info(msg: string) {
|
|
320
|
+
console.log(msg);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### webgl 上限文限制
|
|
327
|
+
|
|
328
|
+
浏览器一般会限制 webgl 上下文数量在 8 到 16 个之间, 对于 `@netless/slide` 来说, 一个活动的 Slide 实例占用两个上下文, 一个负责 2D 渲染一个负责 3D 渲染.
|
|
329
|
+
如果你创建的 Slide 超过了浏览器限制, 那么前面创建的 Slide 将丢失 webgl 上下文, 导致渲染异常.
|
|
330
|
+
|
|
331
|
+
**活动的 Slide 实例** 即指没有调用过 `slideInstance.frozen()` 方法的实例, 它的 webgl 绘制环境可以正常工作. 如果你想冻结当前 Slide 实例,将 webgl 上下文留给新创建的 Slide 对象, 就可以
|
|
332
|
+
调用 `slideInstance.frozen()`, 这个方法会将当前 ppt 画面截图, 并且保存 ppt 状态, 然后销毁 canvas 元素, 并用截到的图片替代 canvas 元素。冻结之后, Slide 对象的任意切页、上(下)一步 等操作都将失效.
|
|
333
|
+
|
|
334
|
+
调用 `slideInstance.release()` 可以将 Slide 对象从冻结状态恢复.
|
|
335
|
+
|
|
336
|
+
你需要自己控制活动的 Slide 实例的数量 在 4 到 8 之间, 一般建议 pc 上控制在 8 以下, 移动端控制在 4 以下.
|
|
337
|
+
|
|
338
|
+
关于控制活动 ppt 的建议:
|
|
339
|
+
1. 通过 [Page_Visibility_API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) 监听页面可见性, 在页面不可见的时候, 将 Slide 对象冻结,并在页面恢复的时候将 Slide 解冻.
|
|
340
|
+
2. 如果同一个页面内有多个活动 PPT, 可以设置一个固定长度的活动 PPT 队列, 将 当前获取焦点的 Slide 激活并推入队列, 将被挤出队列的 Slide 冻结.
|
|
341
|
+
|
|
342
|
+
### 本地缓存管理
|
|
343
|
+
|
|
344
|
+
**注意: `@netless/slide@0.3.6` 版本才开始支持。**
|
|
345
|
+
|
|
346
|
+
`@netless/slide` 使用 indexDB 缓存网络资源及临时生成的纹理. `@netless/slide` 不负责缓存数据的清理, 你需要在适当的时候来清理这部分数据.
|
|
347
|
+
|
|
348
|
+
和清理缓存相关的两个 api, 实例方法 clearSlideCache 和静态方法 clearLocalCache, 前者清理当前 ppt 缓存, 后者清理所有缓存. 需要注意, clearSlideCache 需要在调用 slide.destroy 之前调用, 否则不能完成清理工作.
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
/**
|
|
352
|
+
* 销毁当前 Slide 实例的本地缓存, 需要在 destroy 之前调用。
|
|
353
|
+
*/
|
|
354
|
+
clearSlideCache(): void;
|
|
355
|
+
/**
|
|
356
|
+
* 销毁历史所有本地缓存
|
|
357
|
+
*/
|
|
358
|
+
static clearLocalCache(): void;
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## [示例及具体文档参考](https://github.com/netless-io/netless-slide-demo)
|
|
362
|
+
|
|
363
|
+
## changelog
|
|
364
|
+
|
|
365
|
+
#### 0.1.5
|
|
366
|
+
* 文字销毁处理
|
|
367
|
+
* 实现点击动画的同步
|
|
368
|
+
|
|
369
|
+
#### 0.1.28
|
|
370
|
+
* 切页动画可配置背景色, ISlideRenderOptions.transactionBgColor
|
|
371
|
+
* 修复图片模糊问题
|
|
372
|
+
|
|
373
|
+
#### 0.1.32
|
|
374
|
+
* slide 添加 frozen 和 release 方法, 可以释放 webgl 上下文并缓存为图片
|
|
375
|
+
* 修复图片有一像素白边问题
|
|
376
|
+
* 修复动画音效重复播放问题
|
|
377
|
+
|
|
378
|
+
#### 0.1.34
|
|
379
|
+
* 支持 ppt 隐藏页
|
|
380
|
+
* 修复冻结图片 zIndex 太高的问题
|
|
381
|
+
|
|
382
|
+
#### 0.1.35
|
|
383
|
+
* 修复媒体控制栏位置错误
|
|
384
|
+
* 修复 0 高度 GroupShape 不可见问题
|
|
385
|
+
* 修复动画快速切换造成状态错乱
|
|
386
|
+
* 修复带音频的动画崩溃问题
|
|
387
|
+
|
|
388
|
+
#### 0.1.36
|
|
389
|
+
* 修复无地址视频造成崩溃
|
|
390
|
+
* 修复空格的下划线不显示问题
|
|
391
|
+
* 修复 gif 速度过快 (delay 列表为 0)
|
|
392
|
+
* 修复 layout 和 master 中的页码占位符显示错误
|
|
393
|
+
|
|
394
|
+
#### 0.1.38
|
|
395
|
+
* 修复下划线显示问题
|
|
396
|
+
* 修复竖排文字显示问题
|
|
397
|
+
* 修复媒体资源有问题引起的页面崩溃
|
|
398
|
+
|
|
399
|
+
#### 0.1.39
|
|
400
|
+
* 修复横排文字显示不全的问题
|
|
401
|
+
|
|
402
|
+
#### 0.1.40
|
|
403
|
+
* 修复动画公式解析报错
|
|
404
|
+
* 添加预加载逻辑
|
|
405
|
+
|
|
406
|
+
#### 0.1.41
|
|
407
|
+
* 添加版本号信息到 slide 对象
|
|
408
|
+
|
|
409
|
+
#### 0.1.45
|
|
410
|
+
* 修复 ios canvas2d 上下文无法回收问题
|
|
411
|
+
* 修复 ios 显存爆炸问题
|
|
412
|
+
|
|
413
|
+
#### 0.1.47
|
|
414
|
+
* 修复首次插入 ppt 看不见的问题
|
|
415
|
+
* 修复 ios 内存爆炸问题
|
|
416
|
+
|
|
417
|
+
#### 0.1.48
|
|
418
|
+
* 修复切页动画无法同步
|
|
419
|
+
|
|
420
|
+
#### 0.1.53
|
|
421
|
+
* 修复 Shape 无法点击
|
|
422
|
+
* 修复 pixijs 某些 RenderTexture 没有销毁
|
|
423
|
+
|
|
424
|
+
#### 0.1.54
|
|
425
|
+
* 修复 y 轴坐标为负的文字显示不全
|
|
426
|
+
|
|
427
|
+
#### 0.1.55
|
|
428
|
+
* ipad 及 iphone 2017 年之前设备不支持切页动画
|
|
429
|
+
|
|
430
|
+
#### 0.1.56
|
|
431
|
+
* 修复 ios 声音播放错乱的问题
|
|
432
|
+
|
|
433
|
+
#### 0.1.57
|
|
434
|
+
* 降低内存占用
|
|
435
|
+
* 修复平铺背景显示不正确
|
|
436
|
+
* 修复 tab 键无法显示下划线
|
|
437
|
+
|
|
438
|
+
#### 0.2.1
|
|
439
|
+
* 滤镜类动画支持 tmFilter 属性
|
|
440
|
+
* 文字图片提供两套(带颜色和不带)
|
|
441
|
+
|
|
442
|
+
#### 0.2.2
|
|
443
|
+
* 添加 enableGlobalClick 参数, 支持点击 ppt 页面执行下一步
|
|
444
|
+
* 修复切页动画过程中关闭 ppt 会报错
|
|
445
|
+
* 修复 0 高图形的动画不显示问题
|
|
446
|
+
* 修复多个颜色动画衔接不正确的问题
|
|
447
|
+
* 动画支持 xshear 和 yshear 属性
|
|
448
|
+
|
|
449
|
+
#### 0.2.3
|
|
450
|
+
* 修复字动画引起的 crash
|
|
451
|
+
* 修复若干显示 bug, ppt 还原更准确
|
|
452
|
+
* 修复 ios 上 enableGlobalClick 无效的问题
|
|
453
|
+
|
|
454
|
+
#### 0.2.4
|
|
455
|
+
* player controller 增加更多信息
|
|
456
|
+
|
|
457
|
+
#### 0.2.5
|
|
458
|
+
* 修复 slide 销毁后还有残余 dom 元素没有移除
|
|
459
|
+
|
|
460
|
+
#### 0.2.6
|
|
461
|
+
* 修复 interactive 配置对上下步不生效的问题
|
|
462
|
+
|
|
463
|
+
#### 0.2.8
|
|
464
|
+
* 新增 rtc 混音机制支持
|
|
465
|
+
* 修复 interactiveSeq 动画同步状态错误
|
|
466
|
+
|
|
467
|
+
#### 0.2.9
|
|
468
|
+
* 修复 slide 销毁后, MP4 的 rtc 音频没有停止的问题
|
|
469
|
+
|
|
470
|
+
#### 0.2.10
|
|
471
|
+
* 修复字体下划线图片错误的添加到了 document.body 下
|
|
472
|
+
|
|
473
|
+
#### 0.2.11
|
|
474
|
+
* 新增 maxFPS 配置对切页动画也生效
|
|
475
|
+
|
|
476
|
+
#### 0.3.2
|
|
477
|
+
* 新增错误处理流程
|
|
478
|
+
* 新增日志 api
|
|
479
|
+
|
|
480
|
+
#### 0.3.3
|
|
481
|
+
* 修复加载资源时销毁 slide 会报错
|
|
482
|
+
|
|
483
|
+
#### 0.3.4
|
|
484
|
+
* 完善日志
|
|
485
|
+
* 本地缓存获取超时后走网络请求
|
|
50
486
|
|
|
51
|
-
|
|
487
|
+
#### 0.3.6
|
|
488
|
+
* 修复获取本地缓存一直挂起的问题
|
|
489
|
+
* 添加缓存处理 api, slideInstance.clearSlideCache 和 Slide.clearLocalCache
|
|
52
490
|
|
|
53
|
-
|
|
491
|
+
#### 0.3.7
|
|
492
|
+
* 修复路径动画上一步位置不正确的问题
|
|
493
|
+
* Slide.destroy 方法里添加移除所有事件监听器的逻辑
|
|
494
|
+
* 修复触发器动画切页后没有重置 slideState 的问题
|
|
54
495
|
|
|
55
|
-
|
|
496
|
+
#### 0.3.8
|
|
497
|
+
* subtask 错误添加子任务类型输出,方便定位 bug
|
package/lib/Lock.d.ts
CHANGED
package/lib/Lock.js
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
var Lock = /** @class */ (function () {
|
|
2
|
-
function Lock() {
|
|
2
|
+
function Lock(available) {
|
|
3
3
|
this.autoUnlock = Object.create(null);
|
|
4
4
|
this.locks = Object.create(null);
|
|
5
|
+
this.available = false;
|
|
6
|
+
this.available = available;
|
|
5
7
|
}
|
|
6
8
|
Lock.prototype.addLock = function (type, key) {
|
|
7
9
|
var _this = this;
|
|
10
|
+
if (!this.available) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
8
13
|
this.locks[type] = key;
|
|
9
14
|
this.autoUnlock[type] = window.setTimeout(function () {
|
|
10
15
|
delete _this.locks[type];
|
|
11
16
|
}, 3000);
|
|
12
17
|
};
|
|
13
18
|
Lock.prototype.unlock = function (type, key) {
|
|
19
|
+
if (!this.available) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
14
22
|
if (key && this.locks[type] && this.locks[type] === key) {
|
|
15
23
|
window.clearTimeout(this.autoUnlock[type]);
|
|
16
24
|
delete this.locks[type];
|
|
17
25
|
}
|
|
18
26
|
};
|
|
19
27
|
Lock.prototype.isLocked = function (type) {
|
|
28
|
+
if (!this.available) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
20
31
|
return !!this.locks[type];
|
|
21
32
|
};
|
|
22
33
|
return Lock;
|
package/lib/Slide.d.ts
CHANGED
|
@@ -72,6 +72,10 @@ export declare const SLIDE_EVENTS: {
|
|
|
72
72
|
readonly syncDispatch: "syncDispatch";
|
|
73
73
|
/** 同步事件接收 */
|
|
74
74
|
readonly syncReceive: "syncReceive";
|
|
75
|
+
/**
|
|
76
|
+
* 同步模式下才会触发这个事件, 指示同步消息滞后, 需要全量同步
|
|
77
|
+
*/
|
|
78
|
+
readonly syncEventLag: "syncEventLag";
|
|
75
79
|
/** 当前 slide 渲染开始触发 */
|
|
76
80
|
readonly renderStart: "renderStart";
|
|
77
81
|
/** 当前 slide 渲染完毕触发 */
|
|
@@ -176,6 +180,7 @@ export declare type SyncEvent = SyncInteractiveAnimEvent | SyncPrevStepEvent | S
|
|
|
176
180
|
export interface BasicSyncEvent {
|
|
177
181
|
slideIndex: number;
|
|
178
182
|
uuid?: string;
|
|
183
|
+
incrId?: number;
|
|
179
184
|
}
|
|
180
185
|
export interface SyncMediaPlayEvent extends BasicSyncEvent {
|
|
181
186
|
type: "mediaPlay";
|
|
@@ -217,6 +222,7 @@ export interface SyncSetResourceEvent extends BasicSyncEvent {
|
|
|
217
222
|
url: string;
|
|
218
223
|
}
|
|
219
224
|
export interface SlideEventEmitter {
|
|
225
|
+
removeAllListeners(): this;
|
|
220
226
|
removeListener(event: keyof typeof SLIDE_EVENTS, fn?: any): this;
|
|
221
227
|
emit(event: typeof SLIDE_EVENTS.renderStart, index: number): boolean;
|
|
222
228
|
on(event: typeof SLIDE_EVENTS.renderStart, listener: (index: number) => void): this;
|
|
@@ -246,6 +252,8 @@ export interface SlideEventEmitter {
|
|
|
246
252
|
on(event: typeof SLIDE_EVENTS.mainSeqStepEnd, listener: (index: number) => void): this;
|
|
247
253
|
emit(event: typeof SLIDE_EVENTS.stateChange, state: ISlideState): boolean;
|
|
248
254
|
on(event: typeof SLIDE_EVENTS.stateChange, listener: (state: ISlideState) => void): this;
|
|
255
|
+
emit(event: typeof SLIDE_EVENTS.syncEventLag): boolean;
|
|
256
|
+
on(event: typeof SLIDE_EVENTS.syncEventLag, listener: () => void): this;
|
|
249
257
|
}
|
|
250
258
|
declare const Slide_base: new () => SlideEventEmitter;
|
|
251
259
|
export declare class Slide extends Slide_base {
|
|
@@ -286,6 +294,10 @@ export declare class Slide extends Slide_base {
|
|
|
286
294
|
private designWidth;
|
|
287
295
|
private designHeight;
|
|
288
296
|
private _slideCount;
|
|
297
|
+
private logger;
|
|
298
|
+
private _dispatchIncrId;
|
|
299
|
+
private _receiveIncrId;
|
|
300
|
+
private get dispatchIncrId();
|
|
289
301
|
constructor(config: ISlideConfig);
|
|
290
302
|
private initPlayer;
|
|
291
303
|
private userInputHandle;
|