Skip to content

Commit

Permalink
feat: add 3d motion page template
Browse files Browse the repository at this point in the history
  • Loading branch information
071yoon committed Jan 29, 2024
1 parent 71958b3 commit c1ea57d
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 9 deletions.
72 changes: 72 additions & 0 deletions src/app/3d/gsapOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { easingOptions, gsapOptionsTypeObj } from "../../types/gsapOption";

export const gsapOptions: gsapOptionsTypeObj[] = [
{
type: "xFrom",
min: -100,
max: 100,
componentType: "slider",
step: 1,
default: -100,
},
{
type: "xTo",
min: -100,
max: 100,
componentType: "none",
default: 0,
},
{
type: "yFrom",
min: -100,
max: 100,
componentType: "slider",
step: 1,
default: 0,
},
{
type: "yTo",
min: -100,
max: 100,
componentType: "none",
default: 0,
},
{
type: "opacityFrom",
min: 0,
max: 1,
componentType: "none",
step: 0.1,
default: 0,
},
{
type: "opacityTo",
min: 0,
max: 1,
componentType: "slider",
step: 0.1,
default: 1,
},
{
type: "duration",
min: 0,
max: 2,
componentType: "slider",
step: 0.1,
default: 0.5,
},
{
type: "ease",
componentType: "select",
options: easingOptions,
default: "power2",
},
{
type: "stagger",
min: 0,
max: 1,
componentType: "slider",
step: 0.1,
default: 0.1,
},
];
171 changes: 171 additions & 0 deletions src/app/3d/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
"use client";
import styled from "@emotion/styled";
import { gsap } from "gsap";
import { Button, Divider } from "@mui/joy";
import { useEffect, useRef, useState } from "react";
import { useDebounce } from "@toss/react";
import { gsapOptions } from "./gsapOptions";
import {
GsapSlider,
GsapSelect,
PlaygroundContainer,
IPhoneX,
InsideIPhone,
} from "@/components/playground-page";
import useTransitionToCode from "@hooks/useTransitionToCode";
import { getGsapData } from "@utils/getGsapData";

export default function Block() {
const [counter, setCounter] = useState(0);
const [isCode, setIsCode] = useState(false);
const animationRef = useRef<HTMLDivElement>(null);
const iPhoneCodeRef = useRef<HTMLDivElement>(null);
const [gsapStates, setGsapStates] = useState(
gsapOptions.reduce((acc, cur) => {
acc[cur.type] = cur.default;
return acc;
//! FIXME
}, {} as any)
);

useTransitionToCode({
isCode,
mainRef: animationRef,
codeRef: iPhoneCodeRef,
});

const onChangeSlider =
(type: string) => (_event: Event, newValue: number | number[]) => {
if (typeof newValue === "number")
setGsapStates({ ...gsapStates, [type]: newValue });
};

const handleGsapAnimation = useDebounce((timeline) => {
timeline.play();
}, 300);

useEffect(() => {
const gsapData = getGsapData(gsapStates);
const animationChildren = animationRef.current?.children;
const ctx = gsap.context(() => {
if (animationChildren) {
const timeline = gsap.fromTo(
animationChildren,
{
...gsapData.from,
},
{
stagger: 0.1,
...gsapData.to,
...gsapData.rest,
}
);
timeline.pause();
handleGsapAnimation(timeline);
}
});
return () => {
ctx.revert();
};
}, [counter, gsapStates, handleGsapAnimation]);

return (
<PlaygroundContainer>
<OptionsContainer>
<Button onClick={() => setCounter(counter + 1)}>replay</Button>
{gsapOptions.map((item) => {
if (item.componentType === "select") {
return (
<GsapSelect
key={item.type}
item={item}
gsapStates={gsapStates}
setGsapStates={setGsapStates}
/>
);
}
})}
<Divider />
{gsapOptions.map((item) => {
if (item.componentType === "slider")
return (
<GsapSlider
key={item.type}
type={item.type}
set={onChangeSlider(item.type)}
val={gsapStates}
target={item}
/>
);
})}
</OptionsContainer>
<IPhoneX>
<InsideIPhone
setIsCode={setIsCode}
gsapStates={gsapStates}
animationRef={animationRef}
codeRef={iPhoneCodeRef}
styleOverride={{
width: "100%",
height: "80%",
display: "flex",
flexDirection: "column",
gap: "1rem",
justifyContent: "center",
alignItems: "center",
}}
isBlock
>
<div
style={{
width: "250px",
height: "70px",
backgroundColor: "#a9d3f3",
borderRadius: "1rem",
}}
/>
<div
style={{
width: "250px",
height: "70px",
backgroundColor: "#a9d3f3",
borderRadius: "1rem",
}}
/>
<div
style={{
width: "250px",
height: "70px",
backgroundColor: "#a9d3f3",
borderRadius: "1rem",
}}
/>
<div
style={{
width: "250px",
height: "70px",
backgroundColor: "#a9d3f3",
borderRadius: "1rem",
}}
/>
<div
style={{
width: "250px",
height: "70px",
backgroundColor: "#a9d3f3",
borderRadius: "1rem",
}}
/>
</InsideIPhone>
</IPhoneX>
</PlaygroundContainer>
);
}

const OptionsContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 1rem;
`;
32 changes: 23 additions & 9 deletions src/components/main-page/ThreeDimension.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function ThreeDimension() {
isMountRef.current = true;
gsap.set(cube2Ref.current, {
rotateY: "-90deg",
x: "100px",
x: "80px",
opacity: 1,
});
gsap.set(cube1Ref.current, { opacity: 1 });
Expand All @@ -24,14 +24,14 @@ export default function ThreeDimension() {
if (isHover) {
gsap.to(cube1Ref.current, {
rotateY: "90deg",
x: "-100px",
x: "-80px",
duration: 0.5,
});
gsap.fromTo(
cube2Ref.current,
{
rotateY: "-90deg",
x: "100px",
x: "80px",
duration: 0,
},
{
Expand All @@ -43,14 +43,14 @@ export default function ThreeDimension() {
} else {
gsap.to(cube2Ref.current, {
rotateY: "-90deg",
x: "100px",
x: "80px",
duration: 0.5,
});
gsap.fromTo(
cube1Ref.current,
{
rotateY: "90deg",
x: "-100px",
x: "-80px",
duration: 0,
},
{
Expand All @@ -69,8 +69,12 @@ export default function ThreeDimension() {
navigateTo="3d"
>
<Container>
<Cube1 className="cube-1" ref={cube1Ref} />
<Cube2 className="cube-2" ref={cube2Ref} />
<Cube1 className="cube-1" ref={cube1Ref}>
3D
</Cube1>
<Cube2 className="cube-2" ref={cube2Ref}>
Motion
</Cube2>
</Container>
</PageContainer>
);
Expand All @@ -88,12 +92,22 @@ const Cube1 = styled.div`
position: absolute;
width: 10rem;
height: 10rem;
background-color: #cccccc;
background-color: #fc9bff;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
font-weight: bold;
`;

const Cube2 = styled.div<{ isHover?: boolean }>`
position: absolute;
width: 10rem;
height: 10rem;
background-color: #777777;
background-color: #96d8ff;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
font-weight: bold;
`;

0 comments on commit c1ea57d

Please sign in to comment.