@tuel/scroll
Version:
Advanced scroll-triggered animations and effects for React.
1,444 lines (1,442 loc) • 84.2 kB
JavaScript
import gsap7, { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { ReactLenis } from 'lenis/react';
import { useRef, useState, useCallback, useEffect } from 'react';
import * as THREE from 'three';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { isClient, cn } from '@tuel/utils';
import { useReducedMotion } from '@tuel/performance';
import { useScroll, useTransform, motion } from 'framer-motion';
import { SplitText } from 'gsap/SplitText';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
var defaultImages = [
"/assets/img1.jpg",
"/assets/img2.jpg",
"/assets/img3.jpg",
"/assets/img4.jpg",
"/assets/img5.jpg",
"/assets/img6.jpg",
"/assets/img7.jpg"
];
gsap.registerPlugin(ScrollTrigger);
function WodniackWorkScroll({
letters = ["W", "O", "R", "K"],
images = defaultImages,
introText = "( Intro )",
outroText = "( Outro )",
primaryColor = "#f40c3f",
backgroundColor = "#000000",
dotSize = 0.75,
gridSpacing = 20,
letterInstanceCount = 15,
lineSpeedMultipliers = [0.8, 1, 0.7, 0.9],
lerpFactor = 0.07,
enableSmoothScroll = true,
className,
lenisOptions = {
lerp: 0.1,
smoothWheel: true,
duration: 1.2
}
}) {
return /* @__PURE__ */ jsx("div", { className: `wodniack-work-scroll ${className || ""}`, children: enableSmoothScroll ? /* @__PURE__ */ jsx(ReactLenis, { root: true, options: lenisOptions, children: /* @__PURE__ */ jsx(
WodniackWorkScrollContent,
{
letters,
images,
introText,
outroText,
primaryColor,
backgroundColor,
dotSize,
gridSpacing,
letterInstanceCount,
lineSpeedMultipliers,
lerpFactor,
className
}
) }) : /* @__PURE__ */ jsx(
WodniackWorkScrollContent,
{
letters,
images,
introText,
outroText,
primaryColor,
backgroundColor,
dotSize,
gridSpacing,
letterInstanceCount,
lineSpeedMultipliers,
lerpFactor,
className
}
) });
}
function WodniackWorkScrollContent({
letters,
images,
introText,
outroText,
primaryColor,
backgroundColor,
dotSize,
gridSpacing,
letterInstanceCount,
lineSpeedMultipliers,
lerpFactor,
className
}) {
const containerRef = useRef(null);
const workRef = useRef(null);
const textContainerRef = useRef(null);
const gridCanvasRef = useRef(null);
const lettersCanvasRef = useRef(null);
const cardsCanvasRef = useRef(null);
const lettersSceneRef = useRef(null);
const cardsSceneRef = useRef(null);
const lettersCameraRef = useRef(null);
const cardsCameraRef = useRef(null);
const lettersRendererRef = useRef(null);
const cardsRendererRef = useRef(null);
const pathsRef = useRef([]);
const cardsTextureRef = useRef(null);
const textureCanvasRef = useRef(null);
const lenisRef = useRef(null);
const letterPositionsRef = useRef(
/* @__PURE__ */ new Map()
);
const animationIdRef = useRef(null);
const scrollTriggerRef = useRef(null);
const [isReady, setIsReady] = useState(false);
const [loadedTextures, setLoadedTextures] = useState([]);
const lerp = useCallback((start, end, t) => {
return start + (end - start) * t;
}, []);
const initGridCanvas = useCallback(() => {
const canvas = gridCanvasRef.current;
if (!canvas || !workRef.current) return;
const ctx = canvas.getContext("2d");
const resizeCanvas = () => {
const dpr = window.devicePixelRatio || 1;
canvas.width = window.innerWidth * dpr;
canvas.height = window.innerHeight * dpr;
canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;
ctx.scale(dpr, dpr);
};
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
return () => window.removeEventListener("resize", resizeCanvas);
}, []);
const drawGrid = useCallback(
(scrollProgress = 0) => {
const canvas = gridCanvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
ctx.fillStyle = backgroundColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = primaryColor;
const rows = Math.ceil(canvas.height / gridSpacing);
const cols = Math.ceil(canvas.width / gridSpacing) + 15;
const offset = scrollProgress * gridSpacing * 10 % gridSpacing;
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
ctx.beginPath();
ctx.arc(
x * gridSpacing - offset,
y * gridSpacing,
dotSize,
0,
Math.PI * 2
);
ctx.fill();
}
}
},
[backgroundColor, primaryColor, gridSpacing, dotSize]
);
const createTextAnimationPath = useCallback(
(yPos, amplitude) => {
const points = [];
for (let i = 0; i <= 20; i++) {
const t = i / 20;
points.push(
new THREE.Vector3(
-25 + 50 * t,
yPos + Math.sin(t * Math.PI) * -amplitude,
(1 - Math.pow(Math.abs(t - 0.5) * 2, 2)) * -5
)
);
}
const curve = new THREE.CatmullRomCurve3(points);
const line = new THREE.Line(
new THREE.BufferGeometry().setFromPoints(curve.getPoints(100)),
new THREE.LineBasicMaterial({ color: 0, linewidth: 1 })
);
line.curve = curve;
line.letterElements = [];
return line;
},
[]
);
const loadImages = useCallback(async () => {
const textureLoader = new THREE.TextureLoader();
const loadPromises = images.map(
(imageSrc) => new Promise((resolve) => {
textureLoader.load(imageSrc, (texture) => {
texture.generateMipmaps = true;
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter;
resolve(texture);
});
})
);
const textures = await Promise.all(loadPromises);
setLoadedTextures(textures);
return textures;
}, [images]);
const initThreeJS = useCallback(() => {
if (!workRef.current) return;
const lettersScene = new THREE.Scene();
const cardsScene = new THREE.Scene();
lettersSceneRef.current = lettersScene;
cardsSceneRef.current = cardsScene;
const lettersCamera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
1e3
);
const cardsCamera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
0.1,
1e3
);
lettersCamera.position.setZ(20);
cardsCamera.position.setZ(20);
lettersCameraRef.current = lettersCamera;
cardsCameraRef.current = cardsCamera;
const lettersRenderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
lettersRenderer.setSize(window.innerWidth, window.innerHeight);
lettersRenderer.setClearColor(0, 0);
lettersRenderer.setPixelRatio(window.devicePixelRatio);
const cardsRenderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
cardsRenderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
cardsRenderer.setSize(window.innerWidth, window.innerHeight);
cardsRenderer.setClearColor(0, 0);
lettersRendererRef.current = lettersRenderer;
cardsRendererRef.current = cardsRenderer;
const paths = [
createTextAnimationPath(10, 2),
createTextAnimationPath(3.5, 1),
createTextAnimationPath(-3.5, -1),
createTextAnimationPath(-10, -2)
];
paths.forEach((line) => lettersScene.add(line));
pathsRef.current = paths;
if (lettersCanvasRef.current) {
lettersCanvasRef.current.remove();
}
if (cardsCanvasRef.current) {
cardsCanvasRef.current.remove();
}
lettersRenderer.domElement.style.position = "absolute";
lettersRenderer.domElement.style.top = "0";
lettersRenderer.domElement.style.left = "0";
lettersRenderer.domElement.style.pointerEvents = "none";
cardsRenderer.domElement.style.position = "absolute";
cardsRenderer.domElement.style.top = "0";
cardsRenderer.domElement.style.left = "0";
cardsRenderer.domElement.style.pointerEvents = "none";
workRef.current.appendChild(lettersRenderer.domElement);
workRef.current.appendChild(cardsRenderer.domElement);
lettersCanvasRef.current = lettersRenderer.domElement;
cardsCanvasRef.current = cardsRenderer.domElement;
return () => {
lettersRenderer.dispose();
cardsRenderer.dispose();
};
}, [createTextAnimationPath]);
const createLetterElements = useCallback(() => {
if (!textContainerRef.current || !pathsRef.current.length) return;
textContainerRef.current.innerHTML = "";
letterPositionsRef.current.clear();
pathsRef.current.forEach((line, pathIndex) => {
line.letterElements = Array.from({ length: letterInstanceCount }, () => {
const element = document.createElement("div");
element.className = "wodniack-letter";
element.textContent = letters[pathIndex] || "X";
element.style.position = "absolute";
element.style.transform = "translate(-50%, -50%)";
element.style.color = primaryColor;
element.style.fontSize = "4rem";
element.style.fontWeight = "bold";
element.style.pointerEvents = "none";
element.style.zIndex = "10";
textContainerRef.current.appendChild(element);
letterPositionsRef.current.set(element, {
current: { x: 0, y: 0 },
target: { x: 0, y: 0 }
});
return element;
});
});
}, [letters, letterInstanceCount, primaryColor]);
const initCardsTexture = useCallback(async () => {
if (!loadedTextures.length || !cardsSceneRef.current || !cardsRendererRef.current)
return;
const textureCanvas = document.createElement("canvas");
textureCanvas.width = 4096;
textureCanvas.height = 2048;
textureCanvasRef.current = textureCanvas;
const cardsTexture = new THREE.CanvasTexture(textureCanvas);
cardsTexture.generateMipmaps = true;
cardsTexture.minFilter = THREE.LinearMipmapLinearFilter;
cardsTexture.magFilter = THREE.LinearFilter;
cardsTexture.anisotropy = cardsRendererRef.current.capabilities.getMaxAnisotropy();
cardsTexture.wrapS = THREE.RepeatWrapping;
cardsTexture.wrapT = THREE.RepeatWrapping;
cardsTextureRef.current = cardsTexture;
const cardsPlane = new THREE.Mesh(
new THREE.PlaneGeometry(30, 15, 50, 1),
new THREE.MeshBasicMaterial({
map: cardsTexture,
side: THREE.DoubleSide,
transparent: true,
opacity: 1,
depthTest: false,
depthWrite: false
})
);
const positions = cardsPlane.geometry.attributes.position;
for (let i = 0; i < positions.count; i++) {
positions.setZ(i, Math.pow(positions.getX(i) / 15, 2) * 5);
}
positions.needsUpdate = true;
cardsSceneRef.current.add(cardsPlane);
}, [loadedTextures]);
const drawCardsOnCanvas = useCallback(
(offset = 0) => {
const canvas = textureCanvasRef.current;
if (!canvas || !loadedTextures.length) return;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
const cardWidth = canvas.width / 3;
const cardHeight = canvas.height / 2;
const spacing = canvas.width / 2.5;
loadedTextures.forEach((texture, i) => {
if (texture.image) {
ctx.drawImage(
texture.image,
i * spacing + (0.35 - offset) * canvas.width * 5 - cardWidth,
(canvas.height - cardHeight) / 2,
cardWidth,
cardHeight
);
}
});
if (cardsTextureRef.current) {
cardsTextureRef.current.needsUpdate = true;
}
},
[loadedTextures]
);
const updateTargetPositions = useCallback(
(scrollProgress = 0) => {
if (!pathsRef.current.length || !lettersCameraRef.current) return;
pathsRef.current.forEach((line, lineIndex) => {
const speedMultiplier = lineSpeedMultipliers[lineIndex] || 1;
line.letterElements.forEach((element, i) => {
const point = line.curve.getPoint(
(i / (letterInstanceCount - 1) + scrollProgress * speedMultiplier) % 1
);
const vector = point.clone().project(lettersCameraRef.current);
const positions = letterPositionsRef.current.get(element);
if (positions) {
positions.target = {
x: (-vector.x * 0.5 + 0.5) * window.innerWidth,
y: (-vector.y * 0.5 + 0.5) * window.innerHeight
};
}
});
});
},
[letterInstanceCount, lineSpeedMultipliers]
);
const updateLetterPositions = useCallback(() => {
letterPositionsRef.current.forEach((positions, element) => {
const distX = positions.target.x - positions.current.x;
if (Math.abs(distX) > window.innerWidth * 0.7) {
positions.current.x = positions.target.x;
positions.current.y = positions.target.y;
} else {
positions.current.x = lerp(
positions.current.x,
positions.target.x,
lerpFactor
);
positions.current.y = lerp(
positions.current.y,
positions.target.y,
lerpFactor
);
}
element.style.transform = `translate(-50%, -50%) translate3d(${positions.current.x}px, ${positions.current.y}px, 0px)`;
});
}, [lerp, lerpFactor]);
const animate = useCallback(() => {
updateLetterPositions();
if (lettersSceneRef.current && lettersCameraRef.current && lettersRendererRef.current) {
lettersRendererRef.current.render(
lettersSceneRef.current,
lettersCameraRef.current
);
}
if (cardsSceneRef.current && cardsCameraRef.current && cardsRendererRef.current) {
cardsRendererRef.current.render(
cardsSceneRef.current,
cardsCameraRef.current
);
}
animationIdRef.current = requestAnimationFrame(animate);
}, [updateLetterPositions]);
const initScrollTrigger = useCallback(() => {
if (!workRef.current) return;
const scrollTrigger = ScrollTrigger.create({
trigger: workRef.current,
start: "top top",
end: "+=700%",
pin: true,
pinSpacing: true,
scrub: 1,
onUpdate: (self) => {
updateTargetPositions(self.progress);
drawCardsOnCanvas(self.progress);
drawGrid(self.progress);
}
});
scrollTriggerRef.current = scrollTrigger;
}, [updateTargetPositions, drawCardsOnCanvas, drawGrid]);
const handleResize = useCallback(() => {
if (!lettersCameraRef.current || !cardsCameraRef.current || !lettersRendererRef.current || !cardsRendererRef.current)
return;
lettersCameraRef.current.aspect = window.innerWidth / window.innerHeight;
lettersCameraRef.current.updateProjectionMatrix();
cardsCameraRef.current.aspect = window.innerWidth / window.innerHeight;
cardsCameraRef.current.updateProjectionMatrix();
lettersRendererRef.current.setSize(window.innerWidth, window.innerHeight);
cardsRendererRef.current.setSize(window.innerWidth, window.innerHeight);
cardsRendererRef.current.setPixelRatio(
Math.min(window.devicePixelRatio, 2)
);
const canvas = gridCanvasRef.current;
if (canvas) {
const ctx = canvas.getContext("2d");
const dpr = window.devicePixelRatio || 1;
canvas.width = window.innerWidth * dpr;
canvas.height = window.innerHeight * dpr;
canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;
ctx.scale(dpr, dpr);
}
const progress = scrollTriggerRef.current?.progress || 0;
drawGrid(progress);
updateTargetPositions(progress);
}, [drawGrid, updateTargetPositions]);
useEffect(() => {
const init = async () => {
try {
initGridCanvas();
await loadImages();
initThreeJS();
createLetterElements();
await initCardsTexture();
drawGrid(0);
updateTargetPositions(0);
animate();
initScrollTrigger();
window.addEventListener("resize", handleResize);
setIsReady(true);
} catch (error) {
console.error("Failed to initialize WodniackWorkScroll:", error);
}
};
init();
return () => {
if (animationIdRef.current) {
cancelAnimationFrame(animationIdRef.current);
}
if (scrollTriggerRef.current) {
scrollTriggerRef.current.kill();
}
if (lenisRef.current) {
lenisRef.current.destroy();
}
window.removeEventListener("resize", handleResize);
if (lettersRendererRef.current) {
lettersRendererRef.current.dispose();
}
if (cardsRendererRef.current) {
cardsRendererRef.current.dispose();
}
};
}, [
initGridCanvas,
loadImages,
initThreeJS,
createLetterElements,
initCardsTexture,
drawGrid,
updateTargetPositions,
animate,
initScrollTrigger,
handleResize
]);
return /* @__PURE__ */ jsxs(
"div",
{
className: `wodniack-work-scroll-content ${className || ""}`,
ref: containerRef,
children: [
/* @__PURE__ */ jsx("section", { className: "wodniack-intro", children: /* @__PURE__ */ jsx("h1", { children: introText }) }),
/* @__PURE__ */ jsxs("section", { className: "wodniack-work", ref: workRef, children: [
/* @__PURE__ */ jsx("canvas", { ref: gridCanvasRef, className: "wodniack-grid-canvas" }),
/* @__PURE__ */ jsx("div", { ref: textContainerRef, className: "wodniack-text-container" })
] }),
/* @__PURE__ */ jsx("section", { className: "wodniack-outro", children: /* @__PURE__ */ jsx("h1", { children: outroText }) }),
!isReady && /* @__PURE__ */ jsx("div", { className: "wodniack-loading", children: /* @__PURE__ */ jsx("p", { children: "Loading..." }) })
]
}
);
}
// src/hooks/useSSR.ts
function canUseDOM() {
return !!(typeof window !== "undefined" && window.document && window.document.createElement);
}
var gsap2;
var ScrollTrigger2;
if (canUseDOM()) {
import('gsap').then((mod) => {
gsap2 = mod.gsap;
import('gsap/ScrollTrigger').then((st) => {
ScrollTrigger2 = st.ScrollTrigger;
gsap2.registerPlugin(ScrollTrigger2);
});
});
}
function HorizontalScroll({
children,
className,
containerClassName,
speed = 1,
scrub = 1,
pin = true,
snap = false,
direction = "left",
triggerElement,
start = "top top",
end = "bottom top",
onUpdate,
onComplete
}) {
const containerRef = useRef(null);
const scrollRef = useRef(null);
const prefersReducedMotion = useReducedMotion();
useEffect(() => {
if (!canUseDOM() || prefersReducedMotion || !gsap2 || !ScrollTrigger2) {
return;
}
const container = containerRef.current;
const scrollElement = scrollRef.current;
if (!container || !scrollElement) return;
const scrollWidth = scrollElement.scrollWidth;
const containerWidth = container.offsetWidth;
const scrollDistance = scrollWidth - containerWidth;
const scrollDirection = direction === "left" ? -scrollDistance : scrollDistance;
const animation = gsap2.to(scrollElement, {
x: scrollDirection * speed,
ease: "none",
scrollTrigger: {
trigger: triggerElement || container,
start,
end: end === "auto" ? `+=${scrollDistance}` : end,
scrub,
pin: pin ? container : false,
snap: typeof snap === "boolean" ? void 0 : snap,
invalidateOnRefresh: true,
anticipatePin: 1,
onUpdate: (self) => {
if (onUpdate) {
onUpdate(self.progress);
}
}
}
});
return () => {
animation.kill();
};
}, [
speed,
scrub,
pin,
snap,
direction,
triggerElement,
start,
end,
onUpdate,
onComplete,
prefersReducedMotion
]);
return /* @__PURE__ */ jsx(
"div",
{
ref: containerRef,
className: cn("overflow-hidden", containerClassName),
children: /* @__PURE__ */ jsx("div", { ref: scrollRef, className: cn("flex w-fit", className), children })
}
);
}
function HorizontalScrollItem({
children,
className,
width = "auto"
}) {
return /* @__PURE__ */ jsx("div", { className: cn("flex-shrink-0", className), "data-width": width, children });
}
if (typeof window !== "undefined") {
gsap.registerPlugin(ScrollTrigger);
}
var cubesData = {
"cube-1": {
initial: {
top: -55,
left: 37.5,
rotateX: 360,
rotateY: -360,
rotateZ: -48,
z: -3e4
},
final: {
top: 50,
left: 15,
rotateX: 0,
rotateY: 3,
rotateZ: 0,
z: 0
}
},
"cube-2": {
initial: {
top: -35,
left: 12.5,
rotateX: -360,
rotateY: 360,
rotateZ: 135,
z: -3e4
},
final: {
top: 25,
left: 25,
rotateX: 1,
rotateY: 2,
rotateZ: 0,
z: 0
}
},
"cube-3": {
initial: {
top: -75,
left: 87.5,
rotateX: -360,
rotateY: -360,
rotateZ: 180,
z: -3e4
},
final: {
top: 75,
left: 15,
rotateX: 0,
rotateY: -1,
rotateZ: 0,
z: 0
}
},
"cube-4": {
initial: {
top: -75,
left: 12.5,
rotateX: 360,
rotateY: 360,
rotateZ: 90,
z: -3e4
},
final: {
top: 75,
left: 55,
rotateX: 0,
rotateY: 0,
rotateZ: 0,
z: 0
}
},
"cube-5": {
initial: {
top: -55,
left: 62.5,
rotateX: 360,
rotateY: 360,
rotateZ: -135,
z: -3e4
},
final: {
top: 25,
left: 75,
rotateX: -1,
rotateY: -2,
rotateZ: 0,
z: 0
}
},
"cube-6": {
initial: {
top: -35,
left: 67.5,
rotateX: -180,
rotateY: -360,
rotateZ: -180,
z: -3e4
},
final: {
top: 50,
left: 85,
rotateX: 0,
rotateY: -3,
rotateZ: 0,
z: 0
}
}
};
function Cube({ cubeKey, images, className = "" }) {
const data = cubesData[cubeKey];
return /* @__PURE__ */ jsxs(
"div",
{
className: `cube absolute z-10 w-20 h-20 md:w-24 md:h-24 preserve-3d ${className}`,
style: {
transformStyle: "preserve-3d",
perspective: "1000px",
top: `${data.initial.top}%`,
left: `${data.initial.left}%`,
transform: `
translate3d(-50%, -50%, ${data.initial.z}px)
rotateX(${data.initial.rotateX}deg)
rotateY(${data.initial.rotateY}deg)
rotateZ(${data.initial.rotateZ}deg)
`
},
children: [
/* @__PURE__ */ jsx(
"div",
{
className: "cube-face absolute w-full h-full",
style: { transform: "rotateY(0deg) translateZ(50px)" },
children: /* @__PURE__ */ jsx(
"img",
{
src: images[0] || "/api/placeholder/300/300",
alt: "cube-face",
className: "w-full h-full object-cover"
}
)
}
),
/* @__PURE__ */ jsx(
"div",
{
className: "cube-face absolute w-full h-full",
style: { transform: "rotateY(180deg) translateZ(50px)" },
children: /* @__PURE__ */ jsx(
"img",
{
src: images[1] || "/api/placeholder/300/300",
alt: "cube-face",
className: "w-full h-full object-cover"
}
)
}
),
/* @__PURE__ */ jsx(
"div",
{
className: "cube-face absolute w-full h-full",
style: { transform: "rotateY(90deg) translateZ(50px)" },
children: /* @__PURE__ */ jsx(
"img",
{
src: images[2] || "/api/placeholder/300/300",
alt: "cube-face",
className: "w-full h-full object-cover"
}
)
}
),
/* @__PURE__ */ jsx(
"div",
{
className: "cube-face absolute w-full h-full",
style: { transform: "rotateY(-90deg) translateZ(50px)" },
children: /* @__PURE__ */ jsx(
"img",
{
src: images[3] || "/api/placeholder/300/300",
alt: "cube-face",
className: "w-full h-full object-cover"
}
)
}
),
/* @__PURE__ */ jsx(
"div",
{
className: "cube-face absolute w-full h-full",
style: { transform: "rotateX(90deg) translateZ(50px)" },
children: /* @__PURE__ */ jsx(
"img",
{
src: images[4] || "/api/placeholder/300/300",
alt: "cube-face",
className: "w-full h-full object-cover"
}
)
}
),
/* @__PURE__ */ jsx(
"div",
{
className: "cube-face absolute w-full h-full",
style: { transform: "rotateX(-90deg) translateZ(50px)" },
children: /* @__PURE__ */ jsx(
"img",
{
src: images[5] || "/api/placeholder/300/300",
alt: "cube-face",
className: "w-full h-full object-cover"
}
)
}
)
]
}
);
}
function OrchestraCubes({
className = "",
header1 = "The first media company crafted for the digital first generation.",
header2 = "Where innovation meets precision.",
subtitle = "Symphonia unites visionary thinkers, creative architects, and analytical experts, collaborating seamlessly to transform challenges into opportunities. Together, we deliver tailored solutions that drive impact and inspire growth.",
images = Array.from(
{ length: 36 },
(_, i) => `/api/placeholder/300/300?seed=${i + 1}`
),
stickyHeight = 4,
backgroundColor = "#331707",
textColor = "#ffe9d9"
}) {
const stickyRef = useRef(null);
const logoRef = useRef(null);
const cubesRef = useRef(null);
const header1Ref = useRef(null);
const header2Ref = useRef(null);
const [cubeRefs] = useState(() => {
const refs = {};
Object.keys(cubesData).forEach((key) => {
refs[key] = { current: null };
});
return refs;
});
const interpolate = (start, end, progress) => {
return start + (end - start) * progress;
};
useEffect(() => {
if (!stickyRef.current) return;
Object.entries(cubesData).forEach(([cubeKey, data]) => {
const cubeRef = cubeRefs[cubeKey];
if (!cubeRef.current) return;
cubeRef.current.style.top = `${data.initial.top}%`;
cubeRef.current.style.left = `${data.initial.left}%`;
cubeRef.current.style.transform = `
translate3d(-50%, -50%, ${data.initial.z}px)
rotateX(${data.initial.rotateX}deg)
rotateY(${data.initial.rotateY}deg)
rotateZ(${data.initial.rotateZ}deg)
`;
});
const scrollTrigger = ScrollTrigger.create({
trigger: stickyRef.current,
start: "top top",
end: `+=${stickyHeight * 100}vh`,
pin: true,
scrub: 1,
onUpdate: (self) => {
if (!logoRef.current || !cubesRef.current || !header1Ref.current || !header2Ref.current) {
return;
}
const logo = logoRef.current;
const cubes = cubesRef.current;
const header12 = header1Ref.current;
const header22 = header2Ref.current;
if (self.progress <= 0.2) {
const logoProgress = self.progress * 5;
const logoScale = interpolate(1, 0, logoProgress);
const logoOpacity = interpolate(1, 0, logoProgress);
logo.style.transform = `translate(-50%, -50%) scale(${logoScale})`;
logo.style.opacity = String(logoOpacity);
cubes.style.opacity = String(logoProgress);
} else {
logo.style.opacity = "0";
cubes.style.opacity = "1";
}
if (self.progress >= 0.2 && self.progress <= 0.4) {
const header1StartProgress = (self.progress - 0.2) * 5;
const header1Progress = Math.max(
0,
Math.min(header1StartProgress, 1)
);
const header1Scale = interpolate(0.75, 1, header1Progress);
const header1Blur = interpolate(10, 0, header1Progress);
header12.style.transform = `translate(-50%, -50%) scale(${header1Scale})`;
header12.style.filter = `blur(${header1Blur}px)`;
header12.style.opacity = String(header1Progress);
} else if (self.progress > 0.4) {
header12.style.opacity = "0";
}
if (self.progress >= 0.4 && self.progress <= 0.6) {
const header2StartProgress = (self.progress - 0.4) * 10;
const header2Progress = Math.max(
0,
Math.min(header2StartProgress, 1)
);
const header2Scale = interpolate(0.75, 1, header2Progress);
const header2Blur = interpolate(10, 0, header2Progress);
header22.style.transform = `translate(-50%, -50%) scale(${header2Scale})`;
header22.style.filter = `blur(${header2Blur}px)`;
header22.style.opacity = String(header2Progress);
}
const firstPhaseProgress = Math.min(self.progress * 2, 1);
const secondPhaseProgress = self.progress >= 0.5 ? (self.progress - 0.5) * 2 : 0;
Object.entries(cubesData).forEach(([cubeKey, data]) => {
const cubeRef = cubeRefs[cubeKey];
if (!cubeRef.current) return;
const { initial, final } = data;
const currentTop = interpolate(
initial.top,
final.top,
firstPhaseProgress
);
const currentLeft = interpolate(
initial.left,
final.left,
firstPhaseProgress
);
const currentRotateX = interpolate(
initial.rotateX,
final.rotateX,
firstPhaseProgress
);
const currentRotateY = interpolate(
initial.rotateY,
final.rotateY,
firstPhaseProgress
);
const currentRotateZ = interpolate(
initial.rotateZ,
final.rotateZ,
firstPhaseProgress
);
const currentZ = interpolate(initial.z, final.z, firstPhaseProgress);
let additionalRotation = 0;
if (cubeKey === "cube-2") {
additionalRotation = interpolate(0, 180, secondPhaseProgress);
} else if (cubeKey === "cube-4") {
additionalRotation = interpolate(0, -180, secondPhaseProgress);
}
cubeRef.current.style.top = `${currentTop}%`;
cubeRef.current.style.left = `${currentLeft}%`;
cubeRef.current.style.transform = `
translate3d(-50%, -50%, ${currentZ}px)
rotateX(${currentRotateX}deg)
rotateY(${currentRotateY + additionalRotation}deg)
rotateZ(${currentRotateZ}deg)
`;
});
}
});
return () => {
scrollTrigger.kill();
};
}, [stickyHeight, cubeRefs]);
const cubeImages = Object.keys(cubesData).map(
(_, index) => images.slice(index * 6, (index + 1) * 6)
);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsxs(
"div",
{
ref: stickyRef,
className: `orchestra-cubes relative w-full h-screen overflow-hidden ${className}`,
"data-bg": backgroundColor,
"data-color": textColor,
children: [
/* @__PURE__ */ jsxs(
"div",
{
ref: logoRef,
className: "absolute top-[25%] left-1/2 transform -translate-x-1/2 -translate-y-1/2 flex gap-6 z-20",
children: [
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 bg-[#ffe9d9]" }),
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 bg-[#ffe9d9]" })
] }),
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 bg-[#ffe9d9]" }),
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 bg-[#ffe9d9]" })
] }),
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 bg-[#ffe9d9]" }),
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 bg-[#ffe9d9]" })
] })
]
}
),
/* @__PURE__ */ jsx("div", { ref: cubesRef, className: "cubes-container opacity-0", children: Object.keys(cubesData).map((cubeKey, index) => /* @__PURE__ */ jsx("div", { ref: cubeRefs[cubeKey], children: /* @__PURE__ */ jsx(Cube, { cubeKey, images: cubeImages[index] }) }, cubeKey)) }),
/* @__PURE__ */ jsx(
"div",
{
ref: header1Ref,
className: "absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-center z-10 max-w-4xl px-8",
children: /* @__PURE__ */ jsx("h1", { className: "text-4xl md:text-6xl font-bold leading-tight text-[#ffe9d9]", children: header1 })
}
),
/* @__PURE__ */ jsxs(
"div",
{
ref: header2Ref,
className: "header-2 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-center z-10 max-w-2xl px-8 opacity-0",
children: [
/* @__PURE__ */ jsx("h2", { className: "text-3xl md:text-5xl font-bold mb-6 text-[#ffe9d9]", children: header2 }),
/* @__PURE__ */ jsx("p", { className: "text-lg md:text-xl leading-relaxed text-[#ffe9d9]", children: subtitle })
]
}
)
]
}
),
/* @__PURE__ */ jsx("section", { className: "relative w-full h-screen flex justify-center items-center text-center bg-[#cdb9ab] text-[#331707]", children: /* @__PURE__ */ jsx("h2", { className: "text-3xl md:text-5xl font-bold", children: "Your next section goes here" }) })
] });
}
if (typeof window !== "undefined") {
gsap.registerPlugin(ScrollTrigger);
}
var defaultCards = [
{
id: 1,
title: "Silent Veil",
productCode: "PROD8372",
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM2NjY2NjYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMzMzMzMzMiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PC9zdmc+"
},
{
id: 2,
title: "Crimson Echoes",
productCode: "PROD4921",
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNkYzI2MjYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM5OTE5MTkiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PC9zdmc+"
},
{
id: 3,
title: "Zenith Arc",
productCode: "PROD7586",
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMyNTYzZWIiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxZTQwYWYiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PC9zdmc+"
},
{
id: 4,
title: "Moonspire Dream",
productCode: "PROD3410",
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM2MzY2ZjEiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiM0MzM4Y2EiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PC9zdmc+"
},
{
id: 5,
title: "Ember Flux",
productCode: "PROD9053",
image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjYwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNmOTc4MTYiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNlYTU4MDYiLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2cpIi8+PC9zdmc+"
}
];
function OrkenWorldScroll({ className = "" }) {
const stickyRef = useRef(null);
const outlineCanvasRef = useRef(null);
const fillCanvasRef = useRef(null);
const cardsRef = useRef(null);
const triangleStatesRef = useRef(/* @__PURE__ */ new Map());
const animationFrameIdRef = useRef(null);
const canvasXPositionRef = useRef(0);
const TRIANGLE_SIZE = 15;
const TRIANGLE_ROWS = 100;
const TRIANGLE_COLS = 200;
const SCALE_THRESHOLD = 0.1;
const setCanvasSize = (canvas, ctx) => {
const rect = canvas.getBoundingClientRect();
const pixelRatio = window.devicePixelRatio || 1;
canvas.width = rect.width * pixelRatio;
canvas.height = rect.height * pixelRatio;
ctx.scale(pixelRatio, pixelRatio);
canvas.style.width = rect.width + "px";
canvas.style.height = rect.height + "px";
};
const drawTriangle = (ctx, x, y, size, flipped, outlineScale, fillScale, lineWidth) => {
const halfSize = size * 0.5;
if (outlineScale >= SCALE_THRESHOLD) {
ctx.save();
ctx.translate(x, y);
ctx.scale(outlineScale, outlineScale);
ctx.translate(-x, -y);
ctx.beginPath();
if (!flipped) {
ctx.moveTo(x, y - halfSize);
ctx.lineTo(x + halfSize, y + halfSize);
ctx.lineTo(x - halfSize, y + halfSize);
} else {
ctx.moveTo(x, y + halfSize);
ctx.lineTo(x + halfSize, y - halfSize);
ctx.lineTo(x - halfSize, y - halfSize);
}
ctx.closePath();
ctx.strokeStyle = "rgba(255, 255, 255, 0.075)";
ctx.lineWidth = lineWidth;
ctx.stroke();
}
if (fillScale >= SCALE_THRESHOLD) {
ctx.save();
ctx.translate(x, y);
ctx.scale(fillScale, fillScale);
ctx.translate(-x, -y);
ctx.beginPath();
if (!flipped) {
ctx.moveTo(x, y - halfSize);
ctx.lineTo(x + halfSize, y + halfSize);
ctx.lineTo(x - halfSize, y + halfSize);
} else {
ctx.moveTo(x, y + halfSize);
ctx.lineTo(x + halfSize, y - halfSize);
ctx.lineTo(x - halfSize, y - halfSize);
}
ctx.closePath();
ctx.fillStyle = "#ff6b00";
ctx.strokeStyle = "#ff6b00";
ctx.lineWidth = lineWidth;
ctx.stroke();
ctx.fill();
ctx.restore();
}
};
const drawGrid = (scrollProgress = 0) => {
const outlineCanvas = outlineCanvasRef.current;
const fillCanvas = fillCanvasRef.current;
if (!outlineCanvas || !fillCanvas) return;
const outlineCtx = outlineCanvas.getContext("2d");
const fillCtx = fillCanvas.getContext("2d");
if (!outlineCtx || !fillCtx) return;
if (animationFrameIdRef.current) {
cancelAnimationFrame(animationFrameIdRef.current);
}
outlineCtx.clearRect(0, 0, outlineCanvas.width, outlineCanvas.height);
fillCtx.clearRect(0, 0, fillCanvas.width, fillCanvas.height);
const animationProgress = scrollProgress <= 0.65 ? 0 : (scrollProgress - 0.65) / 0.35;
let needsUpdate = false;
const canvasWidth = outlineCanvas.style.width ? parseInt(outlineCanvas.style.width.replace("px", "")) : outlineCanvas.width;
outlineCanvas.style.height ? parseInt(outlineCanvas.style.height.replace("px", "")) : outlineCanvas.height;
for (let row = 0; row < TRIANGLE_ROWS; row++) {
for (let col = 0; col < TRIANGLE_COLS; col++) {
const isFlipped = (row + col) % 2 === 1;
const key = `${row}-${col}`;
const triangle = triangleStatesRef.current.get(key);
if (!triangle) continue;
const targetScale = triangle.order <= animationProgress ? 1 : 0;
const scaleSpeed = 0.08;
if (triangle.scale !== targetScale) {
triangle.scale += (targetScale - triangle.scale) * scaleSpeed;
needsUpdate = true;
}
const x = col * (TRIANGLE_SIZE * 0.75) + canvasXPositionRef.current;
const y = row * (TRIANGLE_SIZE * 0.85) + (isFlipped ? TRIANGLE_SIZE * 0.3 : 0);
if (x + TRIANGLE_SIZE < 0 || x - TRIANGLE_SIZE > canvasWidth) continue;
const outlineScale = Math.min(triangle.scale + 0.3, 1);
const fillScale = Math.max(triangle.scale - 0.2, 0);
drawTriangle(
outlineCtx,
x,
y,
TRIANGLE_SIZE,
isFlipped,
outlineScale,
0,
1
);
drawTriangle(fillCtx, x, y, TRIANGLE_SIZE, isFlipped, 0, fillScale, 1);
}
}
if (needsUpdate) {
animationFrameIdRef.current = requestAnimationFrame(
() => drawGrid(scrollProgress)
);
}
};
const initializeTriangles = () => {
const positions = [];
for (let row = 0; row < TRIANGLE_ROWS; row++) {
for (let col = 0; col < TRIANGLE_COLS; col++) {
positions.push({ key: `${row}-${col}`, row, col });
}
}
const totalTriangles = positions.length;
for (let i = positions.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[positions[i], positions[j]] = [positions[j], positions[i]];
}
triangleStatesRef.current.clear();
positions.forEach((pos, index) => {
triangleStatesRef.current.set(pos.key, {
order: index / totalTriangles,
scale: 0,
row: pos.row,
col: pos.col
});
});
};
useEffect(() => {
const outlineCanvas = outlineCanvasRef.current;
const fillCanvas = fillCanvasRef.current;
const stickySection = stickyRef.current;
const cards = cardsRef.current;
if (!outlineCanvas || !fillCanvas || !stickySection || !cards) return;
const outlineCtx = outlineCanvas.getContext("2d");
const fillCtx = fillCanvas.getContext("2d");
if (!outlineCtx || !fillCtx) return;
setCanvasSize(outlineCanvas, outlineCtx);
setCanvasSize(fillCanvas, fillCtx);
initializeTriangles();
drawGrid();
const stickyHeight = window.innerHeight * 5;
const scrollTrigger = ScrollTrigger.create({
trigger: stickySection,
start: "top top",
end: `+=${stickyHeight}px`,
pin: true,
onUpdate: (self) => {
canvasXPositionRef.current = -self.progress * 200;
drawGrid(self.progress);
const progress = Math.min(self.progress / 0.654, 1);
const translateX = -progress * 200;
cards.style.transform = `translateX(${translateX}%)`;
}
});
const handleResize = () => {
if (outlineCtx && fillCtx) {
setCanvasSize(outlineCanvas, outlineCtx);
setCanvasSize(fillCanvas, fillCtx);
drawGrid();
}
};
window.addEventListener("resize", handleResize);
return () => {
if (animationFrameIdRef.current) {
cancelAnimationFrame(animationFrameIdRef.current);
}
scrollTrigger.kill();
window.removeEventListener("resize", handleResize);
};
}, []);
return /* @__PURE__ */ jsxs(
"div",
{
className: `min-h-screen ${className}`,
style: { height: "800vh", fontFamily: '"PP Neue Montreal", sans-serif' },
children: [
/* @__PURE__ */ jsx("section", { className: "relative w-screen h-screen overflow-hidden flex justify-center items-center bg-black text-white", children: /* @__PURE__ */ jsxs("h1", { className: "text-center text-4xl md:text-6xl lg:text-[80px] uppercase font-light leading-none", children: [
/* @__PURE__ */ jsx("span", { className: "text-orange-500", children: "Enter a Universe" }),
" Powered by Imagination"
] }) }),
/* @__PURE__ */ jsxs(
"section",
{
ref: stickyRef,
className: "relative w-screen h-screen overflow-hidden",
children: [
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 w-full h-full bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900" }),
/* @__PURE__ */ jsx(
"canvas",
{
ref: outlineCanvasRef,
className: "absolute top-0 left-0 z-[1]",
style: { width: "150%", height: "150%" }
}
),
/* @__PURE__ */ jsx(
"div",
{
ref: cardsRef,
className: "absolute top-0 left-0 w-[300%] h-screen flex justify-around items-center z-[2]",
style: { willChange: "transform" },
children: defaultCards.map((card) => /* @__PURE__ */ jsxs(
"div",
{
className: "relative w-[10%] h-[75%] bg-black rounded-xl overflow-hidden flex flex-col",
children: [
/* @__PURE__ */ jsx("div", { className: "flex-1 w-full", children: /* @__PURE__ */ jsx(
"img",
{
src: card.image,
alt: card.title,
className: "w-full h-full object-cover"
}
) }),
/* @__PURE__ */ jsxs("div", { className: "absolute bottom-5 left-5 text-white", children: [
/* @__PURE__ */ jsx("h1", { className: "text-2xl mb-1 font-light uppercase", children: card.title }),
/* @__PURE__ */ jsx("p", { className: "text-sm opacity-70 font-medium", children: card.productCode })
] })
]
},
card.id
))
}
),
/* @__PURE__ */ jsx(
"canvas",
{
ref: fillCanvasRef,
className: "absolute top-0 left-0 z-[3]",
style: { width: "150%", height: "150%" }
}
)
]
}
),
/* @__PURE__ */ jsx("section", { className: "relative w-screen h-screen overflow-hidden flex justify-center items-center bg-black text-white", children: /* @__PURE__ */ jsxs("h1", { className: "text-center text-4xl md:text-6xl lg:text-[80px] uppercase font-light leading-none", children: [
"Chase the ",
/* @__PURE__ */ jsx("span", { className: "text-orange-500", children: "shadows" }),
" to embrace the light"
] }) })
]
}
);
}
function ParallaxScroll({
children,
className,
speed = 0.5,
offset = ["start end", "end start"],
direction = "vertical",
fadeIn = false,
scaleOnScroll = false,
rotateOnScroll = false
}) {
const ref = useRef(null);
const { scrollYProgress } = useScroll({
target: ref,
offset
});
const distance = 100 * speed;
const y = useTransform(scrollYProgress, [0, 1], [-distance, distance]);
const x = useTransform(scrollYProgress, [0, 1], [-distance, distance]);
const opacity = useTransform(
scrollYProgress,
[0, 0.5, 1],
fadeIn ? [0, 1, 0] : [1, 1, 1]
);
const scale = useTransform(
scrollYProgress,
[0, 0.5, 1],
scaleOnScroll ? [0.8, 1, 0.8] : [1, 1, 1]
);
const rotate = useTransform(
scrollYProgress,
[0, 1],
rotateOnScroll ? [-5, 5] : [0, 0]
);
const transforms = {
opacity,
scale,
rotate
};
if (direction === "vertical") {
transforms.y = y;
} else {
transforms.x = x;
}
return /* @__PURE__ */ jsx(
motion.div,
{
ref,
style: transforms,
className: cn("will-change-transform", className),
children
}
);
}
function ParallaxLayer({
children,
speed,
className,
offset,
direction,
fadeIn,
scaleOnScroll,
rotateOnScroll
}) {
const scrollOffset = typeof offset === "number" ? [`${offset * 100}% end`, `${(offset + 1) * 100}% start`] : offset || ["start end", "end start"];
return /* @__PURE__ */ jsx(
ParallaxScroll,
{
speed,
className,
offset: scrollOffset,
direction,
fadeIn,
scaleOnScroll,
rotateOnScroll,
children
}
);
}
function ParallaxContainer({
children,
className
}) {
return /* @__PURE__ */ jsx("div", { className: cn("relative overflow-hidden", className), children });
}
function RadgaHorizontalScroll({
slides,
stickyHeight = 6,
backgroundColor = "#b4aea7",
outroTitle = "Shaping timeless spaces with contemporary vision",
outroBackgroundColor = "#141414",
className = "",
style = {}
}) {
const stickyRef = useRef(null);
const slidesContainerRef = useRef(null);
const sliderRef = useRef(null);
const slideRefs = useRef([]);
const currentVisibleIndexRef = useRef(null);
const observerRef = useRef(null);
useEffect(() => {
if (!stickyRef.current || !slidesContainerRef.current || !sliderRef.current)
return;
if (typeof window !== "undefined") {
gsap.registerPlugin(Scr