From bed525d3ff03334fc3e9999455b8ba78d03c1dd1 Mon Sep 17 00:00:00 2001 From: Matchu Date: Tue, 8 Jun 2021 08:27:45 -0700 Subject: [PATCH] Add hi-res mode setting, default off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're just having too many glitchy SVGs for my taste, esp since TNT seems to just be using PNGs for now? This change defaults us to using PNGs for users by default, with the option to use SVGs as a new "hi-res mode" setting. This is our first ever setting, wow! I'm also envisioning that like, if we get Fastly Image Optimizer set up, this could be a way to tune the quality of the incoming images. We could also consider a setting to turn off animations altogether—like, just download the PNG instead of the movie, whereas right now we download the movie on the assumption that you might play it at any time. --- src/app/WardrobePage/OutfitControls.js | 143 +++++++++++++++++++------ src/app/components/OutfitPreview.js | 13 ++- 2 files changed, 122 insertions(+), 34 deletions(-) diff --git a/src/app/WardrobePage/OutfitControls.js b/src/app/WardrobePage/OutfitControls.js index 83667f9..23d7cd8 100644 --- a/src/app/WardrobePage/OutfitControls.js +++ b/src/app/WardrobePage/OutfitControls.js @@ -5,10 +5,20 @@ import { Button, DarkMode, Flex, + FormControl, + FormHelperText, + FormLabel, + HStack, IconButton, ListItem, + Popover, + PopoverArrow, + PopoverBody, + PopoverContent, + PopoverTrigger, Portal, Stack, + Switch, Tooltip, UnorderedList, useClipboard, @@ -17,8 +27,10 @@ import { import { ArrowBackIcon, CheckIcon, + ChevronDownIcon, DownloadIcon, LinkIcon, + SettingsIcon, } from "@chakra-ui/icons"; import { MdPause, MdPlayArrow } from "react-icons/md"; import { Link } from "react-router-dom"; @@ -151,13 +163,20 @@ function OutfitControls({ - {showAnimationControls && ( - - - - - - )} + + + + {showAnimationControls && } + + + {blinkInState.type === "started" && ( @@ -432,36 +450,95 @@ function PlayPauseButton() { const PlayPauseButtonContent = React.forwardRef( ({ isPaused, setIsPaused, ...props }, ref) => { return ( - + ); } ); +function SettingsButton({ onLockFocus, onUnlockFocus }) { + return ( + + + + + + + + + + + + + + + + + + ); +} + +function HiResModeSetting() { + const [hiResMode, setHiResMode] = useLocalStorage("DTIHiResMode", false); + + return ( + + + + + Hi-res mode (SVG) + + + Crisper at higher resolutions, but not always accurate + + + + setHiResMode(e.target.checked)} + /> + + + ); +} + +const TranslucentButton = React.forwardRef(({ children, ...props }, ref) => { + return ( + + ); +}); + /** * ControlButton is a UI helper to render the cute round buttons we use in * OutfitControls! @@ -493,6 +570,8 @@ function ControlButton({ icon, "aria-label": ariaLabel, ...props }) { * image URL. */ function useDownloadableImage(visibleLayers) { + const [hiResMode] = useLocalStorage("DTIHiResMode", false); + const [downloadImageUrl, setDownloadImageUrl] = React.useState(null); const [preparedForLayerIds, setPreparedForLayerIds] = React.useState([]); const toast = useToast(); @@ -511,8 +590,12 @@ function useDownloadableImage(visibleLayers) { setDownloadImageUrl(null); + // NOTE: You could argue that we may as well just always use PNGs here, + // regardless of hi-res mode… but using the same src will help both + // performance (can use cached SVG), and predictability (image will + // look like what you see here). const imagePromises = visibleLayers.map((layer) => - loadImage(getBestImageUrlForLayer(layer)) + loadImage(getBestImageUrlForLayer(layer, { hiResMode })) ); let images; @@ -545,7 +628,7 @@ function useDownloadableImage(visibleLayers) { ); setDownloadImageUrl(canvas.toDataURL("image/png")); setPreparedForLayerIds(layerIds); - }, [preparedForLayerIds, visibleLayers, toast]); + }, [preparedForLayerIds, visibleLayers, toast, hiResMode]); return [downloadImageUrl, prepareDownload]; } diff --git a/src/app/components/OutfitPreview.js b/src/app/components/OutfitPreview.js index b05ba78..d964ee7 100644 --- a/src/app/components/OutfitPreview.js +++ b/src/app/components/OutfitPreview.js @@ -124,6 +124,8 @@ export function OutfitLayers({ isPaused = true, ...props }) { + const [hiResMode] = useLocalStorage("DTIHiResMode", false); + const containerRef = React.useRef(null); const [canvasSize, setCanvasSize] = React.useState(0); const [loadingDelayHasPassed, setLoadingDelayHasPassed] = React.useState( @@ -230,13 +232,16 @@ 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} + crossOrigin={ + getBestImageUrlForLayer(layer, { hiResMode }) + .crossOrigin + } alt="" objectFit="contain" maxWidth="100%" @@ -306,8 +311,8 @@ export function FullScreenCenter({ children, ...otherProps }) { ); } -export function getBestImageUrlForLayer(layer) { - if (layer.svgUrl) { +export function getBestImageUrlForLayer(layer, { hiResMode = false } = {}) { + if (hiResMode && layer.svgUrl) { return { src: safeImageUrl(layer.svgUrl), crossOrigin: "anonymous" }; } else { return { src: safeImageUrl(layer.imageUrl), crossOrigin: "anonymous" };