@netless/app-presentation 0.1.1 → 0.1.3
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.d.ts +42 -10
- package/dist/index.global.js +245 -88
- package/dist/index.js +245 -88
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +245 -88
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -15
- package/src/app-presentation.ts +77 -20
- package/src/preload.ts +154 -53
- package/src/presentation.ts +16 -8
package/src/preload.ts
CHANGED
|
@@ -1,68 +1,169 @@
|
|
|
1
1
|
import type { IDisposable } from "@wopjs/disposable";
|
|
2
2
|
import type { PresentationPage } from "./presentation";
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
count = 0
|
|
4
|
+
export enum ELoadState {
|
|
5
|
+
unloaded,
|
|
6
|
+
loading,
|
|
7
|
+
loaded,
|
|
8
|
+
error
|
|
9
|
+
}
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export interface IPreloadMapValue {
|
|
12
|
+
src: string
|
|
13
|
+
state: ELoadState
|
|
14
|
+
}
|
|
15
15
|
|
|
16
|
+
export class Preload implements IDisposable {
|
|
17
|
+
static maxLinks: number = 5;
|
|
18
|
+
readonly loadingLinks: Map<number, HTMLLinkElement> = new Map();
|
|
19
|
+
readonly preloadMap = new Map<number, IPreloadMapValue>();
|
|
20
|
+
readonly waitingLoadSet: Set<number> = new Set();
|
|
21
|
+
readonly waitinglinks: number[] = [];
|
|
22
|
+
touchIndex: number = 0;
|
|
23
|
+
preloadSize: number = 0;
|
|
16
24
|
constructor(readonly pages: PresentationPage[]) {
|
|
17
|
-
this.
|
|
18
|
-
|
|
25
|
+
this.preloadMap = new Map(pages.map((e,index) => [index, {
|
|
26
|
+
src: e.src,
|
|
27
|
+
state: ELoadState.unloaded
|
|
28
|
+
}]));
|
|
29
|
+
this.waitingLoadSet = new Set(pages.map((e,index) => index));
|
|
30
|
+
this.preloadSize = this.preloadMap.size;
|
|
31
|
+
this.touch(0);
|
|
19
32
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
async touch(index: number): Promise<ELoadState> {
|
|
34
|
+
this.touchIndex = index;
|
|
35
|
+
const value = this.preloadMap.get(index);
|
|
36
|
+
let state = ELoadState.unloaded;
|
|
37
|
+
if (value) {
|
|
38
|
+
state = value.state;
|
|
39
|
+
if (value.state === ELoadState.unloaded) {
|
|
40
|
+
if (this.loadingLinks.size) {
|
|
41
|
+
for (const [i,link] of this.loadingLinks) {
|
|
42
|
+
this.destroyLink(i, link);
|
|
43
|
+
this.waitingLoadSet.add(i);
|
|
44
|
+
const v = this.preloadMap.get(i);
|
|
45
|
+
if (v) {
|
|
46
|
+
v.state = ELoadState.unloaded;
|
|
47
|
+
this.preloadMap.set(i, v);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const i of this.waitinglinks) {
|
|
51
|
+
this.waitingLoadSet.add(i);
|
|
52
|
+
}
|
|
53
|
+
this.waitinglinks.length = 0;
|
|
54
|
+
}
|
|
55
|
+
if (this.waitinglinks.includes(index)) {
|
|
56
|
+
this.waitinglinks.splice(this.waitinglinks.indexOf(index), 1);
|
|
57
|
+
}
|
|
58
|
+
state = await new Promise<ELoadState>(resolve => {
|
|
59
|
+
this.waitingLoadSet.delete(index);
|
|
60
|
+
this.createLink(index, resolve);
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (this.waitingLoadSet.size) {
|
|
65
|
+
this.requestAsyncCallBack(()=>{
|
|
66
|
+
this.autoTouch();
|
|
67
|
+
}, 1000)
|
|
68
|
+
}
|
|
69
|
+
return state;
|
|
25
70
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
while (this.head > 0 && links[this.head]) this.head--
|
|
31
|
-
if (!links[this.head]) {
|
|
32
|
-
const link = document.createElement('link')
|
|
33
|
-
links[this.head] = link
|
|
34
|
-
link.rel = 'preload'
|
|
35
|
-
link.as = 'image'
|
|
36
|
-
link.href = this.pages[this.head].src
|
|
37
|
-
link.dataset.order = this.count + ''
|
|
38
|
-
document.head.appendChild(link)
|
|
39
|
-
this.count++
|
|
71
|
+
private autoTouch(){
|
|
72
|
+
this.handler();
|
|
73
|
+
if (this.waitingLoadSet.size === 0) {
|
|
74
|
+
return;
|
|
40
75
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
let nextTouchDelay = 1000;
|
|
77
|
+
this.touchIndex = (this.touchIndex + 1) % this.preloadSize;
|
|
78
|
+
const value = this.preloadMap.get(this.touchIndex);
|
|
79
|
+
if (value) {
|
|
80
|
+
switch (value.state) {
|
|
81
|
+
case ELoadState.unloaded:
|
|
82
|
+
case ELoadState.error:
|
|
83
|
+
if (!this.waitinglinks.includes(this.touchIndex)) {
|
|
84
|
+
this.waitinglinks.push(this.touchIndex);
|
|
85
|
+
this.waitingLoadSet.delete(this.touchIndex);
|
|
86
|
+
}
|
|
87
|
+
nextTouchDelay = 0;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
52
90
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (this.count >= 10) for (const link of this.links)
|
|
58
|
-
if (link && link.dataset.order && +link.dataset.order < this.count - 10)
|
|
59
|
-
document.head.contains(link) && document.head.removeChild(link)
|
|
91
|
+
this.requestAsyncCallBack(()=>{
|
|
92
|
+
this.autoTouch();
|
|
93
|
+
}, nextTouchDelay)
|
|
60
94
|
}
|
|
61
95
|
|
|
96
|
+
private createLink(index: number, resolve?:(state:ELoadState)=>void) {
|
|
97
|
+
const value = this.preloadMap.get(index);
|
|
98
|
+
if (value) {
|
|
99
|
+
const link = document.createElement('link');
|
|
100
|
+
link.rel = 'preload';
|
|
101
|
+
link.as = 'image';
|
|
102
|
+
link.href = value.src;
|
|
103
|
+
link.dataset.order = index + '';
|
|
104
|
+
document.head.appendChild(link);
|
|
105
|
+
link.onload = () => {
|
|
106
|
+
value.state = ELoadState.loaded;
|
|
107
|
+
this.preloadMap.set(index, value);
|
|
108
|
+
document.head.contains(link) && document.head.removeChild(link);
|
|
109
|
+
this.loadingLinks.delete(index);
|
|
110
|
+
resolve && resolve(ELoadState.loaded);
|
|
111
|
+
this.autoTouch();
|
|
112
|
+
}
|
|
113
|
+
link.onerror = () => {
|
|
114
|
+
value.state = ELoadState.error;
|
|
115
|
+
this.preloadMap.set(index, value);
|
|
116
|
+
if (!this.waitinglinks.includes(index)) {
|
|
117
|
+
this.waitinglinks.push(index);
|
|
118
|
+
this.waitingLoadSet.delete(this.touchIndex);
|
|
119
|
+
}
|
|
120
|
+
document.head.contains(link) && document.head.removeChild(link);
|
|
121
|
+
this.loadingLinks.delete(index);
|
|
122
|
+
resolve && resolve(ELoadState.error);
|
|
123
|
+
this.autoTouch();
|
|
124
|
+
}
|
|
125
|
+
this.loadingLinks.set(index, link);
|
|
126
|
+
value.state = ELoadState.loading;
|
|
127
|
+
this.preloadMap.set(index, value);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
private handler() {
|
|
131
|
+
if(!this.waitinglinks.length) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
let i = this.loadingLinks.size;
|
|
135
|
+
while (i < Preload.maxLinks) {
|
|
136
|
+
const index = this.waitinglinks.shift();
|
|
137
|
+
if (index !== undefined) {
|
|
138
|
+
this.createLink(index);
|
|
139
|
+
}
|
|
140
|
+
i++;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
private async requestAsyncCallBack (callBack:()=>void, timeout:number):Promise<void> {
|
|
144
|
+
await new Promise(function(resolve) {
|
|
145
|
+
if ((window as any).requestIdleCallback) {
|
|
146
|
+
requestIdleCallback(()=>{
|
|
147
|
+
resolve(1);
|
|
148
|
+
},{timeout})
|
|
149
|
+
} else {
|
|
150
|
+
setTimeout(()=>{
|
|
151
|
+
resolve(2);
|
|
152
|
+
}, timeout)
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
callBack();
|
|
156
|
+
}
|
|
157
|
+
private destroyLink(index: number, link: HTMLLinkElement) {
|
|
158
|
+
document.head.contains(link) && document.head.removeChild(link)
|
|
159
|
+
this.loadingLinks.delete(index)
|
|
160
|
+
}
|
|
62
161
|
dispose() {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
this.
|
|
162
|
+
for (const [index,link] of this.loadingLinks) {
|
|
163
|
+
this.destroyLink(index, link);
|
|
164
|
+
}
|
|
165
|
+
this.preloadMap.clear();
|
|
166
|
+
this.waitingLoadSet.clear();
|
|
167
|
+
this.waitinglinks.length = 0;
|
|
67
168
|
}
|
|
68
169
|
}
|
package/src/presentation.ts
CHANGED
|
@@ -73,6 +73,7 @@ export class Presentation implements IDisposable<void> {
|
|
|
73
73
|
showPreview = false
|
|
74
74
|
pageIndex = 0
|
|
75
75
|
previewLazyload: ILazyLoadInstance | null = null
|
|
76
|
+
timer: number | null = null
|
|
76
77
|
|
|
77
78
|
constructor(config: PresentationConfig) {
|
|
78
79
|
this.pages = config.pages
|
|
@@ -285,15 +286,22 @@ export class Presentation implements IDisposable<void> {
|
|
|
285
286
|
}
|
|
286
287
|
|
|
287
288
|
updateImage() {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
this.image.src = ''
|
|
291
|
-
return
|
|
289
|
+
if (this.timer) {
|
|
290
|
+
clearTimeout(this.timer)
|
|
292
291
|
}
|
|
293
|
-
this.
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
292
|
+
this.timer = setTimeout(() => {
|
|
293
|
+
this.timer = null;
|
|
294
|
+
this.preload.touch(this.pageIndex).then(() => {
|
|
295
|
+
const page = this.page()
|
|
296
|
+
if (!page) {
|
|
297
|
+
this.image.src = ''
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
this.image.width = page.width
|
|
301
|
+
this.image.height = page.height
|
|
302
|
+
this.image.src = page.src
|
|
303
|
+
})
|
|
304
|
+
}, 200)
|
|
297
305
|
}
|
|
298
306
|
|
|
299
307
|
/** Returns a modified URL that points to the thumbnail image. */
|