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.
194 lines • 10.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = __importStar(require("react"));
// We use forwardRef to expose the DOM element to the parent for direct manipulation
const SliderItem = react_1.default.forwardRef(({ item, onClick }, ref) => {
return ((0, jsx_runtime_1.jsx)("div", { ref: ref, className: "absolute top-1/2 left-1/2 cursor-pointer select-none rounded-xl \r\n shadow-2xl bg-black transform-origin-[0%_100%] pointer-events-auto\r\n w-[var(--width)] h-[var(--height)]\r\n -mt-[calc(var(--height)/2)] -ml-[calc(var(--width)/2)]\r\n overflow-hidden will-change-transform", style: {
'--width': 'clamp(150px, 30vw, 300px)',
'--height': 'clamp(200px, 40vw, 400px)',
transition: 'none', // Critical: handle animation purely via JS
display: 'block', // Ensure initial visibility
}, onClick: onClick, children: (0, jsx_runtime_1.jsxs)("div", { className: "slider-item-content absolute inset-0 z-10 transition-opacity duration-300 ease-out will-change-opacity", style: { opacity: 1 }, children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 z-10 bg-gradient-to-b from-black/30 via-transparent via-50% to-black/50" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute z-10 text-white bottom-5 left-5 text-[clamp(20px,3vw,30px)] drop-shadow-md", children: item.title }), (0, jsx_runtime_1.jsx)("div", { className: "absolute z-10 text-white top-2.5 left-5 text-[clamp(20px,10vw,80px)]", children: item.num }), (0, jsx_runtime_1.jsx)("img", { src: item.imageUrl, alt: item.title, className: "w-full h-full object-cover pointer-events-none", loading: "lazy", decoding: "async" })] }) }));
});
SliderItem.displayName = 'SliderItem';
// --- Main Component: ThreeDSlider ---
const ThreeDSlider = ({ items, speedWheel = 0.05, speedDrag = -0.15, containerStyle = {}, onItemClick, }) => {
// Refs for state that updates 60fps without re-renders
const progressRef = (0, react_1.useRef)(50);
const targetProgressRef = (0, react_1.useRef)(50); // For smooth damping
const isDownRef = (0, react_1.useRef)(false);
const startXRef = (0, react_1.useRef)(0);
const containerRef = (0, react_1.useRef)(null);
const rafRef = (0, react_1.useRef)(null);
// Array of refs to children elements
const itemRefs = (0, react_1.useRef)([]);
// Cache for DOM updates to prevent layout thrashing
const cacheRef = (0, react_1.useRef)({});
const numItems = items.length;
// --- Animation Loop ---
const update = (0, react_1.useCallback)(() => {
if (!itemRefs.current.length)
return;
// Lerp for buttery smoothness
progressRef.current += (targetProgressRef.current - progressRef.current) * 0.1;
const progress = progressRef.current;
const clamped = Math.max(0, Math.min(progress, 100));
// Continuous index
const activeFloat = clamped / 100 * (numItems - 1);
itemRefs.current.forEach((el, index) => {
if (!el)
return;
const denominator = numItems > 1 ? numItems - 1 : 1;
const ratio = (index - activeFloat) / denominator; // -1 (leftmost) to 1 (rightmost)
const tx = ratio * 800;
const ty = ratio * 200;
const rot = ratio * 120;
const dist = Math.abs(index - activeFloat);
const z = numItems - dist;
const opacity = (z / numItems) * 3 - 2;
const newTransform = `translate3d(${tx}%, ${ty}%, 0) rotate(${rot}deg)`;
const newZIndex = Math.round(z * 10).toString();
const newOpacity = Math.max(0, Math.min(1, opacity)).toString();
if (!cacheRef.current[index]) {
cacheRef.current[index] = { transform: '', zIndex: '', opacity: '' };
}
const cache = cacheRef.current[index];
// Only update DOM if changed (prevents thrashing)
if (cache.transform !== newTransform) {
el.style.transform = newTransform;
cache.transform = newTransform;
}
if (cache.zIndex !== newZIndex) {
el.style.zIndex = newZIndex;
cache.zIndex = newZIndex;
}
const inner = el.querySelector('.slider-item-content');
if (inner && cache.opacity !== newOpacity) {
inner.style.opacity = newOpacity;
cache.opacity = newOpacity;
}
});
// Removed rafRef.current = requestAnimationFrame(update); from here
}, [numItems]);
// Start loop
(0, react_1.useEffect)(() => {
let active = true;
const loop = () => {
if (active) {
update();
rafRef.current = requestAnimationFrame(loop);
}
};
// Initialize the loop
rafRef.current = requestAnimationFrame(loop);
return () => {
active = false;
if (rafRef.current) {
cancelAnimationFrame(rafRef.current);
}
};
}, [update]);
// --- Interaction Handlers ---
const handleWheel = (0, react_1.useCallback)((e) => {
const wheelProgress = e.deltaY * speedWheel;
const current = targetProgressRef.current;
const next = current + wheelProgress;
if ((next < 0 && e.deltaY < 0) || (next > 100 && e.deltaY > 0)) {
return;
}
e.preventDefault();
targetProgressRef.current = Math.max(0, Math.min(100, next));
}, [speedWheel]);
const getClientX = (e) => {
if ('touches' in e)
return e.touches[0].clientX;
return e.clientX;
};
const handleMouseDown = (0, react_1.useCallback)((e) => {
isDownRef.current = true;
const x = getClientX(e);
if (x !== undefined)
startXRef.current = x;
}, []);
const handleMouseMove = (0, react_1.useCallback)((e) => {
if (!isDownRef.current)
return;
const x = getClientX(e);
if (x === undefined)
return;
const diff = (x - startXRef.current) * speedDrag;
const current = targetProgressRef.current;
const next = Math.max(0, Math.min(100, current + diff));
targetProgressRef.current = next;
startXRef.current = x;
}, [speedDrag]);
const handleMouseUp = (0, react_1.useCallback)(() => {
isDownRef.current = false;
}, []);
const handleClick = (0, react_1.useCallback)((item, index) => {
const denominator = numItems > 1 ? numItems - 1 : 1;
targetProgressRef.current = (index / denominator) * 100;
if (onItemClick)
onItemClick(item, index);
}, [numItems, onItemClick]);
// --- Listeners ---
(0, react_1.useEffect)(() => {
const container = containerRef.current;
if (!container)
return;
const wheelOpts = { passive: false };
container.addEventListener('wheel', handleWheel, wheelOpts);
container.addEventListener('mousedown', handleMouseDown);
container.addEventListener('touchstart', handleMouseDown, { passive: true });
window.addEventListener('mousemove', handleMouseMove);
window.addEventListener('mouseup', handleMouseUp);
window.addEventListener('touchmove', handleMouseMove, { passive: true });
window.addEventListener('touchend', handleMouseUp);
return () => {
container.removeEventListener('wheel', handleWheel);
container.removeEventListener('mousedown', handleMouseDown);
container.removeEventListener('touchstart', handleMouseDown);
window.removeEventListener('mousemove', handleMouseMove);
window.removeEventListener('mouseup', handleMouseUp);
window.removeEventListener('touchmove', handleMouseMove);
window.removeEventListener('touchend', handleMouseUp);
};
}, [handleWheel, handleMouseDown, handleMouseMove, handleMouseUp]);
return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, className: "relative w-full h-screen overflow-hidden bg-black", style: containerStyle, children: [(0, jsx_runtime_1.jsx)("div", { className: "relative z-10 h-[80vh] overflow-hidden pointer-events-none scale-[0.75] w-full", children: items.map((item, index) => ((0, jsx_runtime_1.jsx)(SliderItem, { ref: (el) => { itemRefs.current[index] = el; }, item: item, index: index, onClick: () => handleClick(item, index) }, `slider-item-${index}`))) }), (0, jsx_runtime_1.jsxs)("div", { className: "absolute inset-0 z-0 pointer-events-none", children: [(0, jsx_runtime_1.jsx)("div", { className: "absolute top-0 left-[90px] w-[10px] h-full border border-y-0 border-white/15" }), (0, jsx_runtime_1.jsx)("div", { className: "absolute bottom-0 left-[30px] text-white/40 rotate-[-90deg] transform-origin-[0%_10%] text-[9px] uppercase leading-relaxed", children: "Code With Muhilan" })] })] }));
};
exports.default = ThreeDSlider;
//# sourceMappingURL=3d-slider.js.map