Hello,
I’m trying to replicate the effect of the threejs canvas and mouse hover in the landing page of https://sparkk.fr/en
I’ve made slight progress as you can see below but its nothing impressive or near what I want.
I tried implementing a radius of effect but that wouldn’t work so I decided to come here for help.
Here is my code, it may need full reworking:
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import { OrbitControls, useGLTF, PerspectiveCamera } from '@react-three/drei';
import * as THREE from 'three';
import gsap from 'gsap';
function Model({ raycaster, mouse }) {
const group = useRef();
const { scene } = useGLTF('/models/st1/cube.glb');
const originalPositions = useRef([]);
const intersectedObjects = useRef(new Set());
const pushDistance = 0.5;
const maxAwayDistance = 1.6
const { camera } = useThree();
useEffect(() => {
let index = 0;
const positions = [];
scene.traverse((child) => {
if (child.isMesh) {
const color = new THREE.Color(`hsl(${index * 30 % 360}, 100%, 50%)`);
if (Array.isArray(child.material)) {
child.material.forEach((material) => {
material.color = color;
material.needsUpdate = true;
});
} else {
child.material.color = color;
child.material.needsUpdate = true;
}
positions.push(child.position.clone());
}
index += 1;
});
originalPositions.current = positions;
}, [scene]);
useFrame(() => {
if (group.current) {
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(group.current.children, true);
intersectedObjects.current.clear();
intersects.forEach(intersect => {
intersectedObjects.current.add(intersect.object);
});
group.current.children.forEach((child, idx) => {
if (child.isMesh) {
const originalPosition = originalPositions.current[idx];
const currentDistance = child.position.distanceTo(originalPosition);
if (intersectedObjects.current.has(child)) {
const intersect = intersects.find((i) => i.object === child);
if (intersect) {
const intersectionPoint = intersect.point;
const pushDirection = new THREE.Vector3().subVectors(child.position, intersectionPoint).normalize();
if (currentDistance < maxAwayDistance) {
gsap.to(child.position, {
duration: 0.5,
x: child.position.x + pushDirection.x * pushDistance,
y: child.position.y + pushDirection.y * pushDistance,
z: child.position.z + pushDirection.z * pushDistance,
ease: 'power2.out'
});
} else {
gsap.to(child.position, {
duration: 0.5,
x: originalPosition.x,
y: originalPosition.y,
z: originalPosition.z,
ease: 'power2.out'
});
}
}
} else {
gsap.to(child.position, {
duration: 0.5,
x: originalPosition.x,
y: originalPosition.y,
z: originalPosition.z,
ease: 'power2.out'
});
}
}
});
}
});
return <primitive object={scene} ref={group} position={[-20,-2,-20]} />;
}
const LandingScene = () => {
const [raycaster] = useState(new THREE.Raycaster());
const mouse = useRef(new THREE.Vector2());
const onMouseMove = (event) => {
mouse.current.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.current.y = -(event.clientY / window.innerHeight) * 2 + 1;
};
useEffect(() => {
window.addEventListener('mousemove', onMouseMove);
return () => window.removeEventListener('mousemove', onMouseMove);
}, []);
return (
<Canvas>
<PerspectiveCamera makeDefault fov={75} aspect={window.innerWidth / window.innerHeight} near={0.1} far={1000} />
<ambientLight intensity={0.5} />
<pointLight position={[10, 10, 10]} />
<Model raycaster={raycaster} mouse={mouse.current} />
</Canvas>
);
};
export default LandingScene;
If anyone can give me any aid it will be heavilt appreciated. I’m content with switching to vanilla threejs instead of fiber if that’s whats needed.
Thank you