Portfolio 3D

Starter pack

Interface

Maintenant que notre scène 3D est principalement terminée, nous pouvons commencer à travailler sur l'interface HTML.

Créons un composant Interface.jsx avec les 4 mêmes sections que notre scène 3D :

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>
  );
};

Nous utiliserons les classes section--bottom, section--right et section--left pour positionner le contenu à l'intérieur des sections.

Pour le moment, nous avons seulement ajouté les noms des sections, nous ajouterons le contenu plus tard.

Ajoutons notre composant Interface dans le composant 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;

Pour styliser notre interface HTML, nous utiliserons le CSS vanilla pour être le plus générique possible. Vous pouvez utiliser votre framework CSS préféré si vous le souhaitez. (J'ai utilisé TailwindCSS sur la plupart de mes projets)

Ajoutons les styles de base Ă  notre fichier 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;
}

Nous avons importé la police Roboto Slab de Google Fonts et défini quelques variables de couleurs.

Le conteneur des sections est centré et a une max-width de 1200px pour maintenir une bonne lisibilité sur les grands écrans.

Les sections ont une height de 100vh (hauteur complète) et sont centrées par défaut. Nous utiliserons les classes section--top, section--bottom, section--right et section--left pour positionner le contenu à l'intérieur des sections.

Notre interface est prĂŞte, ajoutons le contenu !

Indicateur de défilement d'accueil

Sur la section d'accueil, nous allons ajouter un indicateur de défilement pour informer l'utilisateur qu'il peut faire défiler pour voir les autres sections.

Tout d'abord, créons un état pour savoir si l'utilisateur a fait défiler :

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);
  });
  // ...
};

Ensuite, dans la section d'accueil, nous pouvons ajouter un motion.div avec un objet variants pour créer un indicateur de défilement animé :

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

export const Interface = () => {
  // ...
  return (
    <div className="interface">
      <div className="sections">
        {/* ACCUEIL */}
        <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>
  );
};

Nous utilisons framer motion pour animer l'opacité et la position de la roue. Pour faire bouger la roue de haut en bas, nous utilisons les propriétés repeat et repeatType.

End of lesson preview

To get access to the entire lesson, you need to purchase the course.