Recientemente, el desarrollo de aplicaciones web ha visto un número cada vez mayor de esfuerzos para mejorar la experiencia del usuario utilizando gráficos. Con React Three Fiber (R3F), puede utilizar fácilmente las características de tres.js como un componente React, lo que facilita a cualquier persona lograr expresiones atractivas.
explicaremos en detalle cómo combinar la biblioteca de envoltura drei y mecanografiado, e implementar el color de píxeles generado automáticamente a partir de imágenes
📌 Lo que es más, esta implementación no utilizará ningún modelo 3D especial, y solo usará la geometría básica (caja, plano, etc.) proporcionada por tres.js como estándar, para crear expresiones retro pero sofisticadas exclusivas del arte de píxeles.
Las explicaciones paso a paso son fáciles de entender incluso para principiantes, y el contenido es para que pueda aprender mientras trabaja en sus manos, ¡así que asegúrese de intentarlo!
💡 Imagen completa
📺 Mire la demostración en YouTube : puede verlo desde este enlace

¡Continuaremos creando lecciones y trabajos desde TypeScript X React Three Fiber en el futuro!
Haremos un anuncio en YouTube, así que suscríbase a nuestro canal de YouTube y espere notificaciones.
📺 Mira YouTube : puedes verlo desde este enlace
Si desea saber qué puede hacer React Three Fiber, ¡consulte lo siguiente!
¡Tenemos obras fáciles de usar disponibles!
- Traté de hacer que Bears caminara con React x tres.js!
- Intenté hacer que un viejo baile en React x tres.js!
- ¡Traté de mostrar un modelo 3D con React x tres.js!
- Hice un botón 3D que explota en React x tres.js!
- Reaccione tres fibra x drei x Introducción al mecanografiado! ¡Fondo 3D de estilo Poke Poke hecho con objetos estándar!
🚀 Introducción a elementos técnicos: herramientas y bibliotecas para usar en este proyecto
Puede cambiar las herramientas y las bibliotecas que usa a una que sea fácil de usar para usted, pero este proyecto explicará esta suposición.
- VCODE
-
- Un editor de código gratuito proporcionado por Microsoft.
- No necesita ser VScode, pero hay muchas extensiones, por lo que lo recomiendo.
- También se recomienda incluir Eslint o más bonito.
- Nodo.js
-
- Un tiempo de ejecución de JavaScript construido en el motor JavaScript V8 de Chrome .
- Puede ejecutar el código JavaScript fuera de su navegador.
- Esto se explica en base a la suposición de que ya está instalado, así que descargue desde
https://nodejs.org/ja *Recomendamos descargar la versión estable a largo plazo de LTS.
- Vite
-
- Una herramienta de compilación para proyectos web modernos. Se caracteriza por su rápida y liviana
- La "CRA (CRA-react-app)" utilizada anteriormente no figura en el sitio web oficial y se ha convertido en una tecnología antigua.
- De ahora en adelante, Vite debería ser la mejor opción al crear aplicaciones con React.
- Reaccionar
-
- Esta es una biblioteca JavaScript para construir una interfaz de usuario (interfaz de usuario). Fue desarrollado por Facebook y todavía se usa en muchas aplicaciones web hoy.
- Tres.js
-
- Una biblioteca JavaScript para crear fácilmente gráficos 3D. Abraza las operaciones complejas de WebGL y permite un desarrollo 3D intuitivo.
- Es fácil crear gráficos 3D y es más fácil de usar que las operaciones directas de WebGL.
- Reaccionar tres fibra
-
- Esta es una biblioteca que permite que tres.js se usen con React. Combina la estructura de componentes de React con el motor 3D de tres.js.
- Se pueden usar tres.js en el estilo de desarrollo React, lo que permite un desarrollo intuitivo y eficiente.
- Reaccionar tres drei
-
- Una colección de componentes útiles de utilidad para reaccionar tres fibra. Es fácil agregar las características de tres.js de uso común.
- Las características complejas de tres.js se pueden lograr con código corto, reduciendo los costos de aprendizaje.
🚀 ¡Haga "Pixel Art" con React Three Fiber × Drei × TypeScript!
📌 La construcción del medio ambiente se explica en este artículo

📌 Esta vez, no utilizaremos ningún modelo 3D especial, sino que solo usaremos objetos estándar que estén disponibles en React Three Fiber y React Three DREI.
Si desea obtener más información sobre los objetos estándar, consulte también este artículo

Consulte GitHub para ver el código fuente completo que creó esta vez.
💾 Repositorio de GitHub : ¡verifique el código fuente en este enlace
💡 ¡Cree la interfaz de usuario para el panel de control!
Primero, el panel de control crea una interfaz de usuario para que los usuarios carguen imágenes, establezcan tamaños de píxeles y operen varias animaciones.
// Importar reaccionar y tres.js bibliotecas relacionadas importe {useState} de "react"; // Configuración constante: const default_pixel_size = 64; // - default_pixel_size: tamaño predeterminado (recuento de píxeles) cuando se redujo una imagen // ==== // componentes de la aplicación: // - gestión central de la interfaz de usuario (carga de imagen, configuración de tamaño de píxel, operaciones de animación) // - colocar dos lienzos (escenas de píxeles y escenas de fondo) // === CON const App = () => {// // State Management: [TEMPPIYPIXELETSEMES) usestar<number> (Default_pixel_size); // - TEMPIXELSIZE: Tamaño de píxel temporal en el retorno del formulario de entrada (<div style={{ width: "100vw", height: "100vh" }}> {/* -------------------------------------------------------------------------------------------<div className="absolute top-4 left-4 bg-white shadow-lg p-4 rounded-lg z-10 w-64 space-y-4"> {/ * Seleccionar archivo de imagen */} <input type="file" accept="image/*" className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" /> {/ * Píxel Tamaño de tamaño Campo */}<div className="flex items-center space-x-2"> <label className="text-sm text-gray-700">Tamaño de píxel:</label><input type="number" value={tempPixelSize} onChange={(e) => setMppiXelSize (número (e.target.value))} min = "1" max = "128" classname = "W-16 borde-border-gris-300 redondeado md px-2 py-1 enfoque de texto-sm: anillo-2 enfoque: anillo-blue-500 enfoque: nonal" />>>>></div> {/ * Botón Redisplay */} <button className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 transition">Redisplay</button> {/ * Botón de control de animación */}<div className="flex flex-wrap gap-2"><button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition">Onda</button><button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition">de explosión</button></div></div></div> ); }; Exportar aplicación predeterminada;

