bitmovin-player-ui
Version:
Bitmovin Player UI Framework
74 lines (63 loc) • 2.6 kB
text/typescript
import { DOM } from '../DOM';
export interface ImageLoadedCallback {
(url: string, width: number, height: number): void;
}
interface ImageLoaderState {
url: string;
image: DOM;
loadedCallback: ImageLoadedCallback;
loaded: boolean;
width: number;
height: number;
}
/**
* Tracks the loading state of images.
*/
export class ImageLoader {
private state: { [url: string]: ImageLoaderState } = {};
/**
* Loads an image and call the callback once the image is loaded. If the image is already loaded, the callback
* is called immediately, else it is called once loading has finished. Calling this method multiple times for the
* same image while it is loading calls only let callback passed into the last call.
* @param url The url to the image to load
* @param loadedCallback The callback that is called when the image is loaded
*/
load(url: string, loadedCallback: ImageLoadedCallback): void {
if (!this.state[url]) {
// When the image was never attempted to be loaded before, we create a state and store it in the state map
// for later use when the same image is requested to be loaded again.
const state: ImageLoaderState = {
url: url,
image: new DOM('img', {}),
loadedCallback: loadedCallback,
loaded: false,
width: 0,
height: 0,
};
this.state[url] = state;
// Listen to the load event, update the state and call the callback once the image is loaded
state.image.on('load', e => {
state.loaded = true;
state.width = (<HTMLImageElement>state.image.get(0)).width;
state.height = (<HTMLImageElement>state.image.get(0)).height;
this.callLoadedCallback(state);
});
// Set the image URL to start the loading
state.image.attr('src', state.url);
} else {
// We have a state for the requested image, so it is either already loaded or currently loading
const state = this.state[url];
// We overwrite the callback to make sure that only the callback of the latest call gets executed.
// Earlier callbacks become invalid once a new load call arrives, and they are not called as long as the image
// is not loaded.
state.loadedCallback = loadedCallback;
// When the image is already loaded, we directly execute the callback instead of waiting for the load event
if (state.loaded) {
this.callLoadedCallback(state);
}
}
}
private callLoadedCallback(state: ImageLoaderState): void {
state.loadedCallback(state.url, state.width, state.height);
}
}