image-preload
Version:
Simple, framework-agnostic image preloader. Async, sync, background, foreground, whatever!
130 lines (111 loc) • 3.27 kB
text/typescript
import Greenlet from "greenlet";
enum Order {
AllAtOnce,
InOrder
}
type Options = {
order?: Order;
timeout?: number;
shouldContinueOnFail?: boolean;
toBase64?: boolean;
inBackground?: boolean;
onSingleImageFail?: Function;
onSingleImageComplete?: Function;
onComplete?: Function;
};
type FullOptions = {
order: Order;
timeout: number;
shouldContinueOnFail: boolean;
toBase64: boolean;
inBackground: boolean;
onSingleImageFail: Function;
onSingleImageComplete: Function;
onComplete: Function;
};
const defaultOptions: FullOptions = {
order: Order.InOrder,
timeout: 0,
shouldContinueOnFail: true,
toBase64: false,
inBackground: false,
onSingleImageFail: () => {},
onSingleImageComplete: () => {},
onComplete: () => {}
};
function sleep(t: number) {
return new Promise(resolve => setTimeout(resolve, t));
}
async function Preload(images: Array<string>, userProvidedOptions?: Options) {
const options: FullOptions = { ...defaultOptions, ...(userProvidedOptions || {}) };
if (options.timeout && options.order === Order.AllAtOnce) {
throw new Error("Invalid options: Cannot specify `timeout` options and `AllAtOnce` order.");
}
if (options.toBase64 === false && options.inBackground === true) {
throw new Error("Invalid options: If `inBackground` options is true `toBase64` options must be true as well.");
}
if (options.toBase64 === true && options.inBackground === false) {
throw new Error("Invalid options: If `inBackground` options is false `toBase64` options must be false as well.");
}
function loadInDom(x: string) {
return new Promise((resolve, reject) => {
const el = document.createElement("img");
el.setAttribute("src", x);
el.addEventListener("load", e => {
options.onSingleImageComplete(e);
resolve(e);
});
el.addEventListener("error", e => {
options.onSingleImageFail(e);
reject(e);
});
});
}
async function load(x: string) {
if (options.inBackground) {
const fn = Greenlet(y => {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open("GET", y);
req.responseType = "blob";
req.send();
req.addEventListener("load", () => {
const reader = new FileReader();
reader.readAsDataURL(req.response);
reader.addEventListener("loadend", () => {
resolve(reader.result);
});
});
req.addEventListener("error", e => {
reject(e);
});
});
});
try {
return options.onSingleImageComplete(await fn(x));
} catch (e) {
options.onSingleImageFail(e);
throw new Error(e);
}
}
return await loadInDom(x);
}
if (options.order === Order.AllAtOnce) {
await Promise.all(images.map(load));
}
if (options.order === Order.InOrder) {
for (let single of images) {
try {
await sleep(options.timeout);
await load(single);
} catch (e) {
if (!options.shouldContinueOnFail) {
throw new Error(e);
}
}
}
}
options.onComplete();
}
export { Order };
export default Preload;