Transiciones con shaders

Starter pack

En esta lección aprenderemos a crear transiciones usando shaders.

El primer efecto que crearemos es un efecto de transición de pantalla:

Tenemos control total sobre el efecto de transición, podemos cambiar la duración, la función de easing, y el shader en sí.

El segundo efecto que crearemos es un efecto de transición de modelo:

Cuando se desvanece, el modelo se disuelve y los colores se desvanecen en blanco, lo opuesto ocurre al aparecer.

Paquete inicial

Aquí están todos los modelos 3D que usaremos en esta lección por Ergoni y con licencia de Creative Commons Attribution:

El paquete inicial utiliza los siguientes paquetes:

Las dos fuentes utilizadas son Kanit y Rubik Doodle Shadow, ambas de Google Fonts.

Siéntete libre de ajustar cualquier componente a tus preferencias.

Puedes ajustar los colores jugando con los controles de Leva en la esquina superior derecha. Una vez que encuentres los colores que te gusten, añade la propiedad hidden al componente Leva en App.jsx:

// ...

function App() {
  // ...
  return (
    <>
      <Leva hidden />
      {/* ... */}
    </>
  );
}

// ...

Transición de pantalla

Empecemos creando nuestro efecto de transición de pantalla. Vamos a crear un componente llamado ScreenTransition que manejará el efecto de transición.

Vamos a crear un nuevo archivo llamado ScreenTransition.jsx en la carpeta components:

export const ScreenTransition = ({ transition, color }) => {
  return (
    <mesh>
      <planeGeometry args={[2, 2]} />
      <meshBasicMaterial color={color} />
    </mesh>
  );
};

En este componente, estamos creando un plano con un color que cubrirá toda la pantalla. Utilizaremos este plano para hacer la transición entre pantallas.

Nuestro componente toma dos props:

  • transition: un booleano para saber si el componente debe mostrar el efecto de transición
  • color: el color principal del efecto de transición

Añadamos el componente ScreenTransition a App.jsx:

// ...
import { ScreenTransition } from "./components/ScreenTransition";

function App() {
  // ...
  return (
    <>
      {/* ... */}
      <Canvas camera={{ position: [0, 1.8, 5], fov: 42 }}>
        <color attach="background" args={[backgroundColor]} />
        <fog attach="fog" args={[backgroundColor, 5, 12]} />
        <ScreenTransition transition color="#a5b4fc" />
        <Suspense>
          <Experience />
        </Suspense>
      </Canvas>
    </>
  );
}

export default App;

Por ahora, forzamos que se muestre el efecto de transición pasando true a la prop transition. Añadiremos la lógica para controlar el efecto de transición más tarde.

Ajusta el color a tu gusto. Si lo cambias, asegúrate de actualizar el color en el archivo index.css para evitar un destello de color cuando cargue la página web:

// ...

html {
  background-color: #a5b4fc;
}

Plane in middle of the scene

El plano está en el medio de la escena.

Plano a pantalla completa

Queremos que nuestro plano cubra toda la pantalla. Podríamos usar las dimensiones del viewport y hacer que mire hacia la cámara, pero también queremos que esté encima de todo.

Para lograr esto, Drei proporciona un componente Hud que renderizará a sus hijos encima de todo. Podemos agregar una cámara fija al componente Hud para asegurarnos de que esté perfectamente alineada con nuestro plano:

import { Hud, OrthographicCamera } from "@react-three/drei";

export const ScreenTransition = ({ transition, color }) => {
  return (
    <Hud>
      <OrthographicCamera
        makeDefault
        top={1}
        right={1}
        bottom={-1}
        left={-1}
        near={0}
        far={1}
      />
      <mesh>
        <planeGeometry args={[2, 2]} />
        <meshBasicMaterial color={color} />
      </mesh>
    </Hud>
  );
};

Usamos una OrthographicCamera para cubrir fácilmente toda la pantalla, independientemente de la distancia entre la cámara y el plano.

Plane covering the entire screen

El plano ahora cubre toda la pantalla.

Lógica de transición

Una última cosa antes de crear nuestro shader personalizado, vamos a definir un estado de transición en UI.jsx:

// ...
import { atom } from "jotai";

export const transitionAtom = atom(true);
// ...

Establecemos el valor inicial en true para comenzar con un efecto de desvanecimiento después de que el sitio web se cargue.

Usaremos este estado para controlar el efecto de transición.

Aún en UI.jsx, definiremos dos constantes para controlar la duración y el retraso del efecto de transición:

// ...
export const TRANSITION_DELAY = 0.8;
export const TRANSITION_DURATION = 3.2;
// ...

Ahora, reemplazaremos la llamada a la función setScreen con una nueva que manejará el efecto de transición:

// ...
import { useAtom } from "jotai";
import { useRef } from "react";
// ...

export const UI = () => {
  // ...
  const [transition, setTransition] = useAtom(transitionAtom);
  const timeout = useRef();

  // ...
  const transitionToScreen = (newScreen) => {
    setTransition(true);
    clearTimeout(timeout.current);
    timeout.current = setTimeout(() => {
      setScreen(newScreen);
      setTransition(false);
    }, TRANSITION_DURATION * 1000 + TRANSITION_DELAY * 1000);
  };
  // ...
};

En lugar de establecer directamente la nueva pantalla, configuramos el estado de transición a true y usamos un setTimeout para establecer la nueva pantalla después de que se complete el efecto de transición (la duración del efecto de transición más el retraso).

Ahora nuestra función está lista, busca las llamadas a setScreen en el componente UI y reemplázalas con transitionToScreen.

De home a menu:

<motion.button
  onClick={() => transitionToScreen("menu")}
  // ...
>

Y de menu a home:

<motion.button
onClick={() => transitionToScreen("home")}
// ...
>
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.