diff --git a/src/FairyGUI.ts b/src/FairyGUI.ts index 9a6b7be..9c9da8d 100644 --- a/src/FairyGUI.ts +++ b/src/FairyGUI.ts @@ -61,6 +61,7 @@ export { GTween } from "./tween/GTween"; export { GTweener } from "./tween/GTweener"; export { EaseType } from "./tween/EaseType"; +export { AssetLoader } from "./utils/AssetLoader"; export { UBBParser } from "./utils/UBBParser"; export { ByteBuffer } from "./utils/ByteBuffer";; export * from "./utils/ToolSet" diff --git a/src/core/Image.ts b/src/core/Image.ts index f3a72bb..d2b1b79 100644 --- a/src/core/Image.ts +++ b/src/core/Image.ts @@ -11,6 +11,8 @@ export class Image extends UIElement { protected _textureScale: Vec2; protected _tileGridIndice: number = 0; + private _blobURL: string; + private _timerID_1: number = 0; constructor() { @@ -139,19 +141,41 @@ export class Image extends UIElement { this.style.filter = filter; } + public dispose(): void { + super.dispose(); + this.returnBlobURL(); + } + + private returnBlobURL(){ + if(this._blobURL){ + AssetLoader.returnBlobURL(this._blobURL); + this._blobURL = null; + } + } + protected refresh(): void { if (this._timerID_1 != 0) return; - this._timerID_1 = window.requestAnimationFrame(() => { + this._timerID_1 = window.requestAnimationFrame(async () => { this._timerID_1 = 0; - + this.returnBlobURL(); if (!this._src) { this.style.backgroundImage = "none"; return; } + let data = await AssetLoader.load(this._src); + if(data == null){ + this.style.backgroundImage = "none"; + return; + } + let src = this._blobURL = AssetLoader.getBlobURL(this._src); + if(src == null){ + this.style.backgroundImage = "none"; + return; + } if (this._scaleByTile) { - this.style.backgroundImage = "url('" + this._src + "')"; + this.style.backgroundImage = "url('" + src + "')"; if (this._textureScale.x != 1 || this._textureScale.y != 1) this.style.backgroundSize = this._textureScale.x + "px " + this._textureScale.y + "px"; else @@ -161,7 +185,7 @@ export class Image extends UIElement { else if (this._scale9Grid) { this.style.boxSizing = "border-box"; this.style.backgroundImage = "none"; - this.style.borderImage = "url('" + this._src + "')"; + this.style.borderImage = "url('" + src + "')"; if (this._textureScale.x != 1 || this._textureScale.y != 1) this.style.borderImageWidth = Math.floor(this._scale9Grid.top / this._textureScale.y) + "px " + Math.floor(this._scale9Grid.right / this._textureScale.x) + "px " + Math.floor(this._scale9Grid.bottom / this._textureScale.y) + "px " + Math.floor(this._scale9Grid.left / this._textureScale.x) + "px" @@ -175,7 +199,7 @@ export class Image extends UIElement { this.style.borderImageRepeat = ""; } else { - this.style.backgroundImage = "url('" + this._src + "')"; + this.style.backgroundImage = "url('" + src + "')"; this.style.backgroundSize = "100% 100%"; this.style.backgroundRepeat = "no-repeat"; } diff --git a/src/ui/UIPackage.ts b/src/ui/UIPackage.ts index 478824e..1e2ab0c 100644 --- a/src/ui/UIPackage.ts +++ b/src/ui/UIPackage.ts @@ -61,29 +61,16 @@ export class UIPackage { return _instByName[name]; } - public static loadPackage(url: string): Promise { + public static async loadPackage(url: string): Promise { if (!url.endsWith("/")) url += "/"; - return new Promise(resolve => { - let pkg: UIPackage = _instById[url]; - if (pkg) { - resolve(pkg); - return; - } - - let request = new HttpRequest(); - request.send(url + "package.xml", null, "get", "arraybuffer"); - request.on("complete", (evt: Event) => { - let pkg: UIPackage = new UIPackage(); - pkg.loadPackage(new ByteBuffer(evt.data), url); - - _instById[pkg.id] = pkg; - _instByName[pkg.name] = pkg; - _instById[pkg.path] = pkg; - - resolve(pkg); - }); - }); + const data = await AssetLoader.load(url + "package.xml"); + let pkg: UIPackage = new UIPackage(); + pkg.loadPackage(new ByteBuffer(data), url); + _instById[pkg.id] = pkg; + _instByName[pkg.name] = pkg; + _instById[pkg.path] = pkg; + return pkg; } public static removePackage(packageIdOrName: string): void { diff --git a/src/utils/AssetLoader.ts b/src/utils/AssetLoader.ts new file mode 100644 index 0000000..14c92fc --- /dev/null +++ b/src/utils/AssetLoader.ts @@ -0,0 +1,64 @@ +import { Event } from "../event/Event"; +import { HttpRequest } from "./HttpRequest"; + +export interface IAssetLoaderStatic { + load(url: string): Promise; + getBlobURL(data:any):string; + returnBlobURL(url: string): void; +} + +declare global { + var AssetLoader: IAssetLoaderStatic; +} + +export class AssetLoader { + private static cache = new Map(); + private static blobURLCache = new Map(); + private static blobURLCnt = new Map(); + + static load(url: string): Promise { + let cacheContent = this.cache.get(url); + if(cacheContent){ + return cacheContent; + } + return new Promise((resolve, reject) => { + let request = new HttpRequest(); + request.send(url, null, "get", "arraybuffer"); + request.on("complete", (evt: Event) => { + this.cache.set(url, evt.data); + resolve(evt.data); + }); + }); + } + + static getBlobURL(dataURL: string): string { + let blobURL = this.blobURLCache.get(dataURL); + if (blobURL) { + return blobURL; + } + let data = this.cache.get(dataURL); + if(data == null){ + return null; + } + + let blob = new Blob([data], { type: 'application/octet-binary' }); + blobURL = URL.createObjectURL(blob); + + this.blobURLCache.set(dataURL, blobURL); + let cnt = this.blobURLCnt.get(blobURL) || 0; + this.blobURLCnt.set(blobURL, cnt + 1); + return blobURL; + } + + static returnBlobURL(blobURL: string): void { + let cnt = this.blobURLCnt.get(blobURL) || 0; + if(cnt == 0){ + URL.revokeObjectURL(blobURL); + this.blobURLCnt.delete(blobURL); + return; + } + this.blobURLCnt.set(blobURL, cnt - 1); + } +} + +globalThis.AssetLoader = AssetLoader; \ No newline at end of file