diff --git a/package.json b/package.json index 0b7c090..1e29a72 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "canvas": "^2.6.1", "dataloader": "^2.0.0", "dompurify": "^2.2.0", + "easeljs": "^1.0.2", "escape-html": "^1.0.3", "framer-motion": "^4.1.11", "graphql": "^15.5.0", @@ -49,6 +50,7 @@ "react-scripts": "^4.0.1", "react-transition-group": "^4.3.0", "simple-markdown": "^0.7.2", + "tweenjs": "^1.0.2", "typescript": "^4.1.3", "xmlrpc": "^1.3.2" }, @@ -91,6 +93,7 @@ } ], "import/first": "off", + "import/no-webpack-loader-syntax": "off", "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": [ "warn", @@ -128,6 +131,7 @@ "es6-promise-pool": "^2.5.0", "eslint-plugin-cypress": "^2.11.2", "husky": "^6.0.0", + "imports-loader": "^3.0.0", "inquirer": "^7.3.3", "jest-image-snapshot": "^4.3.0", "lint-staged": "^10.5.4", diff --git a/src/app/components/OutfitMovieLayer.js b/src/app/components/OutfitMovieLayer.js index 64cbfc2..61a2991 100644 --- a/src/app/components/OutfitMovieLayer.js +++ b/src/app/components/OutfitMovieLayer.js @@ -4,6 +4,12 @@ import { useToast } from "@chakra-ui/react"; import { loadImage, logAndCapture, safeImageUrl } from "../util"; +// Import EaselJS and TweenJS directly into the `window` object! The bundled +// scripts are built to attach themselves to `window.createjs`, and +// `window.createjs` is where the Neopets movie libraries expects to find them! +require("imports-loader?wrapper=window!easeljs/lib/easeljs"); +require("imports-loader?wrapper=window!tweenjs/lib/tweenjs"); + function OutfitMovieLayer({ libraryUrl, width, @@ -19,8 +25,6 @@ function OutfitMovieLayer({ const hasShownErrorMessageRef = React.useRef(false); const toast = useToast(); - const loadingDeps = useEaselDependenciesLoader(); - // Set the canvas's internal dimensions to be higher, if the device has high // DPI like retina. But we'll keep the layout width/height as expected! const internalWidth = width * window.devicePixelRatio; @@ -63,7 +67,7 @@ function OutfitMovieLayer({ React.useLayoutEffect(() => { const canvas = canvasRef.current; - if (loadingDeps || !canvas) { + if (!canvas) { return; } @@ -101,15 +105,11 @@ function OutfitMovieLayer({ canvas.height = 0; } }; - }, [loadingDeps, libraryUrl, toast]); + }, [libraryUrl, toast]); // This effect gives us the `library` and `movieClip`, based on the incoming // `libraryUrl`. React.useEffect(() => { - if (loadingDeps) { - return; - } - let canceled = false; loadMovieLibrary(libraryUrl) @@ -132,7 +132,7 @@ function OutfitMovieLayer({ setLibrary(null); setMovieClip(null); }; - }, [loadingDeps, libraryUrl]); + }, [libraryUrl]); // This effect puts the `movieClip` on the `stage`, when both are ready. React.useEffect(() => { @@ -230,55 +230,6 @@ function OutfitMovieLayer({ ); } -/** - * useEaselDependenciesLoader loads the CreateJS scripts we use in OutfitCanvas. - * We load it as part of OutfitCanvas, but callers can also use this to preload - * the scripts and track loading progress. - */ -export function useEaselDependenciesLoader() { - // NOTE: I couldn't find an official NPM source for this that worked with - // Webpack, and I didn't want to rely on random people's ports, and I - // couldn't get a bundled version to work quite right. So we load - // createjs async! - const loadingEaselJS = useScriptTag( - "https://code.createjs.com/1.0.0/easeljs.min.js" - ); - const loadingTweenJS = useScriptTag( - "https://code.createjs.com/1.0.0/tweenjs.min.js" - ); - const loadingDeps = loadingEaselJS || loadingTweenJS; - - return loadingDeps; -} - -function useScriptTag(src) { - const [loading, setLoading] = React.useState(true); - - React.useEffect(() => { - const existingScript = document.querySelector( - `script[src=${CSS.escape(src)}]` - ); - if (existingScript) { - setLoading(false); - return; - } - - let canceled = false; - loadScriptTag(src).then(() => { - if (!canceled) { - setLoading(false); - } - }); - - return () => { - canceled = true; - setLoading(true); - }; - }, [src, setLoading]); - - return loading; -} - function loadScriptTag(src) { return new Promise((resolve, reject) => { const script = document.createElement("script"); diff --git a/src/app/components/OutfitPreview.js b/src/app/components/OutfitPreview.js index da3804a..7a2996f 100644 --- a/src/app/components/OutfitPreview.js +++ b/src/app/components/OutfitPreview.js @@ -16,7 +16,6 @@ import OutfitMovieLayer, { buildMovieClip, hasAnimations, loadMovieLibrary, - useEaselDependenciesLoader, } from "./OutfitMovieLayer"; import HangerSpinner from "./HangerSpinner"; import { loadImage, safeImageUrl, useLocalStorage } from "../util"; @@ -163,13 +162,10 @@ export function OutfitLayers({ false ); - const { loading: loadingEasel } = useEaselDependenciesLoader(); - const loadingAnything = loading || loadingEasel; - // When we start in a loading state, or re-enter a loading state, start the // loading delay timer. React.useEffect(() => { - if (loadingAnything) { + if (loading) { setLoadingDelayHasPassed(false); const t = setTimeout( () => setLoadingDelayHasPassed(true), @@ -177,7 +173,7 @@ export function OutfitLayers({ ); return () => clearTimeout(t); } - }, [loadingDelayMs, loadingAnything]); + }, [loadingDelayMs, loading]); React.useLayoutEffect(() => { function computeAndSaveCanvasSize() { @@ -291,7 +287,7 @@ export function OutfitLayers({ // also use a timeout to delay the fade-in by 0.5s, but don't delay the // fade-out at all. (The timeout was an awkward choice, it was hard to // find a good CSS way to specify this delay well!) - opacity={loadingAnything && loadingDelayHasPassed ? 1 : 0} + opacity={loading && loadingDelayHasPassed ? 1 : 0} transition="opacity 0.2s" > {spinnerVariant === "overlay" && ( diff --git a/yarn.lock b/yarn.lock index e02d4e3..fd75e2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9226,6 +9226,11 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +easeljs@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/easeljs/-/easeljs-1.0.2.tgz#68fdcc69d0f217394e2ebf51ae047428a81240de" + integrity sha512-PQTsiud32vrUIqZCbynjOJjCzoEp0xH+MRusRCdsZ1MzL4LCE2vp4Sa5cr6aShB3mK4vMZ8LFPnts4xuFhjEmg== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -11426,6 +11431,16 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" +imports-loader@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-1.2.0.tgz#b06823d0bb42e6f5ff89bc893829000eda46693f" + integrity sha512-zPvangKEgrrPeqeUqH0Uhc59YqK07JqZBi9a9cQ3v/EKUIqrbJHY4CvUrDus2lgQa5AmPyXuGrWP8JJTqzE5RQ== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + source-map "^0.6.1" + strip-comments "^2.0.1" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -17718,6 +17733,11 @@ strip-comments@^1.0.2: babel-extract-comments "^1.0.0" babel-plugin-transform-object-rest-spread "^6.26.0" +strip-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b" + integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw== + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -18348,6 +18368,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tweenjs@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tweenjs/-/tweenjs-1.0.2.tgz#768869c4d4ba91fdb4c5ccc661969c58138555af" + integrity sha512-WnFozCNkUkmJtLqJyGrToxVojW2Srzudktr8BzFKQijQRVcmlq7Fc+qfo75ccnwIJGiRWbXKfg7qU67Tzbb1bg== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"