@linear_non/stellar-kit 1.1.6 → 1.1.7
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/classes/FontLoader.js +1 -0
- package/classes/ImageLoader.js +115 -54
- package/classes/MasterLoader.js +1 -1
- package/kitStore.js +4 -0
- package/package.json +1 -1
package/classes/FontLoader.js
CHANGED
|
@@ -4,6 +4,7 @@ import { emitter, EVENTS } from "../events"
|
|
|
4
4
|
export default class FontLoader {
|
|
5
5
|
static type = "fonts"
|
|
6
6
|
static events = { PROGRESS: EVENTS.APP_FONTS_PROGRESS, LOADED: EVENTS.APP_FONTS_LOADED }
|
|
7
|
+
static getCount = (opts = {}) => opts.fonts?.length ?? 0
|
|
7
8
|
constructor({ fonts = [] }) {
|
|
8
9
|
// fonts = [{ family: "Inter", url: "/fonts/Inter.woff2" }, ...]
|
|
9
10
|
|
package/classes/ImageLoader.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ImageLoader.js
|
|
1
|
+
// classes/ImageLoader.js
|
|
2
2
|
import { emitter, EVENTS } from "../events"
|
|
3
3
|
|
|
4
4
|
export default class ImageLoader {
|
|
@@ -7,97 +7,158 @@ export default class ImageLoader {
|
|
|
7
7
|
PROGRESS: EVENTS.APP_IMAGES_PROGRESS,
|
|
8
8
|
LOADED: EVENTS.APP_IMAGES_LOADED,
|
|
9
9
|
}
|
|
10
|
+
static getCount = (opts = {}) => opts.urls?.length ?? opts.total ?? 0
|
|
10
11
|
|
|
11
|
-
constructor({ origin, fileName, extension, total, urls = [] }) {
|
|
12
|
+
constructor({ origin, fileName, extension, total, urls = [], useWorker = true, cache = null }) {
|
|
12
13
|
this.origin = origin
|
|
13
14
|
this.fileName = fileName
|
|
14
15
|
this.extension = extension
|
|
15
16
|
this.total = total
|
|
16
17
|
this.urls = urls
|
|
18
|
+
this.useWorker = useWorker
|
|
19
|
+
this.cache = cache
|
|
17
20
|
|
|
18
21
|
this.images = []
|
|
19
22
|
this.loaded = 0
|
|
20
23
|
this.isLoaded = false
|
|
24
|
+
|
|
25
|
+
this._results = new Map() // url -> HTMLImageElement
|
|
26
|
+
this._objectURLs = [] // for revoke
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
async load() {
|
|
24
|
-
const
|
|
30
|
+
const raw = this.urls.length
|
|
25
31
|
? this.urls
|
|
26
32
|
: Array.from({ length: this.total }, (_, i) => `${this.origin}/${this.fileName}${i}.${this.extension}`)
|
|
27
33
|
|
|
34
|
+
const base =
|
|
35
|
+
typeof document !== "undefined"
|
|
36
|
+
? document.baseURI
|
|
37
|
+
: typeof location !== "undefined"
|
|
38
|
+
? location.href
|
|
39
|
+
: ""
|
|
40
|
+
const urls = raw.map(u => {
|
|
41
|
+
try {
|
|
42
|
+
return new URL(u, base).href
|
|
43
|
+
} catch {
|
|
44
|
+
return u
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// cache-aware filter
|
|
49
|
+
const toLoad = this.cache ? urls.filter(u => !this.cache.has(u)) : urls
|
|
50
|
+
|
|
28
51
|
this.total = urls.length
|
|
52
|
+
this.loaded = this.total - toLoad.length // count already-cached as loaded
|
|
29
53
|
|
|
30
|
-
|
|
31
|
-
|
|
54
|
+
// (optional) emit initial progress if cache pre-fills
|
|
55
|
+
if (this.loaded > 0) {
|
|
56
|
+
emitter.emit(EVENTS.APP_IMAGES_PROGRESS, {
|
|
57
|
+
loaded: this.loaded,
|
|
58
|
+
total: this.total,
|
|
59
|
+
percent: Math.round((this.loaded / this.total) * 100),
|
|
60
|
+
})
|
|
61
|
+
}
|
|
32
62
|
|
|
63
|
+
await Promise.all(toLoad.map(url => this._loadOne(url)))
|
|
64
|
+
|
|
65
|
+
// build ordered result (use cache first, then freshly loaded)
|
|
66
|
+
this.images = urls.map(u => this.cache?.get(u) || this._results.get(u) || null)
|
|
33
67
|
this.isLoaded = true
|
|
34
68
|
emitter.emit(EVENTS.APP_IMAGES_LOADED, this.images)
|
|
35
|
-
|
|
36
69
|
return this.images
|
|
37
70
|
}
|
|
38
71
|
|
|
39
|
-
|
|
40
|
-
createWorker() {
|
|
72
|
+
_createInlineWorker() {
|
|
41
73
|
const code = `
|
|
42
74
|
self.onmessage = async (e) => {
|
|
43
|
-
const { url
|
|
75
|
+
const { url } = e.data;
|
|
44
76
|
try {
|
|
45
77
|
const res = await fetch(url);
|
|
46
78
|
const blob = await res.blob();
|
|
47
|
-
self.postMessage({ url, blob
|
|
79
|
+
self.postMessage({ url, blob });
|
|
48
80
|
} catch (err) {
|
|
49
|
-
self.postMessage({ url, error: String(err)
|
|
81
|
+
self.postMessage({ url, error: String(err) });
|
|
50
82
|
}
|
|
51
83
|
};`
|
|
52
84
|
const blob = new Blob([code], { type: "application/javascript" })
|
|
53
|
-
|
|
54
|
-
const w = new Worker(workerUrl) // classic worker (no imports)
|
|
55
|
-
// (optional) URL.revokeObjectURL(workerUrl) after creation; leaving as-is for safety
|
|
56
|
-
return w
|
|
85
|
+
return new Worker(URL.createObjectURL(blob))
|
|
57
86
|
}
|
|
58
87
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
img.src = URL.createObjectURL(blob)
|
|
71
|
-
img.onload = () => {
|
|
72
|
-
this.images[index] = img
|
|
73
|
-
emitter.emit(EVENTS.APP_IMAGES_PROGRESS, {
|
|
74
|
-
loaded: this.loaded,
|
|
75
|
-
total: this.total,
|
|
76
|
-
percent: Math.round((this.loaded / this.total) * 100),
|
|
77
|
-
})
|
|
78
|
-
resolve(img)
|
|
79
|
-
worker.terminate()
|
|
88
|
+
async _loadOne(url) {
|
|
89
|
+
// worker path
|
|
90
|
+
if (this.useWorker && typeof Worker !== "undefined") {
|
|
91
|
+
return new Promise(resolve => {
|
|
92
|
+
const w = this._createInlineWorker()
|
|
93
|
+
w.onmessage = async e => {
|
|
94
|
+
const { blob, error } = e.data
|
|
95
|
+
if (error || !blob) {
|
|
96
|
+
this._bumpProgress()
|
|
97
|
+
w.terminate()
|
|
98
|
+
return resolve(null)
|
|
80
99
|
}
|
|
81
|
-
img
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
})
|
|
87
|
-
resolve(null)
|
|
88
|
-
worker.terminate()
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
// count progress even on error
|
|
92
|
-
emitter.emit(EVENTS.APP_IMAGES_PROGRESS, {
|
|
93
|
-
loaded: this.loaded,
|
|
94
|
-
total: this.total,
|
|
95
|
-
percent: Math.round((this.loaded / this.total) * 100),
|
|
96
|
-
})
|
|
97
|
-
resolve(null)
|
|
98
|
-
worker.terminate()
|
|
100
|
+
const img = await this._blobToImage(blob)
|
|
101
|
+
this._store(url, img)
|
|
102
|
+
this._bumpProgress()
|
|
103
|
+
w.terminate()
|
|
104
|
+
resolve(img)
|
|
99
105
|
}
|
|
106
|
+
w.postMessage({ url })
|
|
100
107
|
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// fallback path (no worker)
|
|
111
|
+
try {
|
|
112
|
+
const res = await fetch(url)
|
|
113
|
+
const blob = await res.blob()
|
|
114
|
+
const img = await this._blobToImage(blob)
|
|
115
|
+
this._store(url, img)
|
|
116
|
+
this._bumpProgress()
|
|
117
|
+
return img
|
|
118
|
+
} catch {
|
|
119
|
+
this._bumpProgress()
|
|
120
|
+
return null
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async _blobToImage(blob) {
|
|
125
|
+
if (!blob || !blob.type?.startsWith?.("image/")) return null
|
|
126
|
+
return new Promise(resolve => {
|
|
127
|
+
const url = URL.createObjectURL(blob)
|
|
128
|
+
this._objectURLs.push(url)
|
|
129
|
+
const img = new Image()
|
|
130
|
+
img.src = url
|
|
131
|
+
img.onload = async () => {
|
|
132
|
+
if (img.decode) {
|
|
133
|
+
try {
|
|
134
|
+
await img.decode()
|
|
135
|
+
} catch {}
|
|
136
|
+
}
|
|
137
|
+
resolve(img)
|
|
138
|
+
}
|
|
139
|
+
img.onerror = () => resolve(null)
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_store(url, img) {
|
|
144
|
+
if (!img) return
|
|
145
|
+
this._results.set(url, img)
|
|
146
|
+
if (this.cache) this.cache.set(url, img)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
_bumpProgress() {
|
|
150
|
+
this.loaded++
|
|
151
|
+
emitter.emit(EVENTS.APP_IMAGES_PROGRESS, {
|
|
152
|
+
loaded: this.loaded,
|
|
153
|
+
total: this.total,
|
|
154
|
+
percent: Math.round((this.loaded / this.total) * 100),
|
|
101
155
|
})
|
|
102
156
|
}
|
|
157
|
+
|
|
158
|
+
destroy() {
|
|
159
|
+
this._objectURLs.forEach(u => URL.revokeObjectURL(u))
|
|
160
|
+
this._objectURLs.length = 0
|
|
161
|
+
this.images = []
|
|
162
|
+
this._results.clear()
|
|
163
|
+
}
|
|
103
164
|
}
|
package/classes/MasterLoader.js
CHANGED
|
@@ -12,7 +12,7 @@ export default class MasterLoader {
|
|
|
12
12
|
add(loaderClass, options) {
|
|
13
13
|
const loader = new loaderClass(options)
|
|
14
14
|
this.loaders.push({ loaderClass, loader })
|
|
15
|
-
this.total += loader.total
|
|
15
|
+
this.total += loaderClass.getCount?.(options) ?? loader.total ?? 0
|
|
16
16
|
return loader
|
|
17
17
|
}
|
|
18
18
|
|
package/kitStore.js
CHANGED
|
@@ -7,6 +7,9 @@ export const sizes = {
|
|
|
7
7
|
m: 390, // mobile width
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
// in case you want to "cache" your assets
|
|
11
|
+
export const assets = {}
|
|
12
|
+
|
|
10
13
|
export const flags = {
|
|
11
14
|
isFocus: false,
|
|
12
15
|
isSmooth: false,
|
|
@@ -34,4 +37,5 @@ export default {
|
|
|
34
37
|
mouse,
|
|
35
38
|
flags,
|
|
36
39
|
pageContent: null, // This should be set to the main content element
|
|
40
|
+
assets,
|
|
37
41
|
}
|