show a preview of color/species on the homepage
Using the newly extracted OutfitPreview! I'm really happy with how this turned out :3 It also makes the pageload after clicking Start super smooth, no spinner! Thanks Apollo cache!!
This commit is contained in:
parent
9164d89240
commit
5a9d2f6566
3 changed files with 72 additions and 11 deletions
|
@ -6,6 +6,7 @@ import { useHistory } from "react-router-dom";
|
||||||
import { useLazyQuery } from "@apollo/react-hooks";
|
import { useLazyQuery } from "@apollo/react-hooks";
|
||||||
|
|
||||||
import { Heading1, usePageTitle } from "./util";
|
import { Heading1, usePageTitle } from "./util";
|
||||||
|
import OutfitPreview from "./components/OutfitPreview";
|
||||||
|
|
||||||
import HomepageSplashImg from "../images/homepage-splash.png";
|
import HomepageSplashImg from "../images/homepage-splash.png";
|
||||||
import HomepageSplashImg2x from "../images/homepage-splash@2x.png";
|
import HomepageSplashImg2x from "../images/homepage-splash@2x.png";
|
||||||
|
@ -14,6 +15,8 @@ import SpeciesColorPicker from "./components/SpeciesColorPicker";
|
||||||
function HomePage() {
|
function HomePage() {
|
||||||
usePageTitle("Dress to Impress");
|
usePageTitle("Dress to Impress");
|
||||||
|
|
||||||
|
const [previewState, setPreviewState] = React.useState(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
color="green.800"
|
color="green.800"
|
||||||
|
@ -24,19 +27,31 @@ function HomePage() {
|
||||||
>
|
>
|
||||||
<Box height="8" />
|
<Box height="8" />
|
||||||
<Box
|
<Box
|
||||||
as="img"
|
|
||||||
src={HomepageSplashImg}
|
|
||||||
srcSet={`${HomepageSplashImg} 1x, ${HomepageSplashImg2x} 2x`}
|
|
||||||
alt=""
|
|
||||||
width="200px"
|
width="200px"
|
||||||
height="200px"
|
height="200px"
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
boxShadow="md"
|
boxShadow="md"
|
||||||
|
overflow="hidden"
|
||||||
|
>
|
||||||
|
<OutfitPreview
|
||||||
|
speciesId={previewState?.speciesId}
|
||||||
|
colorId={previewState?.colorId}
|
||||||
|
pose={previewState?.pose}
|
||||||
|
wornItemIds={[]}
|
||||||
|
placeholder={
|
||||||
|
<Box
|
||||||
|
as="img"
|
||||||
|
src={HomepageSplashImg}
|
||||||
|
srcSet={`${HomepageSplashImg} 1x, ${HomepageSplashImg2x} 2x`}
|
||||||
|
alt=""
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
<Box height="4" />
|
<Box height="4" />
|
||||||
<Heading1>Dress to Impress</Heading1>
|
<Heading1>Dress to Impress</Heading1>
|
||||||
<Box height="8" />
|
<Box height="8" />
|
||||||
<StartOutfitForm />
|
<StartOutfitForm onChange={setPreviewState} />
|
||||||
<Box height="4" />
|
<Box height="4" />
|
||||||
<Box fontStyle="italic" fontSize="sm">
|
<Box fontStyle="italic" fontSize="sm">
|
||||||
or
|
or
|
||||||
|
@ -47,7 +62,7 @@ function HomePage() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function StartOutfitForm() {
|
function StartOutfitForm({ onChange }) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const idealPose = React.useMemo(
|
const idealPose = React.useMemo(
|
||||||
|
@ -89,6 +104,14 @@ function StartOutfitForm() {
|
||||||
setColorId(color.id);
|
setColorId(color.id);
|
||||||
setIsValid(isValid);
|
setIsValid(isValid);
|
||||||
setClosestPose(closestPose);
|
setClosestPose(closestPose);
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
onChange({
|
||||||
|
speciesId: species.id,
|
||||||
|
colorId: color.id,
|
||||||
|
pose: closestPose,
|
||||||
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Box width="4" />
|
<Box width="4" />
|
||||||
|
|
|
@ -12,10 +12,16 @@ import useOutfitAppearance from "./useOutfitAppearance";
|
||||||
* fetches the appearance data for it, and preloads and renders the layers
|
* fetches the appearance data for it, and preloads and renders the layers
|
||||||
* together.
|
* together.
|
||||||
*
|
*
|
||||||
|
* If the species/color/pose fields are null and a `placeholder` node is
|
||||||
|
* provided instead, we'll render the placeholder. And then, once those props
|
||||||
|
* become non-null, we'll keep showing the placeholder below the loading
|
||||||
|
* overlay until loading completes. (We use this on the homepage to show the
|
||||||
|
* beach splash until outfit data arrives!)
|
||||||
|
*
|
||||||
* TODO: There's some duplicate work happening in useOutfitAppearance and
|
* TODO: There's some duplicate work happening in useOutfitAppearance and
|
||||||
* useOutfitState both getting appearance data on first load...
|
* useOutfitState both getting appearance data on first load...
|
||||||
*/
|
*/
|
||||||
function OutfitPreview({ speciesId, colorId, pose, wornItemIds }) {
|
function OutfitPreview({ speciesId, colorId, pose, wornItemIds, placeholder }) {
|
||||||
const { loading, error, visibleLayers } = useOutfitAppearance({
|
const { loading, error, visibleLayers } = useOutfitAppearance({
|
||||||
speciesId,
|
speciesId,
|
||||||
colorId,
|
colorId,
|
||||||
|
@ -43,6 +49,7 @@ function OutfitPreview({ speciesId, colorId, pose, wornItemIds }) {
|
||||||
<OutfitLayers
|
<OutfitLayers
|
||||||
loading={loading || loading2}
|
loading={loading || loading2}
|
||||||
visibleLayers={loadedLayers}
|
visibleLayers={loadedLayers}
|
||||||
|
placeholder={placeholder}
|
||||||
doAnimations
|
doAnimations
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -52,9 +59,34 @@ function OutfitPreview({ speciesId, colorId, pose, wornItemIds }) {
|
||||||
* OutfitLayers is the raw UI component for rendering outfit layers. It's
|
* OutfitLayers is the raw UI component for rendering outfit layers. It's
|
||||||
* used both in the main outfit preview, and in other minor UIs!
|
* used both in the main outfit preview, and in other minor UIs!
|
||||||
*/
|
*/
|
||||||
export function OutfitLayers({ loading, visibleLayers, doAnimations = false }) {
|
export function OutfitLayers({
|
||||||
|
loading,
|
||||||
|
visibleLayers,
|
||||||
|
placeholder,
|
||||||
|
doAnimations = false,
|
||||||
|
}) {
|
||||||
|
const [isMounted, setIsMounted] = React.useState(false);
|
||||||
|
React.useEffect(() => {
|
||||||
|
setTimeout(() => setIsMounted(true), 0);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
console.log("opacity", isMounted && loading ? 1 : 0);
|
||||||
|
console.log("transition delay", loading ? "0.5s" : "0s");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box pos="relative" height="100%" width="100%">
|
<Box pos="relative" height="100%" width="100%">
|
||||||
|
{placeholder && (
|
||||||
|
<FullScreenCenter>
|
||||||
|
<Box
|
||||||
|
// We show the placeholder until there are visible layers, at which
|
||||||
|
// point we fade it out.
|
||||||
|
opacity={visibleLayers.length === 0 ? 1 : 0}
|
||||||
|
transition="opacity 0.2s"
|
||||||
|
>
|
||||||
|
{placeholder}
|
||||||
|
</Box>
|
||||||
|
</FullScreenCenter>
|
||||||
|
)}
|
||||||
<TransitionGroup enter={false} exit={doAnimations}>
|
<TransitionGroup enter={false} exit={doAnimations}>
|
||||||
{visibleLayers.map((layer) => (
|
{visibleLayers.map((layer) => (
|
||||||
<CSSTransition
|
<CSSTransition
|
||||||
|
@ -114,8 +146,13 @@ export function OutfitLayers({ loading, visibleLayers, doAnimations = false }) {
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
<Box
|
<Box
|
||||||
// This is similar to our Delay util component, but Delay disappears
|
// This is similar to our Delay util component, but Delay disappears
|
||||||
// immediately on load, whereas we want this to fade out smoothly.
|
// immediately on load, whereas we want this to fade out smoothly. We
|
||||||
opacity={loading ? 1 : 0}
|
// also delay the fade-in by 0.5s, but don't delay the fade-out at all.
|
||||||
|
//
|
||||||
|
// We also use `isMounted` here to make sure it actually _fades_ in!
|
||||||
|
// (This starts the opacity at 0, then fires an immediate callback to
|
||||||
|
// set it to 1, triggering the transition.)
|
||||||
|
opacity={isMounted && loading ? 1 : 0}
|
||||||
transition={`opacity 0.2s ${loading ? "0.5s" : "0s"}`}
|
transition={`opacity 0.2s ${loading ? "0.5s" : "0s"}`}
|
||||||
>
|
>
|
||||||
<FullScreenCenter>
|
<FullScreenCenter>
|
||||||
|
|
|
@ -38,6 +38,7 @@ export default function useOutfitAppearance(outfitState) {
|
||||||
colorId,
|
colorId,
|
||||||
pose,
|
pose,
|
||||||
},
|
},
|
||||||
|
skip: speciesId == null || colorId == null || pose == null,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue