diff --git a/src/assets/mainCharacters_pow2/M_porsche_cross_arm_power2.zip b/src/assets/mainCharacters_pow2/M_porsche_cross_arm_power2.zip new file mode 100644 index 0000000..bc401ae Binary files /dev/null and b/src/assets/mainCharacters_pow2/M_porsche_cross_arm_power2.zip differ diff --git a/src/pages/components/animation.jsx b/src/pages/components/animation.jsx index ff3f5e6..b2c4fc4 100644 --- a/src/pages/components/animation.jsx +++ b/src/pages/components/animation.jsx @@ -1,79 +1,403 @@ -import React, { useEffect, useState } from 'react'; -import JSZip from 'jszip'; +//version 1 + // import React, { useEffect, useState } from 'react'; + // import JSZip from 'jszip'; -const Animation = ({src="src/assets/mainCharacters/M_Porsche_cross_arm.zip" ,animationWidth="500px"}) => { - const [images, setImages] = useState([]); - const [currentFrame, setCurrentFrame] = useState(0); - const [loading, setLoading] = useState(true); - const frameRate = 1000/24; + // const Animation = ({src="src/assets/mainCharacters/M_Porsche_cross_arm.zip" ,animationWidth="500px"}) => { + // const [images, setImages] = useState([]); + // const [currentFrame, setCurrentFrame] = useState(0); + // const [loading, setLoading] = useState(true); + // const frameRate = 1000/24; - useEffect(() => { - const loadImages = async () => { - const zip = new JSZip(); - try { - const response = await fetch(src); - if (!response.ok) { - throw new Error('Network response was not ok'); - } - const data = await response.arrayBuffer(); - const zipContent = await zip.loadAsync(data); + // useEffect(() => { + // const loadImages = async () => { + // const zip = new JSZip(); + // try { + // const response = await fetch(src); + // if (!response.ok) { + // throw new Error('Network response was not ok'); + // } + // const data = await response.arrayBuffer(); + // const zipContent = await zip.loadAsync(data); - const imgPromises = []; - zipContent.forEach((relativePath, file) => { - if (file.name.endsWith('.webp')) { - imgPromises.push( - file.async('base64').then(base64 => { - return `data:image/webp;base64,${base64}`; - }) - ); + // const imgPromises = []; + // zipContent.forEach((relativePath, file) => { + // if (file.name.endsWith('.webp')) { + // imgPromises.push( + // file.async('base64').then(base64 => { + // return `data:image/webp;base64,${base64}`; + // }) + // ); + // } + // }); + + // const imgUrls = await Promise.all(imgPromises); + + // if (imgUrls.length === 0) { + // console.error('No images found in the ZIP file.'); + // } + + // setImages(imgUrls); + // } catch (error) { + // console.error('Error loading images:', error); + // } finally { + // setLoading(false); + // } + // }; + + // loadImages(); + + // }, []); + + // useEffect(() => { + // if (images.length > 0) { + // const interval = setInterval(() => { + // setCurrentFrame((prevFrame) => (prevFrame + 1) % images.length); + // }, frameRate); + + // return () => clearInterval(interval); + // } + // }, [images]); + + // if (loading) { + // return
Loading...
; + // } else { + // return ( + //
+ // {images.length > 0 ? ( + // {`Animation + // ) : ( + //
No images to display.
+ // )} + //
+ // ); + // } + // }; + + // export default Animation; + +//version 2 + // import React, { useEffect, useState, useRef } from 'react'; + // import JSZip from 'jszip'; + + // const Animation = ({ src = "src/assets/mainCharacters/M_Porsche_cross_arm.zip", animationWidth = "500px" }) => { + // const [images, setImages] = useState([]); + // const [loading, setLoading] = useState(true); + // const frameRate = 1000 / 24; // Original frame rate (24 FPS) + // const canvasRef = useRef(null); + // const animationRef = useRef(null); + // const currentFrameRef = useRef(0); + // const imageElementsRef = useRef([]); + + // useEffect(() => { + // const loadImages = async () => { + // const zip = new JSZip(); + // try { + // const response = await fetch(src); + // if (!response.ok) { + // throw new Error('Network response was not ok'); + // } + // const data = await response.arrayBuffer(); + // const zipContent = await zip.loadAsync(data); + + // const imgPromises = []; + // zipContent.forEach((relativePath, file) => { + // if (file.name.endsWith('.webp')) { + // imgPromises.push( + // file.async('base64').then(base64 => { + // const img = new Image(); + // img.src = `data:image/webp;base64,${base64}`; + // return img; // Return the Image object + // }) + // ); + // } + // }); + + // const imgElements = await Promise.all(imgPromises); + + // if (imgElements.length === 0) { + // console.error('No images found in the ZIP file.'); + // } + + // imageElementsRef.current = imgElements; // Store preloaded images + // setImages(imgElements); // Set images state + // } catch (error) { + // console.error('Error loading images:', error); + // } finally { + // setLoading(false); + // } + // }; + + // loadImages(); + // }, [src]); + + // useEffect(() => { + // if (images.length > 0) { + // const canvas = canvasRef.current; + // const ctx = canvas.getContext('2d'); + // let lastFrameTime = 0; + + // const animate = (timestamp) => { + // if (lastFrameTime === 0) lastFrameTime = timestamp; + + // const elapsed = timestamp - lastFrameTime; + // if (elapsed > frameRate) { + // currentFrameRef.current = (currentFrameRef.current + 1) % images.length; + // lastFrameTime = timestamp; + // } + + // // Clear the canvas + // ctx.clearRect(0, 0, canvas.width, canvas.height); + // // Draw the current frame + // ctx.drawImage(imageElementsRef.current[currentFrameRef.current], 0, 0, canvas.width, canvas.height); + + // animationRef.current = requestAnimationFrame(animate); + // }; + + // animationRef.current = requestAnimationFrame(animate); + + // return () => { + // if (animationRef.current) { + // cancelAnimationFrame(animationRef.current); + // } + // }; + // } + // }, [images]); + + // if (loading) { + // return
Loading...
; + // } else { + // return ( + //
+ // + //
+ // ); + // } + // }; + + // export default Animation; + + import React, { useEffect, useState, useRef } from 'react'; + import JSZip from 'jszip'; + + const Animation = ({ src = "src/assets/mainCharacters_pow2/M_porsche_cross_arm_power2.zip", animationWidth = "500px" }) => { + const [images, setImages] = useState([]); + const [loading, setLoading] = useState(true); + const frameRate = 1000 / 24; // Original frame rate (24 FPS) + const canvasRef = useRef(null); + const animationRef = useRef(null); + const currentFrameRef = useRef(0); + const imageElementsRef = useRef([]); + + // Load images from the ZIP file + useEffect(() => { + const loadImages = async () => { + const zip = new JSZip(); + try { + const response = await fetch(src); + if (!response.ok) { + throw new Error('Network response was not ok'); } - }); - - const imgUrls = await Promise.all(imgPromises); - - if (imgUrls.length === 0) { - console.error('No images found in the ZIP file.'); + const data = await response.arrayBuffer(); + const zipContent = await zip.loadAsync(data); + + const imgPromises = []; + zipContent.forEach((relativePath, file) => { + if (file.name.endsWith('.webp')) { + imgPromises.push( + file.async('base64').then(base64 => { + const img = new Image(); + img.src = `data:image/webp;base64,${base64}`; + return new Promise((resolve, reject) => { + img.onload = () => resolve(img); + img.onerror = reject; + }); + }) + ); + } + }); + + const imgElements = await Promise.all(imgPromises); + + if (imgElements.length === 0) { + console.error('No images found in the ZIP file.'); + } + + imageElementsRef.current = imgElements; // Store preloaded images + setImages(imgElements); // Set images state + } catch (error) { + console.error('Error loading images:', error); + } finally { + setLoading(false); } + }; + + loadImages(); + }, [src]); + + // WebGL setup and animation loop + useEffect(() => { + if (images.length > 0) { + const canvas = canvasRef.current; + const gl = canvas.getContext('webgl'); + if (!gl) { + console.error("WebGL is not supported."); + return; + } + + // Set up WebGL context (basic) + gl.clearColor(0.0, 0.0, 0.0, 0.0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.viewport(0, 0, canvas.width, canvas.height); - setImages(imgUrls); - } catch (error) { - console.error('Error loading images:', error); - } finally { - setLoading(false); + // Enable blending + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + // gl.blendFunc(gl.SRC_COLOR, gl.DST_COLOR); + + // Shader program + const vertexShaderSource = ` + attribute vec2 a_position; + attribute vec2 a_texCoord; + varying vec2 v_texCoord; + void main() { + gl_Position = vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + } + `; + const fragmentShaderSource = ` + precision mediump float; + uniform sampler2D u_texture; + varying vec2 v_texCoord; + void main() { + gl_FragColor = texture2D(u_texture, v_texCoord); + } + `; + + const createShader = (source, type) => { + const shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + console.error('ERROR compiling shader', gl.getShaderInfoLog(shader)); + } + return shader; + }; + + const vertexShader = createShader(vertexShaderSource, gl.VERTEX_SHADER); + const fragmentShader = createShader(fragmentShaderSource, gl.FRAGMENT_SHADER); + + const shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, vertexShader); + gl.attachShader(shaderProgram, fragmentShader); + gl.linkProgram(shaderProgram); + if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { + console.error('ERROR linking program', gl.getProgramInfoLog(shaderProgram)); + } + gl.useProgram(shaderProgram); + + // Create buffer and set up attributes + const positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + const vertices = new Float32Array([ + -1.0, -1.0, // Bottom-left + 1.0, -1.0, // Bottom-right + -1.0, 1.0, // Top-left + 1.0, 1.0 // Top-right + ]); + gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); + + const positionLocation = gl.getAttribLocation(shaderProgram, "a_position"); + gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(positionLocation); + + // Create texture and load image + const texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + + const loadTexture = (image) => { + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + + gl.generateMipmap(gl.TEXTURE_2D); + }; + + loadTexture(imageElementsRef.current[0]); // Initially load the first image + + // Texture coordinates + const texCoordBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer); + const texCoords = new Float32Array([ + 0.0, 1.0, // Bottom-left (now corresponds to top-left of the image) + 1.0, 1.0, // Bottom-right (now corresponds to top-right of the image) + 0.0, 0.0, // Top-left (now corresponds to bottom-left of the image) + 1.0, 0.0 // Top-right (now corresponds to bottom-right of the image) + ]); + gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW); + + const texCoordLocation = gl.getAttribLocation(shaderProgram, "a_texCoord"); + gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(texCoordLocation); + + const uTextureLocation = gl.getUniformLocation(shaderProgram, "u_texture"); + + let lastFrameTime = 0; + const animate = (timestamp) => { + if (lastFrameTime === 0) lastFrameTime = timestamp; + + const elapsed = timestamp - lastFrameTime; + if (elapsed > frameRate) { + currentFrameRef.current = (currentFrameRef.current + 1) % images.length; + loadTexture(imageElementsRef.current[currentFrameRef.current]); + lastFrameTime = timestamp; + } + + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + animationRef.current = requestAnimationFrame(animate); + }; + + animationRef.current = requestAnimationFrame(animate); + + return () => { + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + } + }; } - }; - - loadImages(); - - }, []); - - useEffect(() => { - if (images.length > 0) { - const interval = setInterval(() => { - setCurrentFrame((prevFrame) => (prevFrame + 1) % images.length); - }, frameRate); - - return () => clearInterval(interval); - } - }, [images]); - - if (loading) { - return
Loading...
; - } else { - return ( -
- {images.length > 0 ? ( - {`AnimationLoading...
; + } else { + return ( +
+ - ) : ( -
No images to display.
- )} -
- ); - } -}; - -export default Animation; + + ); + } + }; + + export default Animation; + + \ No newline at end of file