fix Download button to use better caching

So I broke the Download button when we switched to impress-2020.openneo.net, and I forgot to update the Amazon S3 config.

But in addition to that, I'm making some code changes here, to make downloads faster: we now use exactly the same URL and crossOrigin configuration between the <img> tag on the page, and the image that the Download button requests, which ensures that it can use the cached copy instead of loading new stuff. (There were two main cases: 1. it always loaded the PNGs instead of the SVG, which doesn't matter for quality if we're rendering a 600x600 bitmap anyway, but is good caching, and 2. send `crossOrigin` on the <img> tag, which isn't necessary there, but is necessary for Download, and having them match means we can use the cached copy.)
This commit is contained in:
Emi Matchu 2020-10-10 01:19:59 -07:00
parent 3aad65ccb5
commit ad43f58a07
4 changed files with 33 additions and 37 deletions

View file

@ -21,9 +21,10 @@ import {
import { MdPause, MdPlayArrow } from "react-icons/md";
import { Link } from "react-router-dom";
import { getBestImageUrlForLayer } from "../components/OutfitPreview";
import PosePicker from "./PosePicker";
import SpeciesColorPicker from "../components/SpeciesColorPicker";
import { useLocalStorage } from "../util";
import { loadImage, useLocalStorage } from "../util";
import useOutfitAppearance from "../components/useOutfitAppearance";
/**
@ -409,15 +410,8 @@ function useDownloadableImage(visibleLayers) {
setDownloadImageUrl(null);
const imagePromises = visibleLayers.map(
(layer) =>
new Promise((resolve, reject) => {
const image = new window.Image();
image.crossOrigin = "Anonymous"; // Requires S3 CORS config!
image.addEventListener("load", () => resolve(image), false);
image.addEventListener("error", (e) => reject(e), false);
image.src = layer.imageUrl + "&xoxo";
})
const imagePromises = visibleLayers.map((layer) =>
loadImage(getBestImageUrlForLayer(layer))
);
const images = await Promise.all(imagePromises);

View file

@ -1,6 +1,6 @@
import React from "react";
import { safeImageUrl } from "../util";
import { loadImage, safeImageUrl } from "../util";
function OutfitMovieLayer({ libraryUrl, width, height, isPaused = false }) {
const [stage, setStage] = React.useState(null);
@ -221,7 +221,7 @@ export async function loadMovieLibrary(librarySrc) {
const manifestImages = new Map(
library.properties.manifest.map(({ id, src }) => [
id,
loadImage(safeImageUrl(librarySrcDir + "/" + src)),
loadImage({src: safeImageUrl(librarySrcDir + "/" + src)}),
])
);
await Promise.all(manifestImages.values());
@ -242,19 +242,6 @@ export async function loadMovieLibrary(librarySrc) {
return library;
}
export function loadImage(url) {
const image = new Image();
const promise = new Promise((resolve, reject) => {
image.onload = () => resolve(image);
image.onerror = (e) => reject(e);
image.src = url;
});
promise.cancel = () => {
image.src = "";
};
return promise;
}
export function buildMovieClip(library, libraryUrl) {
let constructorName;
try {

View file

@ -7,12 +7,11 @@ import { CSSTransition, TransitionGroup } from "react-transition-group";
import OutfitMovieLayer, {
buildMovieClip,
hasAnimations,
loadImage,
loadMovieLibrary,
useEaselDependenciesLoader,
} from "./OutfitMovieLayer";
import HangerSpinner from "./HangerSpinner";
import { safeImageUrl, useLocalStorage } from "../util";
import { loadImage, safeImageUrl, useLocalStorage } from "../util";
import useOutfitAppearance from "./useOutfitAppearance";
/**
@ -190,7 +189,13 @@ export function OutfitLayers({
/>
) : (
<img
src={getBestImageUrlForLayer(layer)}
src={getBestImageUrlForLayer(layer).src}
// The crossOrigin prop isn't strictly necessary for loading
// here (<img> tags are always allowed through CORS), but
// this means we make the same request that the Download
// button makes, so it can use the cached version of this
// image instead of requesting it again with crossOrigin!
crossOrigin={getBestImageUrlForLayer(layer).crossOrigin}
alt=""
// We manage the fade-in and fade-out separately! The fade-in
// happens here, when the <Image> finishes preloading and
@ -216,12 +221,6 @@ export function OutfitLayers({
`,
doTransitions && "do-animations"
)}
// This sets up the cache to not need to reload images during
// download!
// TODO: Re-enable this once we get our change into Chakra
// main. For now, this will make Downloads a bit slower, which
// is fine!
// crossOrigin="Anonymous"
/>
)}
</FullScreenCenter>
@ -280,11 +279,11 @@ export function FullScreenCenter({ children, ...otherProps }) {
);
}
function getBestImageUrlForLayer(layer) {
export function getBestImageUrlForLayer(layer) {
if (layer.svgUrl) {
return safeImageUrl(layer.svgUrl);
return { src: safeImageUrl(layer.svgUrl) };
} else {
return layer.imageUrl;
return { src: layer.imageUrl, crossOrigin: "anonymous" };
}
}

View file

@ -225,3 +225,19 @@ export function useLocalStorage(key, initialValue) {
return [storedValue, setValue];
}
export function loadImage({ src, crossOrigin = null }) {
const image = new Image();
const promise = new Promise((resolve, reject) => {
image.onload = () => resolve(image);
image.onerror = (e) => reject(e);
if (crossOrigin) {
image.crossOrigin = crossOrigin;
}
image.src = src;
});
promise.cancel = () => {
image.src = "";
};
return promise;
}