Hello everyone! I’m a beginner with React Three Fiber, and I’m running into a peculiar problem. My animation only becomes visible and starts rendering after I resize the browser window.
Overall project structure: I load in a texture, grab the image data, and tween from a particle sphere with curl noise to particle’s arranged in the formation of the (non-transparent pixels of the) texture.
codesandbox → resize for animation to turn visible
//...imports
function Animation() {
const texture = useLoader(THREE.TextureLoader, 'src/assets/images/logo.png');
const { camera, gl, scene } = useThree();
const PointsRef = useRef();
const materialRef = useRef();
const geometryRef = useRef();
const positionRef = useRef([]);
const positionsRef = useRef([]);
const velocitiesRef = useRef([]);
const initialPositionsRef = useRef([]);
const currentPositionsRef = useRef([]);
useEffect(() => {
if (!texture.image || !PointsRef.current) {
return;
}
let canvas = document.createElement('canvas');
let textureWidth = texture.image.width * 0.35;
let textureHeight = texture.image.height * 0.35;
let aspect = texture.image.width / texture.image.height;
canvas.height = window.innerHeight * 0.55;
canvas.width = canvas.height * aspect;
let context = canvas.getContext('2d');
context.drawImage(texture.image, 0, 0, canvas.width, canvas.height);
let imageData = context.getImageData(0, 0, canvas.width, canvas.height);
let positions = [];
let colors = [];
let position = [];
let velocities = [];
let initialPositions = [];
let currentPositions = [];
let n = 1.0;
for (let y = 0; y < imageData.height; y += n) {
for (let x = 0; x < imageData.width; x += n) {
let index = (x + y * imageData.width) * 4;
let r = imageData.data[index];
let g = imageData.data[index + 1];
let b = imageData.data[index + 2];
if (r > 0 || g > 0 || b > 0) {
let scale = 0.05;
let position = new THREE.Vector3((x - imageData.width / 2) * scale, (-y + imageData.height / 2) * scale, 0);
positions.push(position.x, position.y, position.z);
colors.push(r / 255, g / 255, b / 255);
}
}
}
geometryRef.current = new THREE.BufferGeometry();
geometryRef.current.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
positionsRef.current = positions;
geometryRef.current.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
let pts = new Array(positions.length / 3).fill().map((p, i) => {
let position = new THREE.Vector3()
.randomDirection()
.multiplyScalar(Math.random() * 0.5 + 25);
return position;
});
position = pts;
positionRef.current = position;
initialPositions = [];
for (let i = 0; i < pts.length; i++) {
initialPositions.push(pts[i].x, pts[i].y, pts[i].z);
}
geometryRef.current.setAttribute('initialPosition', new THREE.Float32BufferAttribute(initialPositions, 3));
initialPositionsRef.current = initialPositions;
currentPositions = [...initialPositions];
geometryRef.current.setAttribute('currentPosition', new THREE.Float32BufferAttribute(currentPositions, 3));
currentPositionsRef.current = currentPositions;
velocities = [];
for (let i = 0; i < positions.length / 3; i++) {
let velocity = new THREE.Vector3()
.randomDirection()
.multiplyScalar(Math.random() * 0.5)
velocities.push(velocity.x, velocity.y, velocity.z);
}
geometryRef.current.setAttribute('velocity', new THREE.Float32BufferAttribute(velocities, 3));
velocitiesRef.current = velocities;
let clock = new THREE.Clock();
try {
let material = new THREE.ShaderMaterial({
uniforms: {
transition: { value: 0 },
morphTransition: { value: 0 },
opacity: { value: 1.0 },
time: { value: 0.0 },
velocity: { value: 0.0 },
uSeed: { value: Math.random() },
},
vertexShader: patchedVertexShader,
fragmentShader: patchedFragmentShader,
transparent: true,
depthTest: false,
blending: THREE.NormalBlending,
});
materialRef.current = material;
} catch (error) {
console.error("Error creating ShaderMaterial: ", error);
};
let tl = gsap.timeline();
tl.to(materialRef.current.uniforms.transition, {
value: 1,
duration: 3,
onStart: function() {
geometryRef.current.attributes.initialPosition.copy(geometryRef.current.attributes.currentPosition);
}
});
tl.to(materialRef.current.uniforms.morphTransition, {
value: 1,
duration: 2.9,
ease: "power2.inOut",
});
tl.eventCallback("onComplete", function() {
}, [texture, PointsRef.current]);
}, []);
´´´
[quote="LiamSherwood, post:1, topic:62412, full:true"]
Hello everyone! I'm a beginner with React Three Fiber, and I'm running into a peculiar problem. My animation only becomes visible and starts rendering after I resize the browser window.
Overall project structure: I load in a texture, grab the image data, and tween from a particle sphere with curl noise to particle's arranged in the formation of the (non-transparent pixels of the) texture.
[codesandbox -> resize for animation to turn visible](https://codesandbox.io/p/github/Liamsherwood/Particle-Animation-w--R3F/master?file=%2Fanimation.jsx)
Any advice would be much appreciated! Thanks.
@drcmda
[/quote]
[quote="LiamSherwood, post:1, topic:62412, full:true"]
Hello everyone! I'm a beginner with React Three Fiber, and I'm running into a peculiar problem. My animation only becomes visible and starts rendering after I resize the browser window.
Overall project structure: I load in a texture, grab the image data, and tween from a particle sphere with curl noise to particle's arranged in the formation of the (non-transparent pixels of the) texture.
[codesandbox -> resize for animation to turn visible](https://codesandbox.io/p/github/Liamsherwood/Particle-Animation-w--R3F/master?file=%2Fanimation.jsx)
Any advice would be much appreciated! Thanks.
@drcmda
[/quote]
useFrame(({ clock }) => {
if (materialRef.current && geometryRef.current) {
let material = materialRef.current;
let geometry = geometryRef.current;
let position = positionRef.current;
let positions = positionsRef.current;
let velocities = velocitiesRef.current;
let initialPositions = initialPositionsRef.current;
let t = clock.getElapsedTime();
let tClamped = Math.min(t, 2); // Clamp t to a maximum of 2
if (t <= 2) {
material.uniforms.transition.value = t / 2;
}
// Update the current positions
if (geometryRef.current && geometryRef.current.attributes.currentPosition) {
for (let i = 0; i < positions.length / 3; i++) {
let velocity = new THREE.Vector3(
velocities[i * 3],
velocities[i * 3 + 1],
velocities[i * 3 + 2]
);
let currentPosition = new THREE.Vector3(
initialPositions[i * 3] + velocity.x * tClamped,
initialPositions[i * 3 + 1] + velocity.y * tClamped,
initialPositions[i * 3 + 2] + velocity.z * tClamped
);
geometryRef.current.attributes.currentPosition.setXYZ(i, currentPosition.x, currentPosition.y, currentPosition.z);
}
geometryRef.current.attributes.currentPosition.needsUpdate = true;
material.uniforms.time.value = t;
}
}
});
return (
<>
<points ref={PointsRef} args={[geometryRef.current, materialRef.current]}>
<bufferGeometry attach="geometryRef.current"/>
<shaderMaterial attach="materialRef.current"/>
</points>
{console.log("PointsRef: ", PointsRef.current)}
</>
);
}
export default function App() {
return (
<Canvas
camera={{ fov: 50, aspect: window.innerWidth / window.innerHeight, near: 1, far: 1000, position: [0, 0, 80] }}
style={{ width: '100vw', height: '100vh' }}
>
<PerspectiveCamera makeDefault position={[0, 0, 80]} />
<Suspense fallback={null}>
<Animation />
</Suspense>
<OrbitControls enableDamping={false} zoomToCursor={false} enableRotate={false} maxDistance={100} minDistance={100} />
</Canvas>
);
}
const root = createRoot(document.getElementById('root'));
root.render(<App />);
It’s messy, i know.
Any advice would be much appreciated! Thanks.
@drcmda