Compare commits
5 commits
main
...
test-anima
Author | SHA1 | Date | |
---|---|---|---|
6b62ebe64f | |||
e4704c62f3 | |||
bdec78f7c6 | |||
f9165a1c93 | |||
22ffb870e8 |
3 changed files with 2 additions and 197 deletions
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Fifty Shades of Bully</title>
|
<title>Vite + React</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|
|
@ -1,195 +0,0 @@
|
||||||
import React, { useEffect, useRef } from 'react';
|
|
||||||
import JSZip from 'jszip';
|
|
||||||
|
|
||||||
const Animation = ({ src, animationWidth }) => {
|
|
||||||
const canvasRef = useRef(null);
|
|
||||||
const imageElementsRef = useRef([]);
|
|
||||||
const animationRef = useRef(null);
|
|
||||||
const currentFrameRef = useRef(0);
|
|
||||||
const loadingRef = useRef(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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
imageElementsRef.current = await Promise.all(imgPromises);
|
|
||||||
|
|
||||||
if (imageElementsRef.current.length === 0) {
|
|
||||||
console.error('No images found in the ZIP file.');
|
|
||||||
}
|
|
||||||
|
|
||||||
loadingRef.current = false; // Set loading to false after images are loaded
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading images:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loadImages();
|
|
||||||
}, [src]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (loadingRef.current || imageElementsRef.current.length === 0) return;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// 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 () => cancelAnimationFrame(animationRef.current);
|
|
||||||
}, [imageElementsRef.current]);
|
|
||||||
|
|
||||||
if (loadingRef.current) {
|
|
||||||
return <div>Loading...</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<canvas ref={canvasRef} width={1920} height={1080} style={{ maxWidth: animationWidth, height: 'auto' }} />
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Animation;
|
|
|
@ -188,7 +188,7 @@
|
||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
import JSZip from 'jszip';
|
import JSZip from 'jszip';
|
||||||
|
|
||||||
const Animation = ({ src = "https://fsob-assets.techtransthai.org/M_Porsche_cross_arm.zip", animationWidth = "500px" }) => {
|
const Animation = ({ src = "src/assets/mainCharacters_pow2/M_porsche_cross_arm_power2.zip", animationWidth = "500px" }) => {
|
||||||
const [images, setImages] = useState([]);
|
const [images, setImages] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const frameRate = 1000 / 24; // Original frame rate (24 FPS)
|
const frameRate = 1000 / 24; // Original frame rate (24 FPS)
|
||||||
|
|
Loading…
Reference in a new issue