lightswind
Version:
A collection of beautifully crafted React Components, Blocks & Templates for Modern Developers. Create stunning web applications effortlessly by using our 160+ professional and animated react components.
140 lines (133 loc) • 6.59 kB
JavaScript
// @ts-nocheck
"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useRef, useEffect } from "react";
export const ThreeDPerspectiveCard = ({ image, width = "300px", height = "350px", }) => {
const cardRef = useRef(null);
const shineRef = useRef(null);
const shadowRef = useRef(null);
useEffect(() => {
// This is an optimization: the mouse move listener should only be
// added if the client is ready and references exist.
if (!cardRef.current || !shineRef.current || !shadowRef.current) {
return;
}
const handleMouseMove = (event) => {
// Get window dimensions
const wHeight = window.innerHeight;
const wWidth = window.innerWidth;
const currentMousePos = { x: event.pageX, y: event.pageY };
const mouseFromCenter = {
x: currentMousePos.x - wWidth / 2,
y: currentMousePos.y - wHeight / 2,
};
// 1. Calculate Rotation for Card (3D Perspective)
const maxRotation = 10; // Max rotation angle in degrees
const mouseXRatio = (currentMousePos.x / wWidth) * 2 - 1; // Range from -1 to 1
const mouseYRatio = (currentMousePos.y / wHeight) * 2 - 1; // Range from -1 to 1
// around1 (RotateX) is inversely proportional to mouse Y
const rotateXDeg = -1 * (mouseYRatio * maxRotation);
// around2 (RotateY) is proportional to mouse X
const rotateYDeg = mouseXRatio * maxRotation;
// 2. Calculate Translation for Floating Effect
const maxTranslate = 20; // Max translation in pixels
const transX = mouseXRatio * maxTranslate;
const transY = mouseYRatio * maxTranslate;
// 3. Calculate Shine Angle (for linear-gradient)
const dy = event.pageY - wHeight / 2;
const dx = event.pageX - wWidth / 2;
// Math.atan2 gives angle in radians, convert to degrees, and adjust
const theta = Math.atan2(dy, dx);
const angle = (theta * 180) / Math.PI - 90;
// 4. Calculate Background Position (Parallax Effect)
const backgroundPositionX = (currentMousePos.x / wWidth) * 100; // 0% to 100%
const backgroundPositionY = (currentMousePos.y / wHeight) * 50; // 0% to 50%
// Apply styles to Shine
shineRef.current.style.background = `linear-gradient(${angle}deg, rgba(255,255,255,${(currentMousePos.y / wHeight) * 0.7 // Intensity based on Y position
}) 0%, rgba(255,255,255, 0) 80%)`;
// Apply styles to Card (3D Rotation, Float, and Parallax)
cardRef.current.style.transform = `translate3d(${transX}px, ${transY}px, 0) scale(1) rotateX(${rotateXDeg}deg) rotateY(${rotateYDeg}deg)`;
cardRef.current.style.backgroundPosition = `${backgroundPositionX}% ${backgroundPositionY}%`;
// Apply styles to Shadow
// This creates a subtle opposite movement and rotation for a deeper shadow effect
shadowRef.current.style.transform = `scale(.9,.9) translateX(${mouseFromCenter.x * -0.02 + 12}px) translateY(${mouseFromCenter.y * -0.02 + 12}px) rotateY(${(mouseFromCenter.x / 25) * 0.5}deg) rotateX(${mouseFromCenter.y / -25}deg)`;
};
// Attach listener to the whole document
document.addEventListener("mousemove", handleMouseMove);
return () => document.removeEventListener("mousemove", handleMouseMove);
}, []);
// The rendering logic remains the same
return (_jsxs("div", { className: "parent-container", children: [_jsxs("div", { className: "wrap", children: [_jsx("div", { className: "card-shadow", ref: shadowRef }), _jsx("div", { className: "card", ref: cardRef, style: {
width,
height,
backgroundImage: `url(${image})`,
// Background size is large to allow for parallax scrolling effect
backgroundSize: "450%",
}, children: _jsx("div", { className: "card-front", children: _jsx("div", { className: "card-shine", ref: shineRef }) }) })] }), _jsx("style", { jsx: true, children: `
.parent-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh; /* Changed to 100vh for demonstration */
position: relative;
width: 100%;
}
.wrap {
/* Defines the 3D space depth for children */
perspective: 1000px;
width: fit-content;
}
.card-shadow,
.card {
position: absolute; /* Changed to absolute to stack shadow and card */
border-radius: 10px;
margin: 0 auto;
/* Add a slight transition for smoother movement */
transition: transform 0.15s ease-out, background-position 0.15s ease-out;
will-change: transform, background-position;
}
.card {
/* Initial position for the card */
background: #fff 50% 50%;
z-index: 2;
}
.card-shadow {
/* Initial position for the shadow */
background: rgba(0, 0, 0, 0.5);
filter: blur(25px);
opacity: 0.8;
width: 95%; /* Make shadow slightly smaller than card */
height: 95%;
z-index: 1;
/* Match initial card dimensions */
top: 2.5%;
left: 2.5%;
/* Also needs a transition for smooth movement */
transition: transform 0.15s ease-out;
will-change: transform;
}
.card-front {
background-color: rgba(0, 0, 0, 0.1);
border-radius: 10px;
width: 100%;
height: 100%;
position: relative;
/* Adds a border for visual depth */
border: 1px solid rgba(255, 255, 255, 0.2);
}
.card-shine {
position: absolute;
width: 100%;
height: 100%;
border-radius: 10px;
z-index: 10; /* Bring the shine layer above the card background */
/* Initial shine gradient */
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.1) 0%,
rgba(255, 255, 255, 0) 60%
);
}
` })] }));
};
export default ThreeDPerspectiveCard;