Esta vez, quiero crear algo simple, así que lo dejaré así como una interfaz de usuario.
La explosión y la ola se utilizan para animar el arte de los píxeles.
Creo que la ventaja de crear Pixel Art con React Three Fiber es que puedes agregar animación al arte de píxeles.
💡 ¡Cree un evento de panel de control!
Pondré el evento en la interfaz de usuario que acabo de crear.
manifilateado
HandleFilechange es el proceso cuando se selecciona una imagen.
Seleccionar una imagen actualizará el tamaño de píxel y luego actualizará la fuente de la imagen.
// Importar reaccionar y tres.js bibliotecas relacionadas importe {useState} de "react"; // Configuración constante: const default_pixel_size = 64; // - default_pixel_size: tamaño predeterminado (recuento de píxeles) cuando se redujo una imagen // ==== // componentes de la aplicación: // - gestión central de la interfaz de usuario (carga de imagen, configuración de tamaño de píxel, operaciones de animación) // - colocar dos lienzos (escenas de píxeles y escenas de fondo) // === CON const App = () => {// // State Management: [TEMPPIYPIXELETSEMES) usestar<number> (Default_pixel_size); // - TEMPPIXELSIZE: Tamaño de píxel temporal en la entrada de entrada const [pixelsize, setPixelsize] = useState<number> (TEMPPIXELSIZE); // - TEMPIXELSIZE: tamaño de píxel temporal en el formulario de entrada const [ImagesRC, setImagesRC] = useState<string | null> (nulo); // - ImagesRC: URL de datos de la imagen importada // --- // qué hacer cuando se selecciona un archivo de imagen: // - Cargue el archivo, conviértelo en una URL de datos y configúrelo en ImagesRC // -----------------------------------------------------------------------------------------------------------------------------------------------------------<HTMLInputElement> ) => {const archivo = e.target.files?. [0]; if (! archivo) return; const lector = new FileReader (); Reader.onload = (ev) => {const url = ev.target? if (typeOf url! == "cadena") return; // Actualizar el tamaño de píxel y luego configure la fuente de imagen SetPixelsize (TEMPPIXELSIZE); setImagesrc (URL); }; lector.readasdataurl (archivo); }; devolver (<div style={{ width: "100vw", height: "100vh" }}> {/* -------------------------------------------------------------------------------------------<div className="absolute top-4 left-4 bg-white shadow-lg p-4 rounded-lg z-10 w-64 space-y-4"> {/ * Seleccionar archivo de imagen */} <input type="file" accept="image/*" onChange={handleFileChange} className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" /> {/ * Píxel Tamaño de tamaño Campo */} ...</div></div> ); }; Exportar aplicación predeterminada;
recarga
El siguiente es el evento del botón Redisplay.
Esto se usa al actualizar el tamaño del píxel y la re-retención al tamaño del píxel.
Esta es una forma que refleja Temppixelsize en Pixelsize.
// Importar reaccionar y tres.js bibliotecas relacionadas importe {useState} de "react"; // Configuración constante: const default_pixel_size = 64; // - default_pixel_size: tamaño predeterminado (recuento de píxeles) cuando se redujo una imagen // ==== // componentes de la aplicación: // - gestión central de la interfaz de usuario (carga de imagen, configuración de tamaño de píxel, operaciones de animación) // - colocar dos lienzos (escenas de píxeles y escenas de fondo) // === CON const App = () => {// // State Management: [TEMPPIYPIXELETSEMES) usestar<number> (Default_pixel_size); // - TEMPPIXELSIZE: Tamaño de píxel temporal en la entrada de entrada const [pixelsize, setPixelsize] = useState<number> (TEMPPIXELSIZE); // - TEMPIXELSIZE: tamaño de píxel temporal en el formulario de entrada const [ImagesRC, setImagesRC] = useState<string | null> (nulo); // - ImagesRC: URL de datos de la imagen importada // --- // qué hacer cuando se selecciona un archivo de imagen: // - Cargue el archivo, conviértelo en una URL de datos y configúrelo en ImagesRC // -----------------------------------------------------------------------------------------------------------------------------------------------------------<HTMLInputElement> ) => {...}; // ----- // Processing Al presionar el botón "Revisar": //-Vuelva a dibujar la imagen reflejando el valor de TEMPIXELSIZE en PixelSize // -----------------------------------------------------------------------------------------------------------------------------------------------------------<div style={{ width: "100vw", height: "100vh" }}> {/* -------------------------------------------------------------------------------------------<div className="absolute top-4 left-4 bg-white shadow-lg p-4 rounded-lg z-10 w-64 space-y-4"> ... {/ * Botón Redisplay */} <button className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 transition" onClick={reloadImage} >Redisplay</button> {/ * Botón de control de animación */}<div className="flex flex-wrap gap-2"><button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition">Onda</button><button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition">de explosión</button></div></div></div> ); }; Exportar aplicación predeterminada;
Controlexplosionanimation y control de vida
El evento de botón de control de animación se verá así:
dado que he establecido animation_time como constante, animaré en ese número de segundos, deshaceré en el mismo número de segundos y volveré al modo predeterminado.
// Importar reaccionar y tres.js bibliotecas relacionadas con importar {useState} de "react"; // Configuración constante: const default_pixel_size = 64; // - default_pixel_size: tamaño predeterminado (en píxeles) al reducir la imagen const animation_time = 3; // - Animation_time: Time (en segundos) a Animation // ==== // Componentes de la aplicación: // - Gestión central de la interfaz de usuario (Carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organizar 2 lienzo (escena de píxeles y escena de fondo) // ===== Const App = () => {// Managemento estatal: Const [TEMPIXELSIZE, SETMPEXELEDIEDIEDEDEDEDEDED]]<number> (Default_pixel_size); // - TEMPPIXELSIZE: Tamaño de píxel temporal en la entrada de entrada const [pixelsize, setPixelsize] = useState<number> (TEMPPIXELSIZE); // - TEMPIXELSIZE: tamaño de píxel temporal en el formulario de entrada const [ImagesRC, setImagesRC] = useState<string | null> (nulo); // - ImagesRC: URL de datos de la imagen importada const [animación, setanimation] = useState ("predeterminado"); // - Animación: modo de animación actual // ---- // Qué hacer cuando se selecciona un archivo de imagen: // - Cargue el archivo, conviértalo en una URL de datos y configúrelo en ImagesRC // -----------------------------------------------------------------------------------------------------------------------------------------------------------<HTMLInputElement> ) => {...}; // ----- // Processing Al presionar el botón "Revisión": //-Repinta la imagen reflejando el valor de TEMPIXELSIZE en PixelSize // --------------------------------------------------------------------------------------------------------------------------------------------------- Control de animación: // - Comience, termine y deshace la explosión secuencialmente con Tiempo de espera // ----------------------------------------------------------------------- ----------------------------------------------------------------------- setanimation ("predeterminado"); }, Animation_time * 2000); }; // ----- // Animación de onda de control: //-Iniciar, finalizar y deshacer las ondas de ejecución secuencial de tiempo de ejecución // ----------------------------------------------------------------------------------------------------------------------------------------------------------- setanimation ("wave_end"); }, Animation_time * 1000); setTimeOut (() => {setAnimation ("predeterminado");}, animation_time * 2000); }; devolver (<div style={{ width: "100vw", height: "100vh" }}> {/* -------------------------------------------------------------------------------------------<div className="absolute top-4 left-4 bg-white shadow-lg p-4 rounded-lg z-10 w-64 space-y-4"> ... {/ * Botón de control de animación */}<div className="flex flex-wrap gap-2"> <button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" onClick={controlWaveAnimation} >Onda</button> <button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" onClick={controlExplosionAnimation} >de explosión</button></div></div></div> ); }; Exportar aplicación predeterminada;
💡 La fuente general actual está aquí
// Importar reaccionar y tres.js bibliotecas relacionadas con importar {useState} de "react"; // Configuración constante: const default_pixel_size = 64; // - default_pixel_size: tamaño predeterminado (en píxeles) al reducir la imagen const animation_time = 3; // - Animation_time: Time (en segundos) a Animation // ==== // Componentes de la aplicación: // - Gestión central de la interfaz de usuario (Carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organizar 2 lienzo (escena de píxeles y escena de fondo) // ===== Const App = () => {// Managemento estatal: Const [TEMPIXELSIZE, SETMPEXELEDIEDIEDEDEDEDEDED]]<number> (Default_pixel_size); // - TEMPPIXELSIZE: Tamaño de píxel temporal en la entrada de entrada const [pixelsize, setPixelsize] = useState<number> (TEMPPIXELSIZE); // - TEMPIXELSIZE: tamaño de píxel temporal en el formulario de entrada const [ImagesRC, setImagesRC] = useState<string | null> (nulo); // - ImagesRC: URL de datos de la imagen importada const [animación, setanimation] = useState ("predeterminado"); // - Animación: modo de animación actual // ---- // Qué hacer cuando se selecciona un archivo de imagen: // - Cargue el archivo, conviértalo en una URL de datos y configúrelo en ImagesRC // -----------------------------------------------------------------------------------------------------------------------------------------------------------<HTMLInputElement> ) => {const archivo = e.target.files?. [0]; if (! archivo) return; const lector = new FileReader (); Reader.onload = (ev) => {const url = ev.target? if (typeOf url! == "cadena") return; // Actualizar el tamaño del píxel una vez y luego configure la fuente de imagen SetPixelsize (TEMPPIXELSIZE); setImagesrc (URL); }; lector.readasdataurl (archivo); }; // ----- // Acción al presionar el botón "Revisión": //-Vuelva a dibujar la imagen reflejando el valor de TEMPIXELSIZE en PixelSize // --------------------------------------------------------------------------- --------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- ---- // Control de animación de onda: //-Inicie, finalice y deshace las ondas secuencialmente con Tiempo de espera // ----------------------------------------------------------------------- ----------------------------------------------------------------------- => {setanimation ("predeterminado"); }, Animation_time * 2000); }; devolver (<div style={{ width: "100vw", height: "100vh" }}> {/* -------------------------------------------------------------------------------------------<div className="absolute top-4 left-4 bg-white shadow-lg p-4 rounded-lg z-10 w-64 space-y-4"> {/ * Seleccionar archivo de imagen */} <input type="file" accept="image/*" onChange={handleFileChange} className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" /> {/ * Píxel Tamaño de tamaño Campo */}<div className="flex items-center space-x-2"> <label className="text-sm text-gray-700">Tamaño de píxel:</label><input type="number" value={tempPixelSize} onChange={(e) => setMppiXelSize (número (e.target.value))} min = "1" max = "128" classname = "W-16 borde-border-gris-300 redondeado md px-2 py-1 enfoque de texto-sm: anillo-2 enfoque: anillo-blue-500 enfoque: nonal" />>>>></div> {/ * Botón Redisplay */} <button className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 transition" onClick={reloadImage} >Redisplay</button> {/ * Botón de control de animación */}<div className="flex flex-wrap gap-2"> <button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" onClick={controlWaveAnimation} >Onda</button> <button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" onClick={controlExplosionAnimation} >de explosión</button></div></div></div> ); }; Exportar aplicación predeterminada;
💡 ¡Crear datos de píxeles a partir de imágenes!
Esta vez, cargaremos la imagen, generaremos los datos de píxeles y mostraremos la pantalla.
Entonces, creemos un proceso que genera datos de píxeles de una imagen.
*A los efectos de la explicación, los archivos, etc. no están separados. Creo que sería mejor separar los archivos a nivel de componente para que sea más legible.
Creación de una función CreatePixelData
// Importar reaccionar y tres.js bibliotecas relacionadas con importar {useState} de "react"; importar * como tres de "tres"; // ----- // Tipo de píxelinfo: Administrar posición (x, y, z) y color (tres.color) de cada píxel // --------------------------------------------------------------------------------------------------------------------------------------------------------------- x: número; Y: número; z: número; Color: tres.color; }; // Configuración constante: const default_pixel_size = 64; // - default_pixel_size: tamaño predeterminado (en píxeles) al reducir la imagen const animation_time = 3; // - Animation_time: Time Take to Animation (segundos) // ===== // Componentes de la aplicación: // - Gestión central de la interfaz de usuario (carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ==== EST APT App = () => {...}; Exportar aplicación predeterminada; // ===== // / CreatePixelData Función: // - Obtenga datos de píxel de la imagen especificada usando Canvas y conviértelo en una matriz de Tipo PixelInfo // ===== CONSTEPIXELDATA = (IMG: HTMLIMAGEELEMENT, TargetWidth: Number, TargethEight: Number): PIXELINFO [] => {////// / / Keatement It tt it it tt tt tt tt tt tt tt tt tt tt tt tt tt tt tt tt tt tt tt tt tt tt it al tamaño especificado const cienvas = document.createElement ("Canvas"); Canvas.Width = TargetWidth; Canvas.Height = Targetheight; const ctx = canvas.getContext ("2d"); if (! ctx) tire un nuevo error ("no contexto 2D disponible"); // dibujar una imagen en el lienzo y obtener información de píxeles ctx.drawimage (img, 0, 0, TargetWidth, Targetheight); const imgdata = ctx.getImagegeTata (0, 0, TargetWidth, Targetheight); const data = imgdata.data; Resultado constante: pixelinfo [] = []; Sea idx = 0; // Para cada píxel en la imagen, obtenga el valor RGBA y conviértalo en pixelInfo para (let y = 0; y <targetheight; y ++) {for (let x = 0; x <targetWidth; x ++) {const r = data [idx], g = data [idx+1], b = data [idx+2], a = data [data+3]; IDX += 4; // ignorar píxeles de baja transparencia (si a <30) if (a <30) continúa; Result.push ({// ajustar las coordenadas x, y para que el centro de la imagen sea el origen x: x - targetwidth / 2, y: -y + targetheight / 2, z: 0, // crea tres.color al convertir el valor RGB en el rango de 0 a 1: nuevo tres.color (r / 255, g / 255, b / 255),}); }} Resultado de retorno; };
El proceso es el siguiente: el proceso es un poco complicado, pero creo que lo que estoy haciendo es simple.
- Obtenga la imagen y el tamaño del píxel
- Dibuja la imagen en lienzo para obtener información de píxeles
- Obtiene información de RGBA para píxeles y la almacena en PixelInfo
Crea una parte de llamada (use efectos)
Dado que los datos de píxeles se actualizan cuando cambia la imagen o el tamaño de píxeles, useeFeFect () para monitorear [PixelSize, ImagesRC].
// Importar reaccionar y tres.js bibliotecas relacionadas con importar {useState, useEffect} de "reaccionar"; importar * como tres de "tres"; // ----- // Tipo de píxelinfo: Administre la posición (x, y, z) y el color (tres.color) de cada píxel // ------------------------------------------------------------------------------------------------------------------------------------------------------- x: número; Y: número; z: número; Color: tres.color; }; // Configuración constante: const default_pixel_size = 64; // - default_pixel_size: tamaño predeterminado (en píxeles) al reducir la imagen const animation_time = 3; // - Animation_time: Time Take to Animation (segundos) // ===== // Componentes de la aplicación: // - Gestión central de la interfaz de usuario (Carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Coloque dos lienzos (escena de píxeles y escena de fondo) // ===== Const App = () => {// Managemento estatal: Const [píxeles, setpixels] = UsEdate<PixelInfo[] | null> (nulo); // - píxeles: matriz de información de píxeles generada a partir de la imagen const [filechangecount, setFileChangeCount] = useState<number> (0); // - FileChangecount: número de cambios en la imagen (utilizada para activar una recarga) const [TEMPPIXELSIZE, SECTEMPPIXELSIZE] = useState<number> (Default_pixel_size); // - TEMPPIXELSIZE: Tamaño de píxel temporal en la entrada de entrada const [pixelsize, setPixelsize] = useState<number> (TEMPPIXELSIZE); // - TEMPIXELSIZE: tamaño de píxel temporal en el formulario de entrada const [ImagesRC, setImagesRC] = useState<string | null> (nulo); // - ImagesRC: URL de datos de la imagen importada const [animación, setanimation] = useState ("predeterminado"); //-Animación: modo de animación actual ... // ---- // Generar datos de píxeles a partir de la imagen cuando cambia la imagen o píxelsize // --------------------------------------------------------------------------------------------------------------------------------------------------------------- activar cuando la imagen cambia setFileChangeCount ((previo) => previo + 1); // Cargar una nueva imagen y generar datos de píxeles con Event Onload const img = new Image (); img.onload = () => {const pixel = createPixelData (img, pixelsize, pixelsize); setPixels (pix); }; img.src = ImageRC; }, [PixelSize, ImageRC]); devolver (<div style={{ width: "100vw", height: "100vh" }}> {/* -------------------------------------------------------------------------------------------</div> ); }; Exportar aplicación predeterminada; // ==== // Función CreatePixelData: // - Obtener datos de píxeles de la imagen especificada usando Canvas y convertirlo a una matriz de Tipo PixelInfo // ===== const createPixelData = (img: htmlimageElement, TargetWidth: Number, TargethEight: Number) Tamaño especificado const Canvas = document.createElement ("Canvas"); Canvas.Width = TargetWidth; Canvas.Height = Targetheight; const ctx = canvas.getContext ("2d"); if (! ctx) tire un nuevo error ("no contexto 2D disponible"); // Dibuje la imagen en el lienzo y obtenga información de píxeles ctx.drawimage (img, 0, 0, TargetWidth, Targetheight); const imgdata = ctx.getImagegeTata (0, 0, TargetWidth, Targetheight); const data = imgdata.data; Resultado constante: pixelinfo [] = []; Sea idx = 0; // Para cada píxel en la imagen, obtenga el valor RGBA y conviértalo en pixelInfo para (let y = 0; y <targetheight; y ++) {for (let x = 0; x <targetWidth; x ++) {const r = data [idx], g = data [idx+1], b = data [idx+2], a = data [data+3]; IDX += 4; // Ignorar los píxeles con baja transparencia (A <30) if (A <30) continúa; result.push ({// ajustar las coordenadas x, y, por lo que el centro de la imagen es el origen x: x - targetwidth / 2, y: -y + targetheight / 2, z: 0, // crea tres.color al convertir el valor RGB en el rango de 0 a 1: nuevo tres.color (r / 255, g / 255, b / 255),}); }} Resultado de retorno; };
💡A ¡El lienzo está disponible para la pantalla de píxeles!
Primero coloque el lienzo y prepare su cámara, iluminación, etc.
// Importar React y tres bibliotecas relacionadas con tres.js Importar {OrbitControls} de "@react-three/drei"; import {Canvas} de "@react-three/fiber"; import {useSestate, useEffect, useref} de "React"; importar * como tres de "tres"; // ----- // Tipo de píxelinfo: Administre la posición (x, y, z) y el color (tres.color) de cada píxel // --------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- El tiempo tardado en segundos en la animación // ==== // Componentes de la aplicación: // - Gestión central de la interfaz de usuario (carga de imágenes, tamaño de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ==== CONST App = () => {... return (return (Devuelve (return (return (<div style={{ width: "100vw", height: "100vh" }}> {/* ------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[1]"><Canvas camera={{ far: 5000, position: [0, 0, 100] }}> {/ * Configuración de iluminación básica */}<ambientLight intensity={1} /><directionalLight position={[100, 200, 100]} intensity={1} /> {/ * OrbitControls que le permiten rotar la escena con la operación del mouse */}<OrbitControls /> {/ * Render PixelGrid solo si los datos de píxeles están presentes */})}</Canvas></div></div> ); }; Exportar aplicación predeterminada; // ==== // Función CreatePixelData: // - Obtenga datos de píxeles de la imagen especificada usando Canvas y conviértelo a una matriz de tipo PixelInfo // ===== const createepixelData = (img: htmlImageElement, TargetWidth: número, Targethight: Number): pixelInfo [] => {... };
💡 Mostrar píxeles !!
// Importar React y tres bibliotecas relacionadas con tres.js Importar {OrbitControls} de "@react-three/drei"; import {Canvas} de "@react-three/fiber"; import {useSestate, useEffect, useref} de "React"; importar * como tres de "tres"; // ----- // Tipo de píxelinfo: Administre la posición (x, y, z) y el color (tres.color) de cada píxel // --------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- El tiempo tardado en segundos en la animación // ==== // componente de píxelgrid: // - Rendera grupos de datos de píxeles, y // - controla la animación de visualización (ampliación gradual) y animación en movimiento (explosión/wave) // === Tipo pixelgridprops = {pixelSize: número; píxeles: pixelinfo []; animación: cadena; filechangecount: número; }; const pixelGrid = ({PixelSize, Pixels, Animation, FileChangeCount,}: PixelGridProps) => {// Una referencia al objeto de grupo en tres.js. Se utiliza para agrupar todos los píxeles const groupref = useref<THREE.Group> (nulo); // Coloque cada componente de PixelBox en un grupo administrado por GroupRef para representar el retorno (<group ref={groupRef}> {Pixels.map ((Pixel, I) => (<PixelBox key={i} pixel={pixel} scale={1} /> ))}</group> ); }; // ==== // componente pixelbox: // - un componente para dibujar un solo píxel (caja) // ==== Tipo PixelBoxProps = {Pixel: PixelInfo; Escala: número; }; const pixelbox = ({pixel, escala}: pixelboxprops) => {// malla: use geometría y material de caja con ubicación y retorno de escala ( <mesh position={[pixel.x, pixel.y, pixel.z]} scale={[scale, scale, scale]}><boxGeometry args={[1, 1, 1]} /><meshStandardMaterial color={pixel.color} /></mesh> ); }; // ==== // Componente de la aplicación: // - Administración central de interfaz de usuario (carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ==== EST APP = () => {... return (<div style={{ width: "100vw", height: "100vh" }}> {/* ------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[1]"><Canvas camera={{ far: 5000, position: [0, 0, 100] }}> {/ * Configuración de iluminación básica */}<ambientLight intensity={1} /><directionalLight position={[100, 200, 100]} intensity={1} /> {/ * OrbitControls que le permiten rotar la escena con la operación del mouse */}<OrbitControls /> {/ * Render PixelGrid solo si los datos de píxel están presentes */} {píxeles && ( <PixelGrid pixelSize={pixelSize} pixels={pixels} animation={animation} fileChangeCount={fileChangeCount} /> )}</Canvas></div></div> ); }; Exportar aplicación predeterminada; // ==== // Función CreatePixelData: // - Obtenga datos de píxeles de la imagen especificada usando Canvas y conviértelo a una matriz de tipo PixelInfo // ===== const createepixelData = (img: htmlImageElement, TargetWidth: Number, Targethight: Number): pixelInfo [] => {... {{{{{{{{{{{{};
Creo que el arte de Pixel se muestra de esta manera.
Esto está bien, pero como lo estoy haciendo con React Three Fiber, agregaré animaciones y más.
💡 Animación de pantalla agregada
Intentaré recrear la animación de la pantalla a medida que se dibuja gradualmente.
Es una imagen de aumentar gradualmente la escala de píxeles de 0 a 1.
// Importar React y tres bibliotecas relacionadas con tres.js Importar {OrbitControls} de "@react-three/drei"; import {Canvas, UseFrame} de "@react-three/fiber"; import {useSestate, useEffect, useref} de "React"; importar * como tres de "tres"; // ----- // Tipo de píxelinfo: posición de cada píxel (x, y, z) y color (tres.color) // ----------------------------------------------------------------------------------------------- --------------------------------------------------------------------------- Animation_time: Time (en segundos) a Animation // ===== // componente pixelgrid: // - Rendera grupos de datos de píxeles, y // - controla la animación de visualización (ampliación gradual) y la animación en movimiento (explosion/wave) // === Tipo pixelgridprops = {mixelsize: número; píxeles: pixelinfo []; animación: cadena; filechangecount: número; }; const pixelGrid = ({PixelSize, Pixels, Animation, FileChangeCount,}: PixelGridProps) => {// Una referencia al objeto de grupo en tres.js. Se utiliza para agrupar todos los píxeles const groupref = useref<THREE.Group> (nulo); // ---- // Mostrar animación: procesamiento de píxeles que se muestran en etapas // ---- // gestionar el estado de escala para cada píxel (el estado inicial es todo 0 = oculto) const [scales, setscales] = useState<number[]> (Array (pixels.length) .fill (0)); // ScaleProgressRef: Mantiene el progreso de la pantalla (tiempo acumulado) para cada lote const escalaprogressRef = useref (0); // BatchIndexRef: gestiona el número de lotes constante constante const BatchIndexref = useref (0); // useFrame para actualizar cada cuadro y visualizar píxeles Paso a paso Useframe ((_, delta) => {// Si se muestran todos los lotes, no se realiza un procesamiento adicional si (batchindexref.current> píxels.length / pixelsize) return; // agrega el tiempo desde que se muestra el tiempo anterior el tiempo de marco anterior. conste de constante = batchIndexref.Current * (animation_time / (pixels.length / pixelsize)); al lote constante de lotes de lotes de lotes = Math.min (startindex+pixelsize, pixels.length); // proceder al siguiente lote lote de lote. Current += 1; // Coloque cada componente de Pixelbox en un grupo administrado por GroupRef y renderizarlo (<group ref={groupRef}> {Pixels.map ((Pixel, I) => (<PixelBox key={i} pixel={pixel} scale={scales[i]} /> ))}</group> ); }; // ==== // componente pixelbox: // - un componente para dibujar un solo píxel (caja) // ==== Tipo PixelBoxProps = {Pixel: PixelInfo; Escala: número; }; const pixelbox = ({pixel, escala}: pixelboxprops) => {// malla: use geometría y material de caja con ubicación y retorno de escala ( <mesh position={[pixel.x, pixel.y, pixel.z]} scale={[scale, scale, scale]}><boxGeometry args={[1, 1, 1]} /><meshStandardMaterial color={pixel.color} /></mesh> ); }; // ==== // Componente de la aplicación: // - Administración central de interfaz de usuario (carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ==== EST APP = () => {... return (<div style={{ width: "100vw", height: "100vh" }}> {/* ------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[1]"><Canvas camera={{ far: 5000, position: [0, 0, 100] }}> {/ * Configuración de iluminación básica */}<ambientLight intensity={1} /><directionalLight position={[100, 200, 100]} intensity={1} /> {/ * OrbitControls que le permiten rotar la escena con la operación del mouse */}<OrbitControls /> {/ * Render PixelGrid solo si los datos de píxel están presentes */} {píxeles && ( <PixelGrid pixelSize={pixelSize} pixels={pixels} animation={animation} fileChangeCount={fileChangeCount} /> )}</Canvas></div></div> ); }; Exportar aplicación predeterminada; // ==== // Función CreatePixelData: // - Obtenga datos de píxeles de la imagen especificada usando Canvas y conviértelo a una matriz de tipo PixelInfo // ===== const createepixelData = (img: htmlImageElement, TargetWidth: Number, Targethight: Number): pixelInfo [] => {... {{{{{{{{{{{{};
💡 Admite la recarga de imágenes !!
Si esto continúa, la animación de la pantalla no funcionará cuando la imagen se vuelva a cargar y se mostrará de inmediato.
Agregue UseeFectect para restablecer el estado al recargar una imagen o cambiar el tamaño del píxel.
// Importar React y tres bibliotecas relacionadas con tres.js Importar {OrbitControls} de "@react-three/drei"; import {Canvas, UseFrame} de "@react-three/fiber"; import {useSestate, useEffect, useref} de "React"; importar * como tres de "tres"; // ----- // Tipo de píxelinfo: posición de cada píxel (x, y, z) y color (tres.color) // ----------------------------------------------------------------------------------------------- --------------------------------------------------------------------------- Animation_time: Time (en segundos) a Animation // ===== // componente pixelgrid: // - Rendera grupos de datos de píxeles, y // - controla la animación de visualización (ampliación gradual) y la animación en movimiento (explosion/wave) // === Tipo pixelgridprops = {mixelsize: número; píxeles: pixelinfo []; animación: cadena; filechangecount: número; }; const pixelGrid = ({PixelSize, Pixels, Animation, FileChangeCount,}: PixelGridProps) => {// Una referencia al objeto de grupo en tres.js. Se utiliza para agrupar todos los píxeles const groupref = useref<THREE.Group> (nulo); // ---- // Mostrar animación: procesamiento de píxeles que se muestran en etapas // ---- // gestionar el estado de escala para cada píxel (el estado inicial es todo 0 = oculto) const [scales, setscales] = useState<number[]> (Array (pixels.length) .fill (0)); // ScaleProgressRef: Mantiene el progreso de la pantalla (tiempo acumulado) para cada lote const escalaprogressRef = useref (0); // BatchIndexRef: gestiona el número de lotes constante constante const BatchIndexref = useref (0); // Una referencia para determinar si un archivo de imagen ha sido cambiado const prevfiLechangeCountRef = useref (fileChangeCount); // restablece el estado de visualización (escala) de todos los píxeles al volver a cargar una imagen useEffect (() => {if (fileChangeCount! == PrevFileChiLechangeCountref.Current) {// restablecer la escala a 0 para ocultar todos los píxeles setscales (array (píxeles píxeles) .long). (0)); // scaleProgressRef.Current = 0; // useFrame para actualizar cada cuadro, y visualice píxeles paso a paso useframe delta) => {// No se realiza un procesamiento adicional si todos los lotes son visibles if (batchindexref.current> pixels.length / pixelsize) return; // Agregar tiempo ya que el marco anterior escalaregurof.current += delta; // Calcule el umbral de tiempo para mostrar el umbral constante de lotes actual = BatchIndexref.Current * (animation_time / (pixels.length / pixelsize)); // Cuando el tiempo transcurrido excede el umbral, comience a mostrar píxeles para el siguiente lote if (scaleProgressRef.Current> umbral) {// Cree una copia de la escala actual estatal const newscales = [... scales]; // Calcule el rango de índice de píxeles pertenecientes al lote actual const startIndex = batchIndexref.Current * pixelsize; const endindex = math.min (startIndex + pixelsize, pixels.length); // Muestra la escala de píxel correspondiente a 1 para (Sea i = startIndex; i <endindex; i ++) {Newscales [i] = 1; } // Establecer el estado de escala actualizado setscales (NEWScales); // proceder al siguiente lote BatchIndexref.Current += 1; }}); // Cada componente de Pixelbox se coloca en un grupo administrado por GroupRef para representar el retorno (<group ref={groupRef}> {Pixels.map ((Pixel, I) => (<PixelBox key={i} pixel={pixel} scale={scales[i]} /> ))}</group> ); }; // ==== // componente pixelbox: // - un componente para dibujar un solo píxel (caja) // ==== Tipo PixelBoxProps = {Pixel: PixelInfo; Escala: número; }; const pixelbox = ({pixel, escala}: pixelboxprops) => {// malla: use geometría y material de caja con ubicación y retorno de escala ( <mesh position={[pixel.x, pixel.y, pixel.z]} scale={[scale, scale, scale]}><boxGeometry args={[1, 1, 1]} /><meshStandardMaterial color={pixel.color} /></mesh> ); }; // ==== // Componente de la aplicación: // - Administración central de interfaz de usuario (carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ==== EST APP = () => {... return (<div style={{ width: "100vw", height: "100vh" }}> {/* ------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[1]"><Canvas camera={{ far: 5000, position: [0, 0, 100] }}> {/ * Configuración de iluminación básica */}<ambientLight intensity={1} /><directionalLight position={[100, 200, 100]} intensity={1} /> {/ * OrbitControls que le permiten rotar la escena con la operación del mouse */}<OrbitControls /> {/ * Render PixelGrid solo si los datos de píxel están presentes */} {píxeles && ( <PixelGrid pixelSize={pixelSize} pixels={pixels} animation={animation} fileChangeCount={fileChangeCount} /> )}</Canvas></div></div> ); }; Exportar aplicación predeterminada; // ==== // Función CreatePixelData: // - Obtenga datos de píxeles de la imagen especificada usando Canvas y conviértelo a una matriz de tipo PixelInfo // ===== const createepixelData = (img: htmlImageElement, TargetWidth: Number, Targethight: Number): pixelInfo [] => {... {{{{{{{{{{{{};
Ahora puede ver que funciona correctamente incluso si recarga la imagen.
PixelSize también se monitorea, por lo que incluso si actualiza el tamaño de píxel, se mostrará nuevamente.
💡 Animación de explosión agregada !!
A continuación, agregue una animación de explosión.
Lo que estamos haciendo es bastante simple: primero calcule las coordenadas aleatorias y luego use UseFrame para moverlas a esa posición. Cuando vuelves a la normalidad, puedes hacer lo contrario.
// Importar React y tres bibliotecas relacionadas con tres.js Importar {OrbitControls} de "@react-three/drei"; import {Canvas, UseFrame} de "@react-three/fiber"; import {useSestate, useEffect, useref} de "React"; importar * como tres de "tres"; // ----- // Tipo de píxelinfo: posición de cada píxel (x, y, z) y color (tres.color) // ----------------------------------------------------------------------------------------------- --------------------------------------------------------------------------- Animation_time: Time (en segundos) a Animation // ===== // componente pixelgrid: // - Rendera grupos de datos de píxeles, y // - controla la animación de visualización (ampliación gradual) y la animación en movimiento (explosion/wave) // === Tipo pixelgridprops = {mixelsize: número; píxeles: pixelinfo []; animación: cadena; filechangecount: número; }; const pixelGrid = ({PixelSize, Pixels, Animation, FileChangeCount,}: PixelGridProps) => {// Una referencia al objeto de grupo en tres.js. Se utiliza para agrupar todos los píxeles const groupref = useref<THREE.Group> (nulo); // ---- // Mostrar animación: Procesamiento de píxeles que se muestran en etapas // ----- ... // ----- // Animación en movimiento: Cambiar la posición de píxeles con explosión o efectos de onda // --- // una referencia para comparar con el estado de animación anterior y reiniciar el progreso si hay un cambio constitanimationRef = useref (animación); // gestionar el progreso de la animación (0 a 1) const animationProgressRef = useref (0); // Generar coordenadas de destino de dispersión aleatorias para cada píxel (para efectos de explosión) const scatterPositionSref = useref (pixels.map (() => ({x: (math.random () - 0.5) * 100, y: (math.random () - 0.5) * 100, z: (math.random () - 0.5) * 100,}))))))); // regenere las coordenadas de dispersión aleatoria cuando los datos de imagen o píxel se actualizan useEffect (() => {scatterPositionSref.Current = Pixels.map (() => ({x: (math.random () - 0.5) * 100, y: (Math.Random () - 0.5) * 100, Z: (Math.random () - 0.5) * 100,}); [píxeles]); // Actualizar la posición de cada píxel Cada cuadro con UseFrame para que coincida con el progreso de la animación Useframe ((_, delta) => {// restablecer el progreso si el estado de la animación cambia if (animación! == dIlaTanImationRef.Current) {LastanimationRf.Current = Animation; AnimationProgressf.Current = 0;}/}// No hay actualizaciones de la posición ocurren en "Estado de la Palación". "Devalado") return; / actualizar el progreso de la animación (normalizado con animation_time) if (animationProgressRef.Current <1) {AnimationProgressRef.Current += Delta / Animation_Time (AnimationProgressRef.Current * Math.pi) / 2); / Calcule y actualice la nueva posición de cada píxel pixels.forEach ((pixel, i) => {// Las coordenadas de destino base son las coordenadas originales de la posición de la posición original de la posición de la posición original. (animación === "explosion_start") {targetX = scatterPositionSref.Current [i] .x * SmoothProgress + Pixel.x * (1 - SmoothProgress); Pixel.z * (1 - SmoothProgress); Pixel.y * SmoothProgress; // Coloque cada componente de PixelBox en un grupo administrado por GroupRef para representar el retorno (<group ref={groupRef}> {Pixels.map ((Pixel, I) => (<PixelBox key={i} pixel={pixel} scale={scales[i]} /> ))}</group> ); }; // ==== // componente pixelbox: // - un componente para dibujar un solo píxel (caja) // ==== Tipo PixelBoxProps = {Pixel: PixelInfo; Escala: número; }; const pixelbox = ({pixel, escala}: pixelboxprops) => {// malla: use geometría y material de caja con ubicación y retorno de escala ( <mesh position={[pixel.x, pixel.y, pixel.z]} scale={[scale, scale, scale]}><boxGeometry args={[1, 1, 1]} /><meshStandardMaterial color={pixel.color} /></mesh> ); }; // ==== // Componente de la aplicación: // - Administración central de interfaz de usuario (carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ==== EST APP = () => {... return (<div style={{ width: "100vw", height: "100vh" }}> {/* ------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[1]"><Canvas camera={{ far: 5000, position: [0, 0, 100] }}> {/ * Configuración de iluminación básica */}<ambientLight intensity={1} /><directionalLight position={[100, 200, 100]} intensity={1} /> {/ * OrbitControls que le permiten rotar la escena con la operación del mouse */}<OrbitControls /> {/ * Render PixelGrid solo si los datos de píxel están presentes */} {píxeles && ( <PixelGrid pixelSize={pixelSize} pixels={pixels} animation={animation} fileChangeCount={fileChangeCount} /> )}</Canvas></div></div> ); }; Exportar aplicación predeterminada; // ==== // Función CreatePixelData: // - Obtenga datos de píxeles de la imagen especificada usando Canvas y conviértelo a una matriz de tipo PixelInfo // ===== const createepixelData = (img: htmlImageElement, TargetWidth: Number, Targethight: Number): pixelInfo [] => {... {{{{{{{{{{{{};
¡Aunque es simple, creo que parece tener una sensación de división por Pixel!
💡 ¡Animación de onda agregada!
Agreguemos animaciones de olas también.
Creo que básicamente puedo agregar esto de la misma manera que una animación de explosión.
// Importar React y tres bibliotecas relacionadas con tres.js Importar {OrbitControls} de "@react-three/drei"; import {Canvas, UseFrame} de "@react-three/fiber"; import {useSestate, useEffect, useref} de "React"; importar * como tres de "tres"; // ----- // Tipo de píxelinfo: posición de cada píxel (x, y, z) y color (tres.color) // ----------------------------------------------------------------------------------------------- --------------------------------------------------------------------------- Animation_time: Time (en segundos) a Animation // ===== // componente pixelgrid: // - Rendera grupos de datos de píxeles, y // - controla la animación de visualización (ampliación gradual) y la animación en movimiento (explosion/wave) // === Tipo pixelgridprops = {mixelsize: número; píxeles: pixelinfo []; animación: cadena; filechangecount: número; }; const pixelGrid = ({PixelSize, Pixels, Animation, FileChangeCount,}: PixelGridProps) => {// Una referencia al objeto de grupo en tres.js. Se utiliza para agrupar todos los píxeles const groupref = useref<THREE.Group> (nulo); // ---- // Mostrar animación: Procesamiento de píxeles que se muestran en etapas // ----- ... // ----- // Animación en movimiento: Cambiar la posición de píxeles con explosión o efectos de onda // --- // una referencia para comparar con el estado de animación anterior y reiniciar el progreso si hay un cambio constitanimationRef = useref (animación); // gestionar el progreso de la animación (0 a 1) const animationProgressRef = useref (0); // Generar coordenadas de destino de dispersión aleatorias para cada píxel (para efectos de explosión) const scatterPositionSref = useref (pixels.map (() => ({x: (math.random () - 0.5) * 100, y: (math.random () - 0.5) * 100, z: (math.random () - 0.5) * 100,}))))))); // regenere las coordenadas de dispersión aleatoria cuando los datos de imagen o píxel se actualizan useEffect (() => {scatterPositionSref.Current = Pixels.map (() => ({x: (math.random () - 0.5) * 100, y: (Math.Random () - 0.5) * 100, Z: (Math.random () - 0.5) * 100,}); [píxeles]); // Actualizar la posición de cada píxel Cada cuadro con UseFrame para que coincida con el progreso de la animación Useframe ((_, delta) => {// restablecer el progreso si el estado de la animación cambia if (animación! == dIlaTanImationRef.Current) {LastanimationRf.Current = Animation; AnimationProgressf.Current = 0;}/}// No hay actualizaciones de la posición ocurren en "Estado de la Palación". "Devalado") return; / actualizar el progreso de la animación (normalizado con animation_time) if (animationProgressRef.Current <1) {AnimationProgressRef.Current += Delta / Animation_Time (AnimationProgressRef.Current * Math.pi) / 2); / Calcule y actualice la nueva posición de cada píxel pixels.forEach ((pixel, i) => {// Las coordenadas de destino base son las coordenadas originales de la posición de la posición original de la posición de la posición original. (animación === "explosion_start") {targetX = scatterPositionSref.Current [i] .x * SmoothProgress + Pixel.x * (1 - SmoothProgress); Pixel.z * (1 - SmoothProgress); Pixel.y * SmoothProgress; AnimationProgressRef.Current * 10) * 0.3) * 5 * SmoothProgress; Actualizar la posición de malla de los píxeles correspondientes. // Coloque cada componente de PixelBox en un grupo administrado por GroupRef para representar el retorno (<group ref={groupRef}> {Pixels.map ((Pixel, I) => (<PixelBox key={i} pixel={pixel} scale={scales[i]} /> ))}</group> ); }; // ==== // componente pixelbox: // - un componente para dibujar un solo píxel (caja) // ==== Tipo PixelBoxProps = {Pixel: PixelInfo; Escala: número; }; const pixelbox = ({pixel, escala}: pixelboxprops) => {// malla: use geometría y material de caja con ubicación y retorno de escala ( <mesh position={[pixel.x, pixel.y, pixel.z]} scale={[scale, scale, scale]}><boxGeometry args={[1, 1, 1]} /><meshStandardMaterial color={pixel.color} /></mesh> ); }; // ==== // Componente de la aplicación: // - Administración central de interfaz de usuario (carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ==== EST APP = () => {... return (<div style={{ width: "100vw", height: "100vh" }}> {/* ------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[1]"><Canvas camera={{ far: 5000, position: [0, 0, 100] }}> {/ * Configuración de iluminación básica */}<ambientLight intensity={1} /><directionalLight position={[100, 200, 100]} intensity={1} /> {/ * OrbitControls que le permiten rotar la escena con la operación del mouse */}<OrbitControls /> {/ * Render PixelGrid solo si los datos de píxel están presentes */} {píxeles && ( <PixelGrid pixelSize={pixelSize} pixels={pixels} animation={animation} fileChangeCount={fileChangeCount} /> )}</Canvas></div></div> ); }; Exportar aplicación predeterminada; // ==== // Función CreatePixelData: // - Obtenga datos de píxeles de la imagen especificada usando Canvas y conviértelo a una matriz de tipo PixelInfo // ===== const createepixelData = (img: htmlImageElement, TargetWidth: Number, Targethight: Number): pixelInfo [] => {... {{{{{{{{{{{{};
Creo que esto puede expresar el sentimiento ondulante.
💡 💡 💡 💡 💡 Antecedentes!
Esto no lo necesita, pero estaba un poco solo, así que también agregaré un fondo.
React Three Drei tiene estrella y nubes, así que los combinaré de una manera agradable.
// Importar reaccionar y tres.js bibliotecas relacionadas con la importación react, {useState, useref, memo, useEffect} de "React"; import {Canvas, UseFrame} de "@react-three/fiber"; import {Cloud, OrbitControls, Stars} de "@react-three/drei"; importar * como tres de "tres"; // ----- // PixelInfo Tipo: Administre la posición de cada píxel (X, Y, Z) y Color (tres.color) // --------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- Default_pixel_size = 64; const animation_time = 3; // ==== // componente píxelgrid: // - Rendera grupos de datos de píxeles, y // - controla la animación de visualización (ampliación gradual) y animación en movimiento (explosión/wave) // === Tipo pixelgridprops = {pixelsize: número; píxeles: pixelinfo []; animación: cadena; filechangecount: número; }; const pixelGrid = ({PixelSize, Pixels, Animation, FileChangeCount,}: PixelGridProps) => {... // / Coloque cada componente Pixelbox en un grupo administrado por GroupRef para representar la retorno (<group ref={groupRef}> {Pixels.map ((Pixel, I) => (<PixelBox key={i} pixel={pixel} scale={scales[i]} /> ))}</group> ); }; // ==== // componente pixelbox: // - un componente para dibujar un solo píxel (caja) // ==== Tipo PixelBoxProps = {Pixel: PixelInfo; Escala: número; }; const pixelbox = ({pixel, escala}: pixelboxprops) => {// malla: use geometría y material de caja con ubicación y retorno de escala ( <mesh position={[pixel.x, pixel.y, pixel.z]} scale={[scale, scale, scale]}><boxGeometry args={[1, 1, 1]} /><meshStandardMaterial color={pixel.color} /></mesh> ); }; // ==== // Componente de la aplicación: // - Administración central de interfaz de usuario (carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ==== EST APP = () => {... return (<div style={{ width: "100vw", height: "100vh" }}> {/* ----------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[-1]"><Canvas camera={{ position: [0, 5, 15], fov: 50 }} style={{ background: "black" }} ><ambientLight intensity={0.5} /><pointLight position={[10, 10, 10]} intensity={0.5} color="white" /><BackgroundScene /></Canvas></div></div> ); }; // ==== // Función CreatePixelData: // - Obtenga datos de píxeles de la imagen especificada usando Canvas y conviértelo a una matriz de tipo PixelInfo // ===== const createepixelData = (img: htmlImageElement, TargetWidth: número, Targethight: Number): pixelInfo [] => {... }; // ==== // Componente de backgroundscene: // - un componente para mostrar un cielo estrellado y nubes en el fondo // - Envolviendo con memo para evitar el redibujado innecesario // ===== const backgroundscene = memo () => {return (<> {/ * representa el cielo estrellado con componentes de estrellas */} <Stars radius={100} // 星が存在する空間の半径 depth={50} // 星の配置される深さの範囲 count={5000} // 表示する星の総数 factor={6} // 星の大きさ調整用の係数 saturation={1} // 色の鮮やかさ fade // 遠くの星がフェードアウトする効果 /> {/ * Expresar nubes con componentes de la nube */} <Cloud position={[0, 0, 0]} // 雲の中心位置 opacity={0.1} // 雲の不透明度(低いほど透明) speed={0.2} // 雲の動く速度 scale={[10, 10, 10]} // 雲全体のサイズ segments={20} // 雲を構成するパーティクルの数 /></> ); }); Exportar aplicación predeterminada;
Puede ser difícil saber en el video, pero parece que el humo está aumentando y se siente bien.
💡 Código fuente final !!
💾 Repositorio de GitHub : ¡verifique el código fuente en este enlace
// Importar reaccionar y tres.js bibliotecas relacionadas con la importación react, {useState, useref, memo, useEffect} de "React"; import {Canvas, UseFrame} de "@react-three/fiber"; import {Cloud, OrbitControls, Stars} de "@react-three/drei"; importar * como tres de "tres"; // ----- // PixelInfo Tipo: Administre la posición de cada píxel (X, Y, Z) y Color (tres.color) // --------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- Default_pixel_size = 64; const animation_time = 3; // ==== // componente píxelgrid: // - Rendera grupos de datos de píxeles, y // - controla la animación de visualización (ampliación gradual) y animación en movimiento (explosión/wave) // === Tipo pixelgridprops = {pixelsize: número; píxeles: pixelinfo []; animación: cadena; filechangecount: número; }; const pixelGrid = ({PixelSize, Pixels, Animation, FileChangeCount,}: PixelGridProps) => {// Una referencia al objeto de grupo en tres.js. Se utiliza para agrupar todos los píxeles const groupref = useref<THREE.Group> (nulo); // ---- // Mostrar animación: procesamiento de píxeles que se muestran en etapas // ---- // gestionar el estado de escala para cada píxel (el estado inicial es todo 0 = oculto) const [scales, setscales] = useState<number[]> (Array (pixels.length) .fill (0)); // ScaleProgressRef: Mantiene el progreso de la pantalla (tiempo acumulado) para cada lote const escalaprogressRef = useref (0); // BatchIndexRef: gestiona el número de lotes constante constante const BatchIndexref = useref (0); // Una referencia para determinar si un archivo de imagen ha sido cambiado const prevfiLechangeCountRef = useref (fileChangeCount); // restablece el estado de visualización (escala) de todos los píxeles al volver a cargar una imagen useEffect (() => {if (fileChangeCount! == PrevFileChiLechangeCountref.Current) {// restablecer la escala a 0 para ocultar todos los píxeles setscales (array (píxeles píxeles) .long). (0)); // scaleProgressRef.Current = 0; // useFrame para actualizar cada cuadro, y visualice píxeles paso a paso useframe delta) => {// No se realiza un procesamiento adicional si todos los lotes son visibles if (batchindexref.current> pixels.length / pixelsize) return; // Agregar tiempo ya que el marco anterior escalaregurof.current += delta; // Calcule el umbral de tiempo para mostrar el umbral constante de lotes actual = BatchIndexref.Current * (animation_time / (pixels.length / pixelsize)); // Cuando el tiempo transcurrido excede el umbral, comience a mostrar píxeles para el siguiente lote if (scaleProgressRef.Current> umbral) {// Cree una copia de la escala actual estatal const newscales = [... scales]; // Calcule el rango de índice de píxeles pertenecientes al lote actual const startIndex = batchIndexref.Current * pixelsize; const endindex = math.min (startIndex + pixelsize, pixels.length); // Muestra la escala de píxel correspondiente a 1 para (Sea i = startIndex; i <endindex; i ++) {Newscales [i] = 1; } // Establecer el estado de escala actualizado setscales (NEWScales); // proceder al siguiente lote BatchIndexref.Current += 1; }}); // ---- // Animación en movimiento: Cambie la posición de píxeles con explosión o efectos de onda // ---- // referencia para comparar con el estado de animación anterior y restablecer el progreso si hay un cambio consttanimationRef = useref (animación); // gestionar el progreso de la animación (0-1) const animationProgressRef = useref (0); // Generar coordenadas de dispersión aleatoria para cada píxel (para efectos de explosión) const scatterPositionSref = useref (pixels.map (() => ({x: (math.random () - 0.5) * 100, y: (math.random () - 0.5) * 100, z: (math.random () - 0.5) * 100,})))))); // regenere las coordenadas de dispersión aleatoria cuando los datos de imagen o píxel se actualizan useEffect (() => {scatterPositionSref.current = Pixels.map (() => ({x: (math.random () - 0.5) * 100, y: (th.random () - 0.5) * 100, Z: (math.random () - 0.5) * 100,}); [píxeles]); // Actualizar la posición de cada píxel Cada cuadro con UseFrame para que coincida con el progreso de la animación Useframe ((_, delta) => {// restablece el progreso si el estado de la animación cambia if (animación! == dastanimationRef.current) {dastanimationRef.current = animación; animación; === "predeterminado") return; // Actualizar el progreso de la animación (normalizado con animation_time) if (animationProgressRef.Current <1) {AnimationProgressRef.Current += Delta / Animation_Time Math.sin ((AnimationProgressRef.Current * Math.pi) / 2); Posición de dispersión if (animation === "Explosion_start") {TargetX = scatterPositionSref.Current [i] .x * SmoothProgress + Pixel.x * (1 - SmoothProgress) SmoothProgress + Pixel.z * (1 - SmoothProgress); SmoothProgress) + Pixel.y * SmoothProgress; Pixel.z + AnimationProgressRef.Current * 10) * 0.3) * 5 * SmoothProgress; SmoothProgress); // Coloque cada componente de PixelBox en un grupo administrado por GroupRef para representar el retorno (<group ref={groupRef}> {Pixels.map ((Pixel, I) => (<PixelBox key={i} pixel={pixel} scale={scales[i]} /> ))}</group> ); }; // ==== // componente pixelbox: // - un componente para dibujar un solo píxel (caja) // ==== Tipo PixelBoxProps = {Pixel: PixelInfo; Escala: número; }; const pixelbox = ({pixel, escala}: pixelboxprops) => {// malla: use geometría y material de caja con ubicación y retorno de escala ( <mesh position={[pixel.x, pixel.y, pixel.z]} scale={[scale, scale, scale]}><boxGeometry args={[1, 1, 1]} /><meshStandardMaterial color={pixel.color} /></mesh> ); }; // ==== // Componente de la aplicación: // - Gestión central de la interfaz de usuario (carga de imágenes, dimensionamiento de píxeles, operaciones de animación) // - Organice dos lienzos (escena de píxeles y escena de fondo) // ===== const app = () => {// state gestión: /// píxeles: matriz de información de píxeles generada a partir de imágenes // - temppixelsize: píxeles temporales de mezal PixelSize: tamaño de píxel real aplicado // - Filechangecount: Número de cambios en la imagen (utilizado para activar una recarga) // - ImagesRC: URL de datos de la imagen importada // - Animación: modo de animación actual const [píxeles, setPixels] = useState<PixelInfo[] | null> (nulo); const [TEMPPIXELSIZE, SECTEMPPIXELSIZE] = USESTATE<number> (Default_pixel_size); const [pixelsize, setPixelsize] = useState<number> (TEMPPIXELSIZE); const [filechangecount, setFileChangeCount] = useState<number> (0); const [ImagesRC, setImagesrc] = useState<string | null> (nulo); const [animación, setanimation] = useState ("predeterminado"); // ----- // Qué hacer cuando se selecciona un archivo de imagen: //-Cargue el archivo, conviértelo en una URL de datos y configúrelo en ImagesRC // -)<HTMLInputElement> ) => {const archivo = e.target.files?. [0]; if (! archivo) return; const lector = new FileReader (); Reader.onload = (ev) => {const url = ev.target? if (typeOf url! == "cadena") return; // Actualizar el tamaño del píxel una vez y luego configure la fuente de imagen SetPixelsize (TEMPPIXELSIZE); setImagesrc (URL); }; lector.readasdataurl (archivo); }; // ----- // Processing Al presionar el botón "Revisar": //-Vuelva a dibujar la imagen reflejando el valor de TEMPIXELSIZE en PixelSize // ------------------------------------------------------------------------------- --------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------- }, [PixelSize, ImageRC]); // ----- // Control Animación de explosión: //-Iniciar, finalizar y deshacer la explosión ejecuta secuencialmente el tiempo de espera // ----------------------------------------------------------------------------------------------------------------------------------------------------------- setanimation ("explosion_end"); }, Animation_time * 1000); setTimeOut (() => {console.log ("predeterminado"); setanimation ("predeterminado");}, animation_time * 2000); }; // ----- // Control de animación de onda: //-Iniciar, finalizar y deshacer las ondas de ejecución secuencial de tiempo de ejecución // --------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------<div style={{ width: "100vw", height: "100vh" }}> {/* -------------------------------------------------------------------------------------------<div className="absolute top-4 left-4 bg-white shadow-lg p-4 rounded-lg z-10 w-64 space-y-4"> {/ * Seleccionar archivo de imagen */} <input type="file" accept="image/*" onChange={handleFileChange} className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm focus:ring-2 focus:ring-blue-500 focus:outline-none" /> {/ * Píxel Tamaño de tamaño Campo */}<div className="flex items-center space-x-2"> <label className="text-sm text-gray-700">Tamaño de píxel:</label><input type="number" value={tempPixelSize} onChange={(e) => setMppiXelSize (número (e.target.value))} min = "1" max = "128" classname = "W-16 borde-border-gris-300 redondeado md px-2 py-1 enfoque de texto-sm: anillo-2 enfoque: anillo-blue-500 enfoque: nonal" />>>>></div> {/ * Botón Redisplay */} <button className="w-full bg-green-600 text-white py-2 px-4 rounded-md hover:bg-green-700 transition" onClick={reloadImage} >Redisplay</button> {/ * Botón de control de animación */}<div className="flex flex-wrap gap-2"> <button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" onClick={controlWaveAnimation} >Onda</button> <button className="flex-1 bg-blue-600 text-white py-2 px-4 rounded-md hover:bg-blue-700 transition" onClick={controlExplosionAnimation} >de explosión</button></div></div> {/* ------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[1]"><Canvas camera={{ far: 5000, position: [0, 0, 100] }}> {/ * Configuración de iluminación básica */}<ambientLight intensity={1} /><directionalLight position={[100, 200, 100]} intensity={1} /> {/ * OrbitControls que le permiten rotar la escena con la operación del mouse */}<OrbitControls /> {/ * Render PixelGrid solo si los datos de píxel están presentes */} {Pixels && ( <PixelGrid pixelSize={pixelSize} pixels={pixels} animation={animation} fileChangeCount={fileChangeCount} /> )}</Canvas></div> {/* ----------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------- <div className="absolute top-0 left-0 w-full h-full z-[-1]"><Canvas camera={{ position: [0, 5, 15], fov: 50 }} style={{ background: "black" }} ><ambientLight intensity={0.5} /><pointLight position={[10, 10, 10]} intensity={0.5} color="white" /><BackgroundScene /></Canvas></div></div> ); }; // ==== // Función CreatePixelData: // - Obtener datos de píxeles de la imagen especificada usando Canvas y convertirlo a una matriz de Tipo PixelInfo // ===== const createPixelData = (img: htmlimageElement, TargetWidth: Number, TargethEight: Number) Tamaño especificado const Canvas = document.createElement ("Canvas"); Canvas.Width = TargetWidth; Canvas.Height = Targetheight; const ctx = canvas.getContext ("2d"); if (! ctx) tire un nuevo error ("no contexto 2D disponible"); // Dibuje la imagen en el lienzo y obtenga información de píxeles ctx.drawimage (img, 0, 0, TargetWidth, Targetheight); const imgdata = ctx.getImagegeTata (0, 0, TargetWidth, Targetheight); const data = imgdata.data; Resultado constante: pixelinfo [] = []; Sea idx = 0; // Para cada píxel en la imagen, obtenga el valor RGBA y conviértalo en pixelInfo para (let y = 0; y <targetheight; y ++) {for (let x = 0; x <targetWidth; x ++) {const r = data [idx], g = data [idx+1], b = data [idx+2], a = data [data+3]; IDX += 4; // Ignorar los píxeles con baja transparencia (A <30) if (A <30) continúa; Result.push ({// ajustar las coordenadas x, y para que el centro de la imagen sea el origen x: x - targetwidth / 2, y: -y + targetheight / 2, z: 0, // crea tres.color al convertir el valor RGB en el rango de 0 a 1: nuevo tres.color (r / 255, g / 255, b / 255),}); }} Resultado de retorno; }; // ===== // Componente de backgroundscene: // - un componente para mostrar cielo estrellado y nubes en el fondo // - un envoltorio con memo para evitar redibujados innecesarios // ===== const fondoscene = memo () => {return (<> {/ * Representar el cielo estrellado con componentes estrella */} <Stars radius={100} // 星が存在する空間の半径 depth={50} // 星の配置される深さの範囲 count={5000} // 表示する星の総数 factor={6} // 星の大きさ調整用の係数 saturation={1} // 色の鮮やかさ fade // 遠くの星がフェードアウトする効果 /> {/ * Expresar nubes con componentes de la nube */} <Cloud position={[0, 0, 0]} // 雲の中心位置 opacity={0.1} // 雲の不透明度(低いほど透明) speed={0.2} // 雲の動く速度 scale={[10, 10, 10]} // 雲全体のサイズ segments={20} // 雲を構成するパーティクルの数 /></> ); }); Exportar aplicación predeterminada;
por último
¡Esta vez, utilizamos React Three Fiber para crear arte de píxeles y animaciones dinámicas a partir de imágenes, y usamos React Three Fiber y React Three Drei para animar el arte de píxeles y el arte de píxeles!
En cuanto a la animación, creo que puede crear algo aún más genial si aumenta libremente el número de implementaciones, ¡así que asegúrese de intentarlo!
📌 ¡Puede obtener una comprensión detallada de los movimientos reales viendo el YouTube a continuación!
📺 Mire la demostración en YouTube : puede verlo desde este enlace

📌 El código que creé esta vez se publica en GitHub, así que por favor, échale un vistazo también.
💾 Repositorio de GitHub : ¡verifique el código fuente en este enlace
¡Si usa malla y reemplace con un objeto 3D, incluso puede acercarse a su ideal!
Meshy es un servicio que le permite generar fácilmente objetos 3D usando AI.
Al usar esto, puede crear fácilmente su objeto 3D ideal, ¡así que creo que puede acercarse aún más a su ideal!
📺 Verifique la malla : puede verificarlo en la página oficial desde este enlace

Si lo encontró útil, ¡suscríbase a nuestro canal!
¡Continuaremos creando lecciones y trabajos desde TypeScript X React Three Fiber en el futuro!
Haremos un anuncio en YouTube, así que suscríbase a nuestro canal de YouTube y espere notificaciones.
📺 Mira YouTube : puedes verlo desde este enlace
Si desea saber qué puede hacer React Three Fiber, ¡consulte lo siguiente!
¡Tenemos obras fáciles de usar disponibles!
- Traté de hacer que Bears caminara con React x tres.js!
- Intenté hacer que un viejo baile en React x tres.js!
- ¡Traté de mostrar un modelo 3D con React x tres.js!
- Hice un botón 3D que explota en React x tres.js!
- Reaccione tres fibra x drei x Introducción al mecanografiado! ¡Fondo 3D de estilo Poke Poke hecho con objetos estándar!