Portafolio 3D

Starter pack

Interfaz

Ahora que tenemos nuestra escena 3D principalmente terminada, podemos empezar a trabajar en la interfaz HTML.

Vamos a crear un componente Interface.jsx con las mismas 4 secciones que nuestra escena 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>
  );
};

Usaremos las clases section--bottom, section--right y section--left para posicionar el contenido dentro de las secciones.

Por el momento, solo hemos a帽adido los nombres de las secciones, agregaremos el contenido m谩s tarde.

Vamos a agregar nuestro componente Interface en el componente 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;

Para estilizar nuestra interfaz HTML, usaremos CSS puro para ser lo m谩s gen茅ricos posible. Puedes usar tu framework de CSS favorito si lo deseas. (Yo us茅 TailwindCSS en la mayor铆a de mis proyectos)

Vamos a agregar los estilos predeterminados a nuestro archivo 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;
}

Importamos la fuente Roboto Slab de Google Fonts y definimos algunas variables de colores.

El contenedor de las secciones est谩 centrado y tiene un max-width de 1200px para mantener una buena legibilidad en pantallas grandes.

Las secciones tienen una height de 100vh (altura completa) y est谩n centradas por defecto. Usaremos las clases section--top, section--bottom, section--right y section--left para posicionar el contenido dentro de las secciones.

隆Nuestra interfaz est谩 lista, vamos a agregar el contenido!

Indicador de desplazamiento en la p谩gina de inicio

En la secci贸n de inicio, a帽adiremos un indicador de desplazamiento para decirle al usuario que puede desplazarse para ver las otras secciones.

Primero, vamos a crear un estado para saber si el usuario ha hecho scroll:

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

Luego, en la secci贸n de inicio, podemos agregar un motion.div con un objeto variants para crear un indicador de desplazamiento animado:

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

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

Estamos usando framer motion para animar la opacidad y la posici贸n de la rueda. Para hacer que la rueda se mueva hacia arriba y hacia abajo, usamos las propiedades repeat y repeatType.

End of lesson preview

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