UNPKG

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.

132 lines 7.72 kB
"use strict"; "use client"; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const framer_motion_1 = require("framer-motion"); /** * ThreeDImageGallery Component * Renders a responsive 3D image gallery with interactive and auto-rotate features. */ const ThreeDImageGallery = ({ // Default images if none are provided images = [ "https://images.pexels.com/photos/2514035/pexels-photo-2514035.jpeg?auto=compress&cs=tinysrgb&w=800", "https://images.pexels.com/photos/816608/pexels-photo-816608.jpeg?auto=compress&cs=tinysrgb&w=800", "https://images.pexels.com/photos/1271620/pexels-photo-1271620.jpeg?auto=compress&cs=tinysrgb&w=800", "https://images.pexels.com/photos/1779487/pexels-photo-1779487.jpeg?auto=compress&cs=tinysrgb&w=800", "https://images.pexels.com/photos/1105666/pexels-photo-1105666.jpeg?auto=compress&cs=tinysrgb&w=800", ], width = 800, // Default width for the gallery container height = 300, // Default height for the gallery container spacing = 1.5, // Default spacing multiplier rotationAngle = 0.1, // Default rotation angle multiplier borderRadius = 0.1, // Default border radius multiplier autoRotate = false, // Default auto-rotate off autoRotateSpeed = 0.3, // Default auto-rotate speed className = "", // Default empty class name style = {}, // Default empty style object onImageClick, // Image click handler }) => { // State to manage the currently active (front-most) image index const [activeIndex, setActiveIndex] = (0, react_1.useState)(Math.floor(images.length / 2)); // Ref for the main container div to get its dimensions for mouse interactions const containerRef = (0, react_1.useRef)(null); // Motion values for X and Y rotation based on mouse position const rotateX = (0, framer_motion_1.useMotionValue)(0); const rotateY = (0, framer_motion_1.useMotionValue)(0); // Effect to handle auto-rotation (0, react_1.useEffect)(() => { let intervalId = null; // Local variable to store the interval ID if (autoRotate) { // Set up an interval to change the active image index intervalId = setInterval(() => { setActiveIndex((prev) => (prev + 1) % images.length); }, autoRotateSpeed * 1000); // Convert speed to milliseconds } // Clean up function for the effect return () => { if (intervalId) { clearInterval(intervalId); // Clear the interval if it was set } }; }, [autoRotate, autoRotateSpeed, images.length]); // Dependencies for the effect // Handler for mouse movement over the gallery container const handleMouseMove = (e) => { // Get the bounding rectangle of the container const { left, top, width, height } = e.currentTarget.getBoundingClientRect(); // Calculate mouse position relative to the container const x = e.clientX - left; const y = e.clientY - top; // Calculate rotation values based on mouse position // Normalized x/y (0 to 1) then mapped to a rotation range (-10 to 10 degrees) const rotateYVal = (x / width - 0.5) * 20; const rotateXVal = (y / height - 0.5) * -20; // Update motion values rotateX.set(rotateXVal); rotateY.set(rotateYVal); }; // Handler for mouse leaving the gallery container const handleMouseLeave = () => { // Reset rotation to 0 when mouse leaves rotateX.set(0); rotateY.set(0); }; return ( // Main container for the 3D gallery (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { ref: containerRef, className: `relative flex items-center justify-center ${className}`, style: { width, // Apply width from props height, // Apply height from props perspective: 1200, // Apply perspective for 3D effect transformStyle: "preserve-3d", // Preserve 3D transformations for children ...style, // Apply any additional inline styles }, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, children: (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { style: { rotateX, // Apply X rotation from motion value rotateY, // Apply Y rotation from motion value transformStyle: "preserve-3d", // Preserve 3D transformations for children }, className: "relative", children: (0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { initial: false, children: images.map((img, index) => { // Calculate offset from the active image const offset = index - activeIndex; // Scale of the image (active image is full size, others are smaller) const scale = index === activeIndex ? 1 : 0.8; // Z-index to ensure correct layering in 3D space const zIndex = -Math.abs(offset); // X translation based on offset and spacing const x = offset * spacing * 200; // 200 is the fixed width of an image // Y rotation for each image to create the 3D fan effect const rotateYImage = offset * -rotationAngle * 180; return ( // Individual image container with motion properties (0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: "absolute rounded-lg overflow-hidden shadow-xl cursor-pointer", style: { width: 200, // Fixed width for each image height: 280, // Fixed height for each image borderRadius: `${borderRadius * 100}px`, // Apply border radius zIndex, // Apply calculated z-index }, // Animation properties for image position, scale, rotation, and opacity animate: { x, scale, rotateY: rotateYImage, opacity: Math.abs(offset) > 2 ? 0 : 1, // Hide images that are too far off-center filter: index === activeIndex ? "brightness(1)" : "brightness(0.7)", // Dim non-active images }, // Spring transition for smooth animations transition: { type: "spring", stiffness: 150, damping: 20 }, // Click handler for images onClick: () => { // If a non-active image is clicked, make it active if (index !== activeIndex) return setActiveIndex(index); // If the active image is clicked, trigger the onImageClick prop onImageClick?.(index); }, children: (0, jsx_runtime_1.jsx)("img", { src: img, alt: `gallery-image-${index}`, className: "w-full h-full object-cover" // Ensure image covers its container , // Fallback for broken image URLs onError: (e) => { e.currentTarget.src = `https://placehold.co/200x280/cccccc/333333?text=Image+Error`; e.currentTarget.onerror = null; // Prevent infinite loop on error } }) }, img)); }) }) }) })); }; exports.default = ThreeDImageGallery; //# sourceMappingURL=3d-image-gallery.js.map