forked from OpenNeo/impress
Potentially improve loading state in new item page preview
I thiiiink I've seen the status of a movie `<outfit-layer>` sometimes be `loading` even when it's clearly already loaded and running. I haven't been able to track down where and how that happens exactly, so this is me acting on a hunch: that maybe the I-would-have-thought-very-unlikely event that the iframe finishes loading before the `<outfit-layer>` connects with its children maybe happens more often than one might think! In this change, we set up the iframe to receive `requestStatus` messages, which it responds to with the status immediately. And we send one of these when the `<outfit-layer>` first discovers the iframe. Fingers crossed!
This commit is contained in:
parent
b03fee8c7e
commit
20202b5cd9
2 changed files with 43 additions and 22 deletions
|
@ -71,11 +71,11 @@ class OutfitLayer extends HTMLElement {
|
|||
}
|
||||
|
||||
play() {
|
||||
this.#forwardIsPlaying(true);
|
||||
this.#sendMessageToIframe({ type: "play" });
|
||||
}
|
||||
|
||||
pause() {
|
||||
this.#forwardIsPlaying(false);
|
||||
this.#sendMessageToIframe({ type: "pause" });
|
||||
}
|
||||
|
||||
#connectToChildren() {
|
||||
|
@ -83,14 +83,23 @@ class OutfitLayer extends HTMLElement {
|
|||
const iframe = this.querySelector("iframe");
|
||||
|
||||
if (image) {
|
||||
// Initialize status based on the image's current `complete` attribute,
|
||||
// then wait for load/error events to update it further if needed.
|
||||
this.#setStatus(image.complete ? "loaded" : "loading");
|
||||
image.addEventListener("load", () => this.#setStatus("loaded"));
|
||||
image.addEventListener("error", () => this.#setStatus("error"));
|
||||
this.#setStatus(image.complete ? "loaded" : "loading");
|
||||
} else if (iframe) {
|
||||
this.iframe = iframe;
|
||||
|
||||
// Initialize status to `loading`, and asynchronously request a status
|
||||
// message from the iframe if it managed to load before this triggers
|
||||
// (impressive, but I think I've seen it happen!). Then, wait for
|
||||
// messages or error events from the iframe to update status further if
|
||||
// needed.
|
||||
this.#setStatus("loading");
|
||||
this.#sendMessageToIframe({ type: "requestStatus" });
|
||||
window.addEventListener("message", (m) => this.#onMessage(m));
|
||||
this.iframe.addEventListener("error", () => this.#setStatus("error"));
|
||||
this.#setStatus("loading");
|
||||
} else {
|
||||
throw new Error(`<outfit-layer> must contain an <img> or <iframe> tag`);
|
||||
}
|
||||
|
@ -134,15 +143,13 @@ class OutfitLayer extends HTMLElement {
|
|||
}
|
||||
}
|
||||
|
||||
#forwardIsPlaying(isPlaying) {
|
||||
if (this.iframe == null) {
|
||||
#sendMessageToIframe(message) {
|
||||
if (this.iframe?.contentWindow == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.iframe.contentWindow.postMessage(
|
||||
{ type: isPlaying ? "play" : "pause" },
|
||||
"*", // The frame is sandboxed (origin == null), so send to Any origin.
|
||||
);
|
||||
// The frame is sandboxed (origin == null), so send to Any origin.
|
||||
this.iframe.contentWindow.postMessage(message, "*");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -290,6 +290,28 @@ function hasAnimations(createjsNode) {
|
|||
);
|
||||
}
|
||||
|
||||
function sendStatus() {
|
||||
if (loadingStatus === "loading") {
|
||||
sendMessage({ type: "status", status: "loading" });
|
||||
} else if (loadingStatus === "loaded") {
|
||||
sendMessage({
|
||||
type: "status",
|
||||
status: "loaded",
|
||||
hasAnimations: hasAnimations(movieClip),
|
||||
});
|
||||
} else if (loadingStatus === "error") {
|
||||
sendMessage({ type: "status", status: "error" });
|
||||
} else {
|
||||
throw new Error(
|
||||
`unexpected loadingStatus ${JSON.stringify(loadingStatus)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function sendMessage(message) {
|
||||
parent.postMessage(message, document.location.origin);
|
||||
}
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
updateCanvasDimensions();
|
||||
|
||||
|
@ -310,6 +332,8 @@ window.addEventListener("message", ({ data }) => {
|
|||
play();
|
||||
} else if (data.type === "pause") {
|
||||
pause();
|
||||
} else if (data.type === "requestStatus") {
|
||||
sendStatus();
|
||||
} else {
|
||||
throw new Error(`unexpected message: ${JSON.stringify(data)}`);
|
||||
}
|
||||
|
@ -317,23 +341,13 @@ window.addEventListener("message", ({ data }) => {
|
|||
|
||||
startMovie()
|
||||
.then(() => {
|
||||
parent.postMessage(
|
||||
{
|
||||
type: "status",
|
||||
status: "loaded",
|
||||
hasAnimations: hasAnimations(movieClip),
|
||||
},
|
||||
document.location.origin,
|
||||
);
|
||||
sendStatus();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(logPrefix, error);
|
||||
|
||||
loadingStatus = "error";
|
||||
parent.postMessage(
|
||||
{ type: "status", status: "error" },
|
||||
document.location.origin,
|
||||
);
|
||||
sendStatus();
|
||||
|
||||
// If loading the movie fails, show the fallback image instead, by moving
|
||||
// it out of the canvas content and into the body.
|
||||
|
|
Loading…
Reference in a new issue