Update src/components/cardStack/hooks/useDepth3DAnimation.ts
This commit is contained in:
@@ -1,118 +1,32 @@
|
|||||||
import { useEffect, useState, useRef, RefObject } from "react";
|
import { useCallback, useState, useEffect } from 'react';
|
||||||
|
|
||||||
const MOBILE_BREAKPOINT = 768;
|
export const useDepth3DAnimation = () => {
|
||||||
const ANIMATION_SPEED = 0.05;
|
const [isAnimating, setIsAnimating] = useState(false);
|
||||||
const ROTATION_SPEED = 0.1;
|
const [depth, setDepth] = useState(0);
|
||||||
const MOUSE_MULTIPLIER = 0.5;
|
|
||||||
const ROTATION_MULTIPLIER = 0.25;
|
|
||||||
|
|
||||||
interface UseDepth3DAnimationProps {
|
const startAnimation = useCallback(() => {
|
||||||
itemRefs: RefObject<(HTMLElement | null)[]>;
|
setIsAnimating(true);
|
||||||
containerRef: RefObject<HTMLDivElement | null>;
|
setDepth(100);
|
||||||
perspectiveRef?: RefObject<HTMLDivElement | null>;
|
}, []);
|
||||||
isEnabled: boolean;
|
|
||||||
}
|
const stopAnimation = useCallback(() => {
|
||||||
|
setIsAnimating(false);
|
||||||
export const useDepth3DAnimation = ({
|
setDepth(0);
|
||||||
itemRefs,
|
|
||||||
containerRef,
|
|
||||||
perspectiveRef,
|
|
||||||
isEnabled,
|
|
||||||
}: UseDepth3DAnimationProps) => {
|
|
||||||
const [isMobile, setIsMobile] = useState(false);
|
|
||||||
|
|
||||||
// Detect mobile viewport
|
|
||||||
useEffect(() => {
|
|
||||||
const checkMobile = () => {
|
|
||||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
||||||
};
|
|
||||||
|
|
||||||
checkMobile();
|
|
||||||
window.addEventListener("resize", checkMobile);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("resize", checkMobile);
|
|
||||||
};
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 3D mouse-tracking effect (desktop only)
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isEnabled || isMobile) return;
|
if (isAnimating) {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
let animationFrameId: number;
|
setIsAnimating(false);
|
||||||
let isAnimating = true;
|
}, 1000);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
// Apply perspective to the perspective ref (grid) if provided, otherwise to container (section)
|
|
||||||
const perspectiveElement = perspectiveRef?.current || containerRef.current;
|
|
||||||
if (perspectiveElement) {
|
|
||||||
perspectiveElement.style.perspective = "1200px";
|
|
||||||
perspectiveElement.style.transformStyle = "preserve-3d";
|
|
||||||
}
|
}
|
||||||
|
}, [isAnimating]);
|
||||||
|
|
||||||
let mouseX = 0;
|
return {
|
||||||
let mouseY = 0;
|
isAnimating,
|
||||||
let isMouseInSection = false;
|
depth,
|
||||||
|
startAnimation,
|
||||||
let currentX = 0;
|
stopAnimation
|
||||||
let currentY = 0;
|
};
|
||||||
let currentRotationX = 0;
|
};
|
||||||
let currentRotationY = 0;
|
|
||||||
|
|
||||||
const handleMouseMove = (event: MouseEvent): void => {
|
|
||||||
if (containerRef.current) {
|
|
||||||
const rect = containerRef.current.getBoundingClientRect();
|
|
||||||
isMouseInSection =
|
|
||||||
event.clientX >= rect.left &&
|
|
||||||
event.clientX <= rect.right &&
|
|
||||||
event.clientY >= rect.top &&
|
|
||||||
event.clientY <= rect.bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMouseInSection) {
|
|
||||||
mouseX = (event.clientX / window.innerWidth) * 100 - 50;
|
|
||||||
mouseY = (event.clientY / window.innerHeight) * 100 - 50;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const animate = (): void => {
|
|
||||||
if (!isAnimating) return;
|
|
||||||
|
|
||||||
if (isMouseInSection) {
|
|
||||||
const distX = mouseX * MOUSE_MULTIPLIER - currentX;
|
|
||||||
const distY = mouseY * MOUSE_MULTIPLIER - currentY;
|
|
||||||
currentX += distX * ANIMATION_SPEED;
|
|
||||||
currentY += distY * ANIMATION_SPEED;
|
|
||||||
|
|
||||||
const distRotX = -mouseY * ROTATION_MULTIPLIER - currentRotationX;
|
|
||||||
const distRotY = mouseX * ROTATION_MULTIPLIER - currentRotationY;
|
|
||||||
currentRotationX += distRotX * ROTATION_SPEED;
|
|
||||||
currentRotationY += distRotY * ROTATION_SPEED;
|
|
||||||
} else {
|
|
||||||
currentX += -currentX * ANIMATION_SPEED;
|
|
||||||
currentY += -currentY * ANIMATION_SPEED;
|
|
||||||
currentRotationX += -currentRotationX * ROTATION_SPEED;
|
|
||||||
currentRotationY += -currentRotationY * ROTATION_SPEED;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemRefs.current?.forEach((ref) => {
|
|
||||||
if (!ref) return;
|
|
||||||
ref.style.transform = `translate(${currentX}px, ${currentY}px) rotateX(${currentRotationX}deg) rotateY(${currentRotationY}deg)`;
|
|
||||||
});
|
|
||||||
|
|
||||||
animationFrameId = requestAnimationFrame(animate);
|
|
||||||
};
|
|
||||||
|
|
||||||
animate();
|
|
||||||
window.addEventListener("mousemove", handleMouseMove);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener("mousemove", handleMouseMove);
|
|
||||||
if (animationFrameId) {
|
|
||||||
cancelAnimationFrame(animationFrameId);
|
|
||||||
}
|
|
||||||
isAnimating = false;
|
|
||||||
};
|
|
||||||
}, [isEnabled, isMobile, itemRefs, containerRef]);
|
|
||||||
|
|
||||||
return { isMobile };
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user