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.

193 lines 8.57 kB
"use strict"; // import React, { useRef, useState, useEffect } from 'react'; // import { motion, useScroll, useTransform } from 'framer-motion'; // // --- Constants --- // const SLIDE_COUNT = 8; // const SLIDE_REM_WIDTH = 18; // w-72 (18rem) // const SLIDE_MARGIN_REM = 2; // m-4 on both sides (2rem) // const PIXELS_PER_REM = 16; // const SCROLL_HEIGHT_VH = 400; // Vertical scroll duration (400vh) // const SLIDE_DATA = [ // { id: 1, text: "Slide One", color: "bg-orange-500" }, // { id: 2, text: "Slide Two", color: "bg-red-500" }, // { id: 3, text: "Slide Three", color: "bg-violet-500" }, // { id: 4, text: "Slide Four", color: "bg-primarylw" }, // { id: 5, text: "Slide Five", color: "bg-teal-500" }, // { id: 6, text: "Slide Six", color: "bg-yellow-500" }, // { id: 7, text: "Slide Seven", color: "bg-fuchsia-500" }, // { id: 8, text: "Slide Eight", color: "bg-green-500" }, // ]; // /** // * Renders a single 3D rotating slide, driven by the parent's scroll motion. // * @param {object} props - Component props // * @param {number} positionIndex - The index of the slide (0 to 7) // * @param {string} content - Text content for the slide // * @param {string} colorClass - Tailwind background color class // * @param {motion.MotionValue<number>} scrollX - The horizontal scroll offset of the track // */ // const Slide = ({ positionIndex, content, colorClass, scrollX }) => { // const slideRef = useRef(null); // // Total width of one slide block including its margin // const slideBlockWidthPx = (SLIDE_REM_WIDTH + SLIDE_MARGIN_REM * 2) * PIXELS_PER_REM; // // Calculate the position of the slide's center point (relative to the track start) // const slideCenterPositionPx = (positionIndex * slideBlockWidthPx) + (slideBlockWidthPx / 2); // // Define the effective scroll window for the 3D effect // const WINDOW_SIZE_PX = 900; // // --- Framer Motion Mappings --- // // 1. Calculate the distance from the slide's center to the current scroll offset. // const distance = useTransform(scrollX, (x) => { // // Current viewport center relative to the un-translated track position // const viewportCenter = window.innerWidth / 2; // // Current slide center position in the viewport (before track translation is applied) // const currentCenter = slideCenterPositionPx; // // We want distance = 0 when the slide is in the viewport center. // // The track moves by 'x' (negative). // // The slide's position in the viewport is currentCenter + x. // return (currentCenter + x) - viewportCenter; // }); // // 2. Map distance to Rotation (rotateY) // const rotateY = useTransform( // distance, // [-WINDOW_SIZE_PX, 0, WINDOW_SIZE_PX], // ["-45deg", "0deg", "45deg"] // ); // // 3. Map distance to Scale // const scale = useTransform( // distance, // [-WINDOW_SIZE_PX, 0, WINDOW_SIZE_PX], // [0.5, 1.25, 0.65] // 3D perspective effect // ); // // 4. Map distance to TranslateZ (Depth) // const translateZ = useTransform( // distance, // [-WINDOW_SIZE_PX, 0, WINDOW_SIZE_PX], // [64, 16, 64] // In pixels (4em -> 1em -> 4em) // ); // // 5. Map distance to Opacity (Fade) // const opacity = useTransform( // distance, // [-WINDOW_SIZE_PX / 2, 0, WINDOW_SIZE_PX / 2], // [0.2, 1, 0.2] // ); // // 6. Fade effect for the content wrapper (inner text) // const innerOpacity = useTransform( // distance, // [-WINDOW_SIZE_PX / 4, 0, WINDOW_SIZE_PX / 4], // [0, 1, 0] // ); // // Style for the sliding element to achieve the initial offset and sizing // const slideStyle = { // flexShrink: 0, // width: `${SLIDE_REM_WIDTH}rem`, // height: '100%', // margin: `0 ${SLIDE_MARGIN_REM}rem`, // Adjusted margin for calculation // perspective: '40em', // transformOrigin: 'center center', // position: 'relative', // // We rely on the order of elements in the DOM for z-index effect here. // }; // return ( // <motion.div ref={slideRef} style={slideStyle}> // {/* The main content box that rotates */} // <motion.div // className={`content h-full w-full rounded-xl relative shadow-xl ${colorClass}`} // style={{ // transformStyle: 'preserve-3d', // transform: ` // rotateY(${rotateY}) // translateZ(${translateZ}px) // scale(${scale}) // `, // opacity: opacity, // }} // > // <div className="content-wrapper absolute inset-0 flex items-center justify-center p-4"> // <motion.div // className="text-white text-xl font-extrabold text-center uppercase" // style={{ opacity: innerOpacity }} // > // {content} // </motion.div> // </div> // {/* Reflection/Shadow beneath the box (using CSS pseudo-element logic) */} // {/* Use a simple black gradient to simulate the fade/shadow */} // <div className="absolute top-full left-0 w-full h-1/2 overflow-hidden [transform:rotateX(180deg)] [transform-origin:top]"> // <div className="absolute inset-0 [transform:rotateX(180deg)] [transform-origin:top]" // style={{ // background: `linear-gradient(to bottom, rgba(0,0,0,0.5) 0%, rgba(0,0,0,1) 100%)`, // opacity: opacity, // }}> // </div> // </div> // </motion.div> // </motion.div> // ); // }; // /** // * --- Main Pinned Carousel Component --- // * Creates a vertical scroll container (400vh) that pins the viewport and maps // * vertical scroll progress to horizontal translation, creating a 3D horizontal carousel effect. // */ // const ScrollSnapCarouselPin = () => { // const containerRef = useRef(null); // const trackRef = useRef(null); // // State for responsive calculation // const [viewportWidth, setViewportWidth] = useState(window.innerWidth); // useEffect(() => { // const handleResize = () => setViewportWidth(window.innerWidth); // window.addEventListener('resize', handleResize); // // Ensure cleanup function runs when component unmounts // return () => window.removeEventListener('resize', handleResize); // }, []); // // 1. Get vertical scroll progress of the component container // const { scrollYProgress } = useScroll({ // target: containerRef, // offset: ["start start", "end end"] // }); // // Calculate the required horizontal distance to scroll // const slideBlockWidthPx = (SLIDE_REM_WIDTH + SLIDE_MARGIN_REM * 2) * PIXELS_PER_REM; // const totalContentWidth = SLIDE_COUNT * slideBlockWidthPx; // // Starting point X: Position to center the first slide // // End point X: Position to center the last slide // const startX = (viewportWidth / 2) - (slideBlockWidthPx / 2); // const endX = (viewportWidth / 2) - (totalContentWidth - (slideBlockWidthPx / 2)); // // 2. Map vertical scroll progress (0 to 1) to horizontal translation // const x = useTransform(scrollYProgress, [0, 1], [startX, endX]); // return ( // // 3. Outer Wrapper: Gives the large vertical scroll height (400vh) // <div // ref={containerRef} // className="relative z-10 w-full" // // Height set dynamically to ensure scroll lock // style={{ height: `${SCROLL_HEIGHT_VH}vh` }} // > // {/* 4. Pinning Container: Fixed view that holds the carousel */} // <div className="sticky top-0 h-screen overflow-hidden flex items-center justify-center bg-gray-950 p-4"> // <h2 className="absolute top-10 text-2xl font-bold text-white z-20"> // Scroll Down to View 3D Carousel // </h2> // {/* Carousel Viewport */} // <div className="w-full h-[60vh] overflow-visible perspective-[1000px] relative"> // {/* 5. Horizontal Track: This element moves horizontally */} // <motion.div // ref={trackRef} // className="flex items-center h-full absolute" // style={{ x }} // > // {SLIDE_DATA.map((slide, index) => ( // <Slide // key={slide.id} // positionIndex={index} // content={slide.text} // colorClass={slide.color} // scrollX={x} // /> // ))} // </motion.div> // </div> // </div> // </div> // ); // }; // export default ScrollSnapCarouselPin; //# sourceMappingURL=ScrollSnapCarouselPin.js.map