3D 포트폴리오

Starter pack

인터페이스

이제 3D 장면이 주로 완성되었으므로 HTML 인터페이스 작업을 시작할 수 있습니다.

Interface.jsx 컴포넌트를 만들고 3D 장면과 동일한 4개의 섹션을 추가합시다:

export const Interface = () => {
  return (
    <div className="interface">
      <div className="sections">
        {/* HOME */}
        <section className="section section--bottom">HOME</section>
        {/* SKILLS */}
        <section className="section section--right">SKILLS</section>
        {/* PROJECTS */}
        <section className="section section--left">PROJECTS</section>
        {/* CONTACT */}
        <section className="section section--left">CONTACT</section>
      </div>
    </div>
  );
};

section--bottom, section--right, section--left 클래스를 사용하여 섹션 내부의 콘텐츠를 포지셔닝하겠습니다.

현재는 섹션 이름만 추가했으며, 나중에 콘텐츠를 추가할 것입니다.

이제 Interface 컴포넌트를 App 컴포넌트에 추가합시다:

import { Scroll, ScrollControls } from "@react-three/drei";
import { Canvas } from "@react-three/fiber";
import { MotionConfig } from "framer-motion";
import { Experience } from "./components/Experience";
import { config } from "./config";
import { Interface } from "./components/Interface";

function App() {
  return (
    <>
      <Canvas camera={{ position: [0, 0.5, 5], fov: 42 }}>
        <color attach="background" args={["#f5f3ee"]} />
        <fog attach="fog" args={["#f5f3ee", 10, 50]} />
        <ScrollControls
          pages={config.sections.length}
          damping={0.1}
          maxSpeed={0.2}
        >
          <group position-y={-1}>
            <MotionConfig
              transition={{
                duration: 0.6,
              }}
            >
              <Experience />
            </MotionConfig>
          </group>
          <Scroll html>
            <Interface />
          </Scroll>
        </ScrollControls>
      </Canvas>
    </>
  );
}

export default App;

HTML 인터페이스를 스타일링하기 위해 바닐라 CSS를 사용하여 가능한 한 일반적으로 만들겠습니다. 선호하는 CSS 프레임워크를 사용할 수 있습니다. (저는 대부분의 프로젝트에서 TailwindCSS를 사용했습니다)

index.css 파일에 기본 스타일을 추가합시다:

@import url("https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;700&display=swap");

:root {
  --primary-color: #4668ee;
  --text-color: #1a202c;
  --text-light-color: #555;
}

#root {
  width: 100vw;
  height: 100vh;
}

body {
  margin: 0;
  font-family: "Roboto Slab", serif;
}

/* ... */
.interface {
  width: 100vw;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.sections {
  max-width: 1200px;
  width: 100%;
}

.section {
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

.section--top {
  align-items: flex-start;
}

.section--bottom {
  align-items: flex-end;
}

.section--right {
  justify-content: flex-end;
}

.section--left {
  justify-content: flex-start;
}

Google Fonts에서 Roboto Slab 글꼴을 가져와 몇 가지 색상 변수를 정의했습니다.

섹션 컨테이너는 중앙에 위치하며, 큰 화면에서 가독성을 유지하기 위해 max-width1200px로 설정되었습니다.

섹션은 기본적으로 중심에 위치하며, 100vh (전체 높이)height를 가지고 있습니다. section--top, section--bottom, section--right, section--left 클래스를 사용하여 섹션 내부의 콘텐츠를 포지셔닝할 것입니다.

인터페이스가 준비되었습니다. 이제 콘텐츠를 추가합시다!

홈 스크롤 인디케이터

홈 섹션에서 스크롤 인디케이터를 추가하여 사용자가 다른 섹션을 보려면 스크롤해야 한다는 것을 알려줍니다.

먼저, 사용자가 스크롤했는지 여부를 알기 위한 상태를 생성해보겠습니다:

import { useScroll } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import { useState } from "react";

export const Interface = () => {
  const scrollData = useScroll();
  const [hasScrolled, setHasScrolled] = useState(false);
  useFrame(() => {
    setHasScrolled(scrollData.offset > 0);
  });
  // ...
};

그런 다음 홈 섹션에서 variants 객체가 있는 motion.div를 추가하여 애니메이션 스크롤 인디케이터를 만들 수 있습니다:

// ...
import { motion } from "framer-motion";

export const Interface = () => {
  // ...
  return (
    <div className="interface">
      <div className="sections">
        {/* HOME */}
        <section className="section section--bottom">
          <motion.div
            className="scroll-down"
            initial={{
              opacity: 0,
            }}
            animate={{
              opacity: hasScrolled ? 0 : 1,
            }}
          >
            <motion.div
              className="scroll-down__wheel"
              initial={{
                translateY: 0,
              }}
              animate={{
                translateY: 4,
              }}
              transition={{
                duration: 0.4,
                repeatDelay: 0.5,
                repeatType: "reverse",
                repeat: Infinity,
              }}
            ></motion.div>
          </motion.div>
        </section>
        {/* ... */}
      </div>
    </div>
  );
};

우리는 framer motion을 사용하여 불투명도 및 휠 위치를 애니메이션화합니다. 휠이 위아래로 움직이게 하려면 repeatrepeatType 속성을 사용합니다.

Three.js logoReact logo

React Three Fiber: The Ultimate Guide to 3D Web Development

✨ You have reached the end of the preview ✨

Go to the next level with Three.js and React Three Fiber!

Get full access to this lesson and the complete course when you enroll:

  • 🔓 Full lesson videos with no limits
  • 💻 Access to the final source code
  • 🎓 Course progress tracking & completion
  • 💬 Invite to our private Discord community
Unlock the Full Course – Just $85

One-time payment. Lifetime updates included.