@netless/slide 1.4.55 → 1.4.57
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 +8 -30
- package/lib/FrozenMediaState.d.ts +13 -0
- package/lib/FrozenMediaState.js +18 -0
- package/lib/RenderSlideVisibility.d.ts +20 -0
- package/lib/RenderSlideVisibility.js +16 -0
- package/lib/RenderingTaskManager.js +76 -4
- package/lib/Slide.d.ts +6 -0
- package/lib/Slide.js +28 -28
- package/lib/Slide.js.map +1 -0
- package/lib/typeGuard/SyncEvent.d.ts +10 -0
- package/lib/typeGuard/SyncEvent.js +27 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,39 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## [示例及具体文档参考](https://github.com/netless-io/netless-slide-demo)
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
`@netless/slide` 默认按 FIFO 顺序执行所有接收到的同步事件。对于需要在弱网积压时优先追赶最新页面的 interactive 客户端,可以显式启用 `latest-pending-render`;可写端和只读端都可以使用:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
const slide = new Slide({
|
|
11
|
-
// 其他配置
|
|
12
|
-
syncEventQueuePolicy: "latest-pending-render",
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
slide.setSyncEventQueuePolicy("fifo");
|
|
16
|
-
slide.setSyncEventQueuePolicy("latest-pending-render");
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
该策略只处理尚未开始执行的 pending 事件:
|
|
20
|
-
|
|
21
|
-
- 新的 `renderSlide` 到达时,删除同一资源段内排在它之前的 pending 翻页、`nextStep`、`prevStep`、交互动画和媒体操作。
|
|
22
|
-
- 保留最新 `renderSlide`,以及它之后到达的所有操作,并继续按 FIFO 执行。
|
|
23
|
-
- `setResource` 和未知事件是硬边界,不会被删除,也不会跨越边界压缩。
|
|
24
|
-
- 已经开始执行的事件不会被取消;该策略不保证完全不显示 running 中间页。
|
|
25
|
-
- 动态切换策略本身不会清空或重排已有队列;切换后新到达的 `renderSlide` 才会触发压缩。
|
|
26
|
-
- latest 策略下开始执行远端 `renderSlide`,或远端事件形成 pending 积压后,Slide 会暂时阻止本地 `nextStep()`、`prevStep()`、`renderSlide()`、`doRenderSlide()` 和画布交互;同步接收、状态恢复和队列执行不受影响。本机事件优先通过现有 Lock 的 UUID 回包识别,并以 `clientId` 作为补充,不会锁住正在操作的当前控制者。
|
|
27
|
-
- 临时输入锁在同步队列重新空闲后解除,并恢复调用方最近一次通过 `setInteractive()` 设置的权限;不会把原本的只读端恢复成可写。
|
|
28
|
-
|
|
29
|
-
该能力不会根据 `interactive` 自动开启。调用方应通过自己的灰度开关显式选择;关闭策略时可以恢复为 `fifo`,不会修改调用方原有的本地交互权限。
|
|
30
|
-
|
|
31
|
-
该策略只在 Slide 的 `mode: "interactive"` 下生效;`sync`、`local` 和 `addLink` mode 会强制使用 `fifo`。这项 mode 保护不能替代调用方的只读权限与 feature flag 判断。
|
|
5
|
+
## changelog
|
|
32
6
|
|
|
33
|
-
|
|
7
|
+
### 1.4.57 (2026-08-27)
|
|
8
|
+
* 支持在客户端解码并播放本地 GIF 资源,兼容旧版 Safari
|
|
9
|
+
* 修复 WebGL 切页转场被错误关闭的问题
|
|
34
10
|
|
|
35
|
-
|
|
11
|
+
### 1.4.56 (2026-08-25)
|
|
12
|
+
* 修复冻结 (frozen) 恢复 (release) 时画面闪烁问题
|
|
13
|
+
* 完善 Canvas/WebGL 播放器销毁清理,避免状态切换后残留多个 canvas
|
|
36
14
|
|
|
37
|
-
###
|
|
15
|
+
### 1.4.55 (2026-08-21)
|
|
38
16
|
* 增加 `latest-pending-render` 同步队列策略,弱网积压时丢弃最后翻页前尚未执行的中间页面操作,并在追赶期间阻止本地输入
|
|
39
17
|
* 修复 PPT 图形内阴影(`innerShadow`)被错误渲染为不透明黑边的问题
|
|
40
18
|
* 按转换数据中的颜色、模糊半径、距离和方向渲染柔化内阴影
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface FrozenMediaPlaybackState {
|
|
2
|
+
type: "pause" | "play";
|
|
3
|
+
time: number;
|
|
4
|
+
}
|
|
5
|
+
export declare type FrozenMediaTimes = {
|
|
6
|
+
[key: string]: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function captureFrozenMediaTimes(mediaState: {
|
|
9
|
+
[key: string]: FrozenMediaPlaybackState;
|
|
10
|
+
}, frozenTime: number): FrozenMediaTimes;
|
|
11
|
+
export declare function applyFrozenMediaTimes(mediaState: {
|
|
12
|
+
[key: string]: FrozenMediaPlaybackState;
|
|
13
|
+
}, frozenMediaTimes: FrozenMediaTimes, releaseTime: number): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function captureFrozenMediaTimes(mediaState, frozenTime) {
|
|
2
|
+
var frozenMediaTimes = Object.create(null);
|
|
3
|
+
for (var key in mediaState) {
|
|
4
|
+
if (mediaState[key].type === "play") {
|
|
5
|
+
frozenMediaTimes[key] = frozenTime;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return frozenMediaTimes;
|
|
9
|
+
}
|
|
10
|
+
export function applyFrozenMediaTimes(mediaState, frozenMediaTimes, releaseTime) {
|
|
11
|
+
for (var key in mediaState) {
|
|
12
|
+
var media = mediaState[key];
|
|
13
|
+
var frozenTime = frozenMediaTimes[key];
|
|
14
|
+
if (media.type === "play" && frozenTime !== undefined) {
|
|
15
|
+
media.time = releaseTime - (Math.max(frozenTime, media.time) - media.time);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare type VisibilityStyle = {
|
|
2
|
+
visibility: string;
|
|
3
|
+
};
|
|
4
|
+
declare type DisplayStyle = {
|
|
5
|
+
display: string;
|
|
6
|
+
};
|
|
7
|
+
declare type RenderSlidePlayer = {
|
|
8
|
+
view?: {
|
|
9
|
+
style: VisibilityStyle;
|
|
10
|
+
} | null;
|
|
11
|
+
};
|
|
12
|
+
declare type RenderSlideElement = {
|
|
13
|
+
style: VisibilityStyle & DisplayStyle;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Reveal a completed live render and remove its fallback image.
|
|
17
|
+
* Frozen and release states own visibility while their player transition is active.
|
|
18
|
+
*/
|
|
19
|
+
export declare function updateRenderSlideVisibility(isReleasing: boolean, player: RenderSlidePlayer | undefined, frame: RenderSlideElement, cacheImage: RenderSlideElement): void;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reveal a completed live render and remove its fallback image.
|
|
3
|
+
* Frozen and release states own visibility while their player transition is active.
|
|
4
|
+
*/
|
|
5
|
+
export function updateRenderSlideVisibility(isReleasing, player, frame, cacheImage) {
|
|
6
|
+
if (isReleasing || !(player === null || player === void 0 ? void 0 : player.view)) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (player.view.style.visibility === "hidden") {
|
|
10
|
+
player.view.style.visibility = "visible";
|
|
11
|
+
}
|
|
12
|
+
if (frame.style.visibility === "hidden") {
|
|
13
|
+
frame.style.visibility = "visible";
|
|
14
|
+
}
|
|
15
|
+
cacheImage.style.display = "none";
|
|
16
|
+
}
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
var __extends = (this && this.__extends) || (function () {
|
|
2
|
+
var extendStatics = function (d, b) {
|
|
3
|
+
extendStatics = Object.setPrototypeOf ||
|
|
4
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
5
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
6
|
+
return extendStatics(d, b);
|
|
7
|
+
};
|
|
8
|
+
return function (d, b) {
|
|
9
|
+
if (typeof b !== "function" && b !== null)
|
|
10
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
11
|
+
extendStatics(d, b);
|
|
12
|
+
function __() { this.constructor = d; }
|
|
13
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
14
|
+
};
|
|
15
|
+
})();
|
|
1
16
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
17
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
18
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -35,11 +50,24 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
35
50
|
}
|
|
36
51
|
};
|
|
37
52
|
import EventEmitter from "eventemitter3";
|
|
53
|
+
var RenderTaskCancelledError = /** @class */ (function (_super) {
|
|
54
|
+
__extends(RenderTaskCancelledError, _super);
|
|
55
|
+
function RenderTaskCancelledError(message) {
|
|
56
|
+
if (message === void 0) { message = "render task cancelled"; }
|
|
57
|
+
var _this = _super.call(this, message) || this;
|
|
58
|
+
_this.code = "RENDER_TASK_CANCELLED";
|
|
59
|
+
_this.name = "AbortError";
|
|
60
|
+
return _this;
|
|
61
|
+
}
|
|
62
|
+
return RenderTaskCancelledError;
|
|
63
|
+
}(Error));
|
|
64
|
+
export { RenderTaskCancelledError };
|
|
38
65
|
var Task = /** @class */ (function () {
|
|
39
66
|
function Task(index, slideIndex, fn, eventHub, id) {
|
|
40
67
|
this.state = "idle";
|
|
41
68
|
this.index = -1;
|
|
42
69
|
this.slideIndex = -1;
|
|
70
|
+
this.cancelled = false;
|
|
43
71
|
this.fn = fn;
|
|
44
72
|
this.index = index;
|
|
45
73
|
this.slideIndex = slideIndex;
|
|
@@ -52,6 +80,9 @@ var Task = /** @class */ (function () {
|
|
|
52
80
|
return __generator(this, function (_a) {
|
|
53
81
|
switch (_a.label) {
|
|
54
82
|
case 0:
|
|
83
|
+
if (this.cancelled) {
|
|
84
|
+
return [2 /*return*/];
|
|
85
|
+
}
|
|
55
86
|
this.eventHub.emit("task-start", this);
|
|
56
87
|
_a.label = 1;
|
|
57
88
|
case 1:
|
|
@@ -60,11 +91,17 @@ var Task = /** @class */ (function () {
|
|
|
60
91
|
return [4 /*yield*/, this.fn()];
|
|
61
92
|
case 2:
|
|
62
93
|
_a.sent();
|
|
94
|
+
if (this.cancelled) {
|
|
95
|
+
return [2 /*return*/];
|
|
96
|
+
}
|
|
63
97
|
this.state = "end";
|
|
64
98
|
this.eventHub.emit("task-end", this);
|
|
65
99
|
return [3 /*break*/, 4];
|
|
66
100
|
case 3:
|
|
67
101
|
e_1 = _a.sent();
|
|
102
|
+
if (this.cancelled) {
|
|
103
|
+
return [2 /*return*/];
|
|
104
|
+
}
|
|
68
105
|
this.eventHub.emit("task-error", {
|
|
69
106
|
task: this,
|
|
70
107
|
error: e_1,
|
|
@@ -75,6 +112,9 @@ var Task = /** @class */ (function () {
|
|
|
75
112
|
});
|
|
76
113
|
});
|
|
77
114
|
};
|
|
115
|
+
Task.prototype.cancel = function () {
|
|
116
|
+
this.cancelled = true;
|
|
117
|
+
};
|
|
78
118
|
return Task;
|
|
79
119
|
}());
|
|
80
120
|
export { Task };
|
|
@@ -84,6 +124,7 @@ var RenderingTaskManager = /** @class */ (function () {
|
|
|
84
124
|
this.eventHub = new EventEmitter();
|
|
85
125
|
this.tasks = [];
|
|
86
126
|
this.index = 0;
|
|
127
|
+
this.destroyed = false;
|
|
87
128
|
this.eventHub.on("task-end", function (task) {
|
|
88
129
|
var _a;
|
|
89
130
|
var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
|
|
@@ -98,15 +139,28 @@ var RenderingTaskManager = /** @class */ (function () {
|
|
|
98
139
|
_this.eventHub.emit("task-end-" + task.id);
|
|
99
140
|
});
|
|
100
141
|
this.eventHub.on("task-error", function (_a) {
|
|
101
|
-
var task = _a.task;
|
|
142
|
+
var task = _a.task, error = _a.error;
|
|
102
143
|
var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
|
|
103
144
|
if (selfIndex >= 0) {
|
|
104
145
|
_this.tasks.splice(selfIndex, 1);
|
|
105
146
|
_this.replaceIdleTask();
|
|
106
147
|
}
|
|
107
|
-
_this.eventHub.emit("task-error-" + task.id);
|
|
148
|
+
_this.eventHub.emit("task-error-" + task.id, error);
|
|
149
|
+
});
|
|
150
|
+
this.eventHub.on("task-cancel", function (_a) {
|
|
151
|
+
var task = _a.task, error = _a.error;
|
|
152
|
+
var selfIndex = _this.tasks.findIndex(function (t) { return t.index === task.index; });
|
|
153
|
+
if (selfIndex >= 0) {
|
|
154
|
+
_this.tasks.splice(selfIndex, 1);
|
|
155
|
+
}
|
|
156
|
+
_this.eventHub.emit("task-cancel-" + task.id, error);
|
|
108
157
|
});
|
|
109
158
|
}
|
|
159
|
+
RenderingTaskManager.prototype.cancelTask = function (task) {
|
|
160
|
+
var error = new RenderTaskCancelledError();
|
|
161
|
+
task.cancel();
|
|
162
|
+
this.eventHub.emit("task-cancel", { task: task, error: error });
|
|
163
|
+
};
|
|
110
164
|
RenderingTaskManager.prototype.replaceIdleTask = function () {
|
|
111
165
|
var _this = this;
|
|
112
166
|
var _a;
|
|
@@ -116,11 +170,19 @@ var RenderingTaskManager = /** @class */ (function () {
|
|
|
116
170
|
ids.add(i);
|
|
117
171
|
}
|
|
118
172
|
}
|
|
119
|
-
|
|
120
|
-
|
|
173
|
+
// Remove from the end so deleting one idle task does not shift the
|
|
174
|
+
// indexes selected for the remaining cancellations.
|
|
175
|
+
Array.from(ids).sort(function (a, b) { return b - a; }).forEach(function (id) {
|
|
176
|
+
var task = _this.tasks.splice(id, 1)[0];
|
|
177
|
+
if (task) {
|
|
178
|
+
_this.cancelTask(task);
|
|
179
|
+
}
|
|
121
180
|
});
|
|
122
181
|
};
|
|
123
182
|
RenderingTaskManager.prototype.addTask = function (fn, slideIndex, id) {
|
|
183
|
+
if (this.destroyed) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
124
186
|
var task = new Task(this.index++, slideIndex, fn, this.eventHub, id);
|
|
125
187
|
this.tasks.push(task);
|
|
126
188
|
this.tasks.sort(function (a, b) { return a.index - b.index; });
|
|
@@ -128,11 +190,21 @@ var RenderingTaskManager = /** @class */ (function () {
|
|
|
128
190
|
if (this.tasks.length === 1) {
|
|
129
191
|
task.apply();
|
|
130
192
|
}
|
|
193
|
+
return task;
|
|
131
194
|
};
|
|
132
195
|
RenderingTaskManager.prototype.hasStartTask = function () {
|
|
133
196
|
return this.tasks.some(function (t) { return t.state === "start"; });
|
|
134
197
|
};
|
|
135
198
|
RenderingTaskManager.prototype.destroy = function () {
|
|
199
|
+
var _this = this;
|
|
200
|
+
if (this.destroyed) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
this.destroyed = true;
|
|
204
|
+
var tasks = this.tasks.splice(0);
|
|
205
|
+
tasks.forEach(function (task) {
|
|
206
|
+
_this.cancelTask(task);
|
|
207
|
+
});
|
|
136
208
|
this.eventHub.removeAllListeners();
|
|
137
209
|
};
|
|
138
210
|
return RenderingTaskManager;
|
package/lib/Slide.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { AliTrackLogger } from "./AliTrackLogger";
|
|
|
5
5
|
import { preloadResource } from "./global";
|
|
6
6
|
export { ErrorType, PreloadCancelledError } from "@netless/ppt-player";
|
|
7
7
|
export type { SyncEventQueuePolicy } from "./SyncTaskManager";
|
|
8
|
+
export declare const LOCAL_GIF_RUNTIME_CAPABILITY = "netless-slide-local-gif-production-client-geometry";
|
|
8
9
|
export declare type UrlInterrupter = (url: string) => Promise<string>;
|
|
9
10
|
export interface ILoaderDelegate {
|
|
10
11
|
/**
|
|
@@ -179,6 +180,10 @@ export interface ISlideRenderOptions {
|
|
|
179
180
|
*/
|
|
180
181
|
transitionResolutionLevel?: number;
|
|
181
182
|
antialias?: boolean;
|
|
183
|
+
/** Maximum compressed bytes accepted for one client-decoded GIF. */
|
|
184
|
+
localGifMaxCompressedBytes?: number;
|
|
185
|
+
/** Maximum estimated GIF working set for one cached slide. */
|
|
186
|
+
localGifMaxWorkingSetBytes?: number;
|
|
182
187
|
}
|
|
183
188
|
export interface INavigatorDelegate {
|
|
184
189
|
gotoPage?: (index: number) => void;
|
|
@@ -452,6 +457,7 @@ export declare class Slide extends Slide_base {
|
|
|
452
457
|
private isAnimating;
|
|
453
458
|
private renderingTaskManager;
|
|
454
459
|
private isLoading;
|
|
460
|
+
private isReleasing;
|
|
455
461
|
private interactive;
|
|
456
462
|
private syncCatchUpInputBlocked;
|
|
457
463
|
private anchor;
|