diff --git a/src/app/WardrobePage/OutfitControls.js b/src/app/WardrobePage/OutfitControls.js index 3ea60be..9310b44 100644 --- a/src/app/WardrobePage/OutfitControls.js +++ b/src/app/WardrobePage/OutfitControls.js @@ -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); diff --git a/src/app/components/OutfitMovieLayer.js b/src/app/components/OutfitMovieLayer.js index aa807d3..5b80ccb 100644 --- a/src/app/components/OutfitMovieLayer.js +++ b/src/app/components/OutfitMovieLayer.js @@ -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 { diff --git a/src/app/components/OutfitPreview.js b/src/app/components/OutfitPreview.js index 75dcfeb..14e6055 100644 --- a/src/app/components/OutfitPreview.js +++ b/src/app/components/OutfitPreview.js @@ -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({ /> ) : ( 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 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" /> )} @@ -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" }; } } diff --git a/src/app/util.js b/src/app/util.js index a150835..6d95951 100644 --- a/src/app/util.js +++ b/src/app/util.js @@ -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; +}