Introducción

Three.js es una librería escrita en Javascript que nos permite acceder a toda la funcionalidad de WebGL de una manera más sencilla. Para los que no conocen WebGL, se trata de una especificación estándar que posee una API en Javascript que sirve para renderizar gráficos en 3D en la web. Las posibilidades creativas son infinitas, pero tiene una curva de aprendizaje que se vuelve más empinada cuanto más complejas queremos hacer nuestras aplicaciones. Es por este motivo que existen pocos sitios y aplicaciones web que sacan el máximo partido de la librería.

Frente a esta realidad, aparecen esfuerzos como los de Paul Henschel, creador de librerías tales como react-spring y react-three-fiber, que abstraen un poco más la implementación de Three.js y nos permiten escribir nuestras aplicaciones de manera declarativa utilizando React en forma de componentes reutilizables. Esto hace nuestro código mucho más fácil de manipular y leer. Los constantes esfuerzos de esta comunidad me llevaron a darle una chance para un proyecto de investigación personal, que desglosaremos a continuación.

Antes de comenzar, unas palabras de advertencia: Esta demo es básica y me sirvió para introducirme tanto a Three.js como a React a la vez. No planeo entrar en detalles acerca de cómo crear una app en React, sino más bien mostrar algunos puntos clave de react-three-fiber. Todo lo que verán a continuación es un conjunto de hacks y workarounds propios de un principiante.

Creando una experiencia en 3D

En Three.js todo aquello que corresponde al mundo 3D (de ahora en más “la escena”) se encapsula dentro de un elemento Canvas y lo que esté ahí dentro se renderiza cada frame. Este tag separa aquello que pertenece al dominio de Three.js de aquello que pertenece al dominio del DOM. Más adelante mostraré de qué manera pueden interactuar entre sí.

Todos los elementos de la escena 3D se ubican dentro de Canvas

Dentro del Canvas es posible insertar declarativamente todo aquello que ofrece la API de Three.js. Basta con agregar la etiqueta con su import correspondiente. En la imagen anterior puede verse una luz de ambiente, una luz de punto y más controles que forman parte de la escena. Los componentes de react-three-fiber son equivalentes a los de Three.js y operan de la misma manera internamente. Es posible acceder a ellos desde el código por medio del hook useRef y realizar acciones sobre ellos de manera imperativa. La documentación oficial de Three.js es extensiva y su API puede explorarse desde la web oficial.

Es posible crear nuestros componentes personalizados con su lógica pero muchas de las recetas típicas de Three.js como ser controles de cámara, hooks útiles y otras cosas más se pueden importar desde la librería drei, como por ejemplo el fondo estrellado de nuestra escena. Estas recetas son propuestas y mantenidas por la comunidad.

Carga de modelos 3D con React Suspense

La forma recomendada de cargar assets asincrónicamente en esta clase de proyectos es por medio del componente Suspense. Aquellas librerías que lo soportan hace que resulte muy natural incluirlo en el proyecto. Permite realizar un fetch de datos y mostrar un componente Fallback mientras tanto. De esta manera, es posible renderizar otras partes de nuestra app a medida que hay conexión o se descargan los datos, sin preocuparnos por condiciones de carrera.

Suspense nos permite cargar datos (en este caso un modelo 3D) asincrónicamente.

En la imagen anterior realizamos la carga del componente Room, el cual internamente ejecuta una operación que puede tardar: la carga de un modelo .gltf. En el prop fallback irá nuestro código o componente que renderice un indicador de carga (sin progreso) mientras esperamos.

El modelo 3D

El modelo 3D fue obtenido de SketchFab y pueden conseguirlo aquí. Para incluirlo más fácilmente en react-three-fiber es posible utilizar un comando que nos convierte cualquier modelo .gltf en un componente React, con la posibilidad de referenciar las mallas individuales y los materiales del modelo, borrar, renderizar condicionalmente y mucho más. La librería que hace esto posible es gltfjsx.

La librería gltfjsx crea un componente funcional que realiza la carga del modelo

HTML y Three.js

Es posible “incluir” elementos HTML dentro del Canvas. Es una de las recetas de la librería drei. Podemos manejar sus estilos CSS y pasarles props. La única limitación es que éstos deben estar anclados a un punto expecífico de la escena 3D. Para lograr esto, una de las opciones es crear una malla vacía la cual recibe en sus props un vector de ubicación y dentro de ella renderizar el componente Html, el cual que puede recibir, bueno… HTML 😀

El componente mesh proporciona un anclaje del HTML al mundo 3D

Animaciones en nuestra escena 3D

Las animaciones en nuestra escena pueden ser complejas, y debuggear frame por frame puede volverse una pesadilla. Es por esto que el creador de react-three-fiber nos brinda una solución no sólo a nosotros sino a cualquier persona que quiera incluir animaciones basadas en física (y también animacion clásica) en su proyecto React.

React Spring es una librería que soporta tanto React como React Native, y funciona en prácticamente todos los navegadores. Es muy fácil de incluir y utilizar, como veremos a continuación. Para el siguiente ejemplo utilizaremos el código del componente Marker:

Definición de una animación en React Spring

Lo primero es definir nuestras animaciones. El hook useSpring nos pide como mínimo un valor inicial (que puede ser un objeto con estilos o alguna propiedad personalizada, o lo que queramos) y un valor final, del mismo tipo. El valor inicial se declara en la propiedad from, y el final en la propiedad to, o bien, directamente como hacemos nosotros. La propiedad config es un objeto que puede contener las propiedades físicas de esa animación como su masa, tensión y fricción, o bien una duración si lo que queremos es una animación clásica con easing. Es posible alterar las propiedades de estas animaciones en base a variables de estado internas o props que reciba nuestro componente, como se ve en la imagen.

El paso siguiente es definir nuestros componentes como suceptibles de animación. Para ello, podemos añadir el prefijo animated a la mayoría de nuestros componentes. En el caso de componentes custom es posible utilizar la función animated para convertirlos en componentes animables. Lo que nos devuelve esta función es un componente que puede ser animado por react-spring fuera del ciclo de vida de los componentes de React.

El paso final es, como se ve en la imagen anterior, pasar como props de nuestro componente animado aquellas propiedades que queremos animar. Cada vez que el estado del componente cambia, el spring ejecuta la animación configurada e interpola entre los valores from y to. Parece magia.

Conclusiones

Este conjunto de librerías parecen funcionar muy bien entre sí. Están creciendo día a día y con el soporte de la comunidad es muy posible que se vuelvan populares pronto. Por el momento, es posible clonar el código de este experimento desde aquí.

Share:

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *