anki/ts/reviewer/images.ts
Hikaru Y 779ca57660
Refactor CSS/image preloading; implement custom font preloading (#2356)
* Refactor CSS preloading

- Rename css.ts to preload.ts
- Rename type/function names
- Automatically remove style/link element on load/error event

* Refactor image preloading

- Reuse template element
- Change timeout value from 100ms to 200ms, as it often takes more than
  100ms to load even a single small image on a low-spec machine
- Refactor preloadAnswerImages():
  - Use 'new Image()' instead of <link rel=preload>
  - Stop calculating images that only appear on the answer side as
    cached images are resolved immediately

* Update tsconfig.json

es2020.string -> String.matchAll()
es2018.regexp -> RegExprMatchArray.groups

* Implement custom font preloading

Font files for some languages such as Chinese and Japanese can be as
large as 20MB, so we set the timeout value to 800ms for font preloading.
2023-05-10 13:26:02 +10:00

43 lines
1.3 KiB
TypeScript

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const template = document.createElement("template");
export function allImagesLoaded(): Promise<void[]> {
return Promise.all(
Array.from(document.getElementsByTagName("img")).map(imageLoaded),
);
}
function imageLoaded(img: HTMLImageElement): Promise<void> {
return img.complete
? Promise.resolve()
: new Promise((resolve) => {
img.addEventListener("load", () => resolve());
img.addEventListener("error", () => resolve());
});
}
function extractImageSrcs(fragment: DocumentFragment): string[] {
const srcs = [...fragment.querySelectorAll("img[src]")].map(
(img) => (img as HTMLImageElement).src,
);
return srcs;
}
function createImage(src: string): HTMLImageElement {
const img = new Image();
img.src = src;
return img;
}
export function preloadAnswerImages(html: string): void {
template.innerHTML = html;
extractImageSrcs(template.content).forEach(createImage);
}
/** Prevent flickering & layout shift on image load */
export function preloadImages(fragment: DocumentFragment): Promise<void>[] {
return extractImageSrcs(fragment).map(createImage).map(imageLoaded);
}