@stianlarsen/react-light-beam 2.1.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +419 -129
- package/dist/index.d.mts +111 -1
- package/dist/index.d.ts +111 -1
- package/dist/index.js +326 -103
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +317 -94
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../node_modules/@gsap/react/src/index.js","../src/hooks/useDarkmode.tsx","../src/index.tsx"],"names":["useLayoutEffect","useEffect","gsap","useRef","useState","ScrollTrigger","jsx"],"mappings":"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","file":"index.js","sourcesContent":["/*!\n * @gsap/react 2.1.2\n * https://gsap.com\n *\n * Copyright 2008-2025, GreenSock. All rights reserved.\n * Subject to the terms at https://gsap.com/standard-license or for\n * Club GSAP members, the agreement issued with that membership.\n * @author: Jack Doyle, jack@greensock.com\n*/\n/* eslint-disable */\nimport { useEffect, useLayoutEffect, useRef } from \"react\";\nimport gsap from \"gsap\";\n\nlet useIsomorphicLayoutEffect = typeof document !== \"undefined\" ? useLayoutEffect : useEffect,\n isConfig = value => value && !Array.isArray(value) && typeof(value) === \"object\",\n emptyArray = [],\n defaultConfig = {},\n _gsap = gsap; // accommodates situations where different versions of GSAP may be loaded, so a user can gsap.registerPlugin(useGSAP);\n\nexport const useGSAP = (callback, dependencies = emptyArray) => {\n let config = defaultConfig;\n if (isConfig(callback)) {\n config = callback;\n callback = null;\n dependencies = \"dependencies\" in config ? config.dependencies : emptyArray;\n } else if (isConfig(dependencies)) {\n config = dependencies;\n dependencies = \"dependencies\" in config ? config.dependencies : emptyArray;\n }\n (callback && typeof callback !== \"function\") && console.warn(\"First parameter must be a function or config object\");\n const { scope, revertOnUpdate } = config,\n mounted = useRef(false),\n context = useRef(_gsap.context(() => { }, scope)),\n contextSafe = useRef((func) => context.current.add(null, func)),\n deferCleanup = dependencies && dependencies.length && !revertOnUpdate;\n deferCleanup && useIsomorphicLayoutEffect(() => {\n mounted.current = true;\n return () => context.current.revert();\n }, emptyArray);\n useIsomorphicLayoutEffect(() => {\n callback && context.current.add(callback, scope);\n if (!deferCleanup || !mounted.current) { // React renders bottom-up, thus there could be hooks with dependencies that run BEFORE the component mounts, thus cleanup wouldn't occur since a hook with an empty dependency Array would only run once the component mounts.\n return () => context.current.revert();\n }\n }, dependencies);\n return { context: context.current, contextSafe: contextSafe.current };\n};\nuseGSAP.register = core => { _gsap = core; };\nuseGSAP.headless = true; // doesn't require the window to be registered.\n","\"use client\";\nimport { useEffect, useState } from \"react\";\n\nexport const useIsDarkmode = () => {\n const [isDarkmode, setIsDarkmodeActive] = useState(false);\n\n useEffect(() => {\n const matchMedia = window.matchMedia(\"(prefers-color-scheme: dark)\");\n\n const handleChange = () => {\n setIsDarkmodeActive(matchMedia.matches);\n };\n\n // Set the initial value\n setIsDarkmodeActive(matchMedia.matches);\n\n // Listen for changes\n matchMedia.addEventListener(\"change\", handleChange);\n\n // Cleanup listener on unmount\n return () => {\n matchMedia.removeEventListener(\"change\", handleChange);\n };\n }, []);\n\n return { isDarkmode };\n};\n","\"use client\";\nimport gsap from \"gsap\";\nimport {ScrollTrigger} from \"gsap/ScrollTrigger\";\nimport {useGSAP} from \"@gsap/react\";\nimport React, {useEffect, useRef} from \"react\";\nimport {LightBeamProps} from \"../types/types\";\nimport {useIsDarkmode} from \"./hooks/useDarkmode\";\n\n// Register GSAP plugins\ngsap.registerPlugin(ScrollTrigger, useGSAP);\n\n// Default inline styles using CSS variables for easy customization\n// Users can override via className by setting CSS variables\nconst defaultStyles: React.CSSProperties = {\n height: \"var(--react-light-beam-height, 500px)\",\n width: \"var(--react-light-beam-width, 100vw)\",\n // CRITICAL: NO transition on GSAP-controlled properties (background, opacity, mask)\n // Transitions would fight with GSAP's instant updates, causing visual glitches\n // especially when scroll direction changes\n transition: \"none\",\n willChange: \"background, opacity\", // Specific properties for better performance\n userSelect: \"none\",\n pointerEvents: \"none\",\n contain: \"layout style paint\", // CSS containment for better performance\n WebkitTransition: \"none\",\n WebkitUserSelect: \"none\",\n MozUserSelect: \"none\",\n};\n\nexport const LightBeam = ({\n className,\n style,\n colorLightmode = \"rgba(0,0,0, 0.5)\",\n colorDarkmode = \"rgba(255, 255, 255, 0.5)\",\n maskLightByProgress = false,\n fullWidth = 1.0, // Default to full width\n invert = false,\n id = undefined,\n onLoaded = undefined,\n scrollElement,\n disableDefaultStyles = false,\n }: LightBeamProps) => {\n const elementRef = useRef<HTMLDivElement>(null);\n const {isDarkmode} = useIsDarkmode();\n const chosenColor = isDarkmode ? colorDarkmode : colorLightmode;\n\n // Use refs to track current values without triggering useGSAP re-runs\n const colorRef = useRef(chosenColor);\n const invertRef = useRef(invert);\n const maskByProgressRef = useRef(maskLightByProgress);\n\n // Update refs whenever values change\n useEffect(() => {\n colorRef.current = chosenColor;\n invertRef.current = invert;\n maskByProgressRef.current = maskLightByProgress;\n }, [chosenColor, colorLightmode, colorDarkmode, invert, maskLightByProgress]);\n\n // Call onLoaded callback when component mounts\n useEffect(() => {\n onLoaded && onLoaded();\n }, []);\n\n // GSAP ScrollTrigger implementation\n useGSAP(\n () => {\n const element = elementRef.current;\n if (!element || typeof window === \"undefined\") return;\n\n // Pre-calculate constants for performance\n const opacityMin = 0.839322;\n const opacityRange = 0.160678; // 1 - 0.839322\n\n // Helper function to interpolate background gradient\n // NOTE: Takes color as parameter to always use current value (not closure!)\n const interpolateBackground = (progress: number, color: string): string => {\n // At progress 0: gradients are wide (90% and 10% positions)\n // At progress 1: gradients converge (0% and 100% positions)\n const leftPos = 90 - progress * 90; // 90% → 0%\n const rightPos = 10 + progress * 90; // 10% → 100%\n const leftSize = 150 - progress * 50; // 150% → 100%\n\n return `conic-gradient(from 90deg at ${leftPos}% 0%, ${color}, transparent 180deg) 0% 0% / 50% ${leftSize}% no-repeat, conic-gradient(from 270deg at ${rightPos}% 0%, transparent 180deg, ${color}) 100% 0% / 50% 100% no-repeat`;\n };\n\n // Helper function to interpolate mask\n // NOTE: Takes color as parameter to always use current value (not closure!)\n const interpolateMask = (progress: number, color: string): string => {\n if (!maskByProgressRef.current) {\n return `linear-gradient(to bottom, ${color} 25%, transparent 95%)`;\n }\n const stopPoint = 50 + progress * 45; // 50% → 95%\n return `linear-gradient(to bottom, ${color} 0%, transparent ${stopPoint}%)`;\n };\n\n // EXACT MATCH TO FRAMER MOTION LOGIC:\n // fullWidth controls the MINIMUM beam width, not maximum!\n // fullWidth=1.0 → beam goes from 0% to 100% wide (full range)\n // fullWidth=0.5 → beam goes from 50% to 100% wide (narrower minimum)\n // fullWidth=0.2 → beam goes from 80% to 100% wide (very narrow minimum)\n const adjustedFullWidth = 1 - fullWidth;\n\n // Helper function to calculate progress (EXACTLY like Framer Motion)\n const calculateProgress = (rawProgress: number): number => {\n // ScrollTrigger rawProgress is 0-1 as element moves from start to end\n // We need to convert this to match Framer's rect.top / windowHeight logic\n // CRITICAL: GSAP progress (0→1) is INVERSE of Framer's normalizedPosition (1→0)\n\n // Apply fullWidth floor (minimum progress value)\n const normalizedPosition = Math.max(\n adjustedFullWidth, // Minimum (floor)\n Math.min(1, 1 - rawProgress) // Convert GSAP progress to Framer's normalized position\n );\n\n // Apply invert logic (EXACTLY like Framer Motion)\n // Use invertRef to get current value without closure issues\n return invertRef.current ? normalizedPosition : 1 - normalizedPosition;\n };\n\n // Determine scroll container\n const scroller = scrollElement\n ? (scrollElement as Element | Window)\n : undefined;\n\n // Create ScrollTrigger with FIXED range (like Framer Motion)\n const st = ScrollTrigger.create({\n trigger: element,\n start: \"top bottom\", // Element top hits viewport bottom\n end: \"top top\", // Element top hits viewport top\n scroller: scroller,\n scrub: true, // Instant scrubbing\n onUpdate: (self) => {\n // Calculate progress using Framer Motion logic\n const progress = calculateProgress(self.progress);\n\n // Update styles\n gsap.set(element, {\n background: interpolateBackground(progress, colorRef.current),\n opacity: opacityMin + opacityRange * progress,\n maskImage: interpolateMask(progress, colorRef.current),\n webkitMaskImage: interpolateMask(progress, colorRef.current),\n });\n },\n onRefresh: (self) => {\n // Set initial state when ScrollTrigger refreshes\n const progress = calculateProgress(self.progress);\n gsap.set(element, {\n background: interpolateBackground(progress, colorRef.current),\n opacity: opacityMin + opacityRange * progress,\n maskImage: interpolateMask(progress, colorRef.current),\n webkitMaskImage: interpolateMask(progress, colorRef.current),\n });\n },\n });\n\n // Set initial state immediately\n const initialProgress = calculateProgress(st.progress);\n gsap.set(element, {\n background: interpolateBackground(initialProgress, colorRef.current),\n opacity: opacityMin + opacityRange * initialProgress,\n maskImage: interpolateMask(initialProgress, colorRef.current),\n webkitMaskImage: interpolateMask(initialProgress, colorRef.current),\n });\n\n // Refresh ScrollTrigger after a brief delay to ensure layout is settled\n // This is especially important for Next.js SSR/hydration\n const refreshTimeout = setTimeout(() => {\n ScrollTrigger.refresh();\n }, 100);\n\n // Cleanup function to kill ScrollTrigger and clear timeout\n return () => {\n st.kill();\n clearTimeout(refreshTimeout);\n };\n },\n {\n // CRITICAL: Use refs for frequently changing values!\n // colorRef, invertRef, maskByProgressRef allow updates without recreating ScrollTrigger\n // This prevents visual glitches when these values change mid-scroll\n // Only include values that affect ScrollTrigger's position/range calculations\n dependencies: [\n fullWidth, // Affects trigger range\n scrollElement, // Affects which element to watch\n ],\n scope: elementRef,\n }\n );\n\n const combinedClassName = `react-light-beam ${className || \"\"}`.trim();\n\n // Prepare final styles (same logic as before, just without MotionValues)\n const finalStyles = disableDefaultStyles\n ? {\n // No default styles, only user styles\n willChange: \"background, opacity\",\n contain: \"layout style paint\",\n ...style, // User styles override\n }\n : {\n // Merge default styles with user styles\n ...defaultStyles,\n ...style, // User styles override everything\n };\n\n return (\n <div\n ref={elementRef}\n className={combinedClassName}\n style={finalStyles}\n {...(id ? {id} : {})}\n />\n );\n};\n\n\n\n// <div class=\"react-light-beam max-h:[500px] absolute top:[-100px] inset:[0]\" style=\"height: var(--react-light-beam-height, 500px); width: var(--react-light-beam-width, 100vw); will-change: background,\n// opacity; pointer-events: none; contain: layout style paint; transition: var(--react-light-beam-transition, all 0.25s ease); mask-image: linear-gradient(rgba(255, 255, 255, 0.8) 25%, transparent 95%);\n// opacity: 0.9434; background: conic-gradient(from 90deg at 31.723% 0%, rgba(255, 255, 255, 0.8), transparent 180deg) 0% 0% / 50% 117.624% no-repeat, conic-gradient(from 270deg at 68.277% 0%, transparent\n// 180deg, rgba(255, 255, 255, 0.8)) 100% 0% / 50% 100% no-repeat;\"></div>\n\n// <div class=\"react-light-beam max-h:[500px] absolute top:[-100px] inset:[0]\" style=\"height: var(--react-light-beam-height, 500px); width: var(--react-light-beam-width, 100vw); will-change: background,\n// opacity; pointer-events: none; contain: layout style paint; transition: var(--react-light-beam-transition, all 0.25s ease); mask-image: linear-gradient(rgba(0, 0, 0, 0.2) 25%, transparent 95%); opacity:\n// 0.941; background: conic-gradient(from 90deg at 33.0405% 0%, rgba(0, 0, 0, 0.2), transparent 180deg) 0% 0% / 50% 118.356% no-repeat, conic-gradient(from 270deg at 66.9595% 0%, transparent 180deg, rgba(0, 0,\n// 0, 0.2)) 100% 0% / 50% 100% no-repeat;\"></div>\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/hooks/useDarkmode.tsx","../src/effects/DustParticles.tsx","../src/effects/MistEffect.tsx","../src/effects/PulseEffect.tsx","../src/index.tsx"],"names":["useState","useEffect","useMemo","useGSAP","gsap","jsx","Fragment","ScrollTrigger","useRef","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\nimport {useEffect, useState} from \"react\";\n\nexport const useIsDarkmode = () => {\n const [isDarkmode, setIsDarkmodeActive] = useState(false);\n\n useEffect(() => {\n const matchMedia = window.matchMedia(\"(prefers-color-scheme: dark)\");\n\n const handleChange = () => {\n console.log(\"Darkmode match?\", matchMedia.matches)\n \n setIsDarkmodeActive(matchMedia.matches);\n };\n\n // Set the initial value\n setIsDarkmodeActive(matchMedia.matches);\n\n // Listen for changes\n matchMedia.addEventListener(\"change\", handleChange);\n handleChange()\n\n // Cleanup listener on unmount\n return () => {\n matchMedia.removeEventListener(\"change\", handleChange);\n };\n }, []);\n\n return {isDarkmode};\n};\n","\"use client\";\nimport gsap from \"gsap\";\nimport {useGSAP} from \"@gsap/react\";\nimport {useMemo} from \"react\";\nimport {DustParticlesConfig} from \"../../types/types\";\n\ninterface DustParticlesProps {\n config: DustParticlesConfig;\n beamColor: string;\n}\n\nexport const DustParticles = ({config, beamColor}: DustParticlesProps) => {\n const {\n enabled = false,\n count = 30,\n speed = 1,\n sizeRange = [1, 3],\n opacityRange = [0.2, 0.6],\n color,\n } = config;\n\n // Generate particle data once\n const particles = useMemo(() => {\n if (!enabled) return [];\n\n return Array.from({length: count}, (_, i) => {\n // Random position\n const x = Math.random() * 100; // 0-100%\n const y = Math.random() * 100; // 0-100%\n\n // Random size within range\n const size = sizeRange[0] + Math.random() * (sizeRange[1] - sizeRange[0]);\n\n // Random opacity within range\n const opacity = opacityRange[0] + Math.random() * (opacityRange[1] - opacityRange[0]);\n\n // Random animation duration (inversely proportional to speed)\n const duration = (3 + Math.random() * 4) / speed; // 3-7s / speed\n\n // Random animation delay for stagger effect\n const delay = Math.random() * duration;\n\n return {\n id: `dust-${i}`,\n x,\n y,\n size,\n opacity,\n duration,\n delay,\n };\n });\n }, [enabled, count, sizeRange, opacityRange, speed]);\n\n useGSAP(\n () => {\n if (!enabled || particles.length === 0) return;\n\n const timelines: gsap.core.Timeline[] = [];\n\n particles.forEach((particle) => {\n const element = document.getElementById(particle.id);\n if (!element) return;\n\n // Create floating animation for each particle\n const tl = gsap.timeline({\n repeat: -1,\n yoyo: true,\n delay: particle.delay,\n });\n\n // Animate vertical movement and slight horizontal drift\n tl.to(element, {\n y: `-=${20 + Math.random() * 30}`, // Float upward 20-50px\n x: `+=${Math.random() * 20 - 10}`, // Slight horizontal drift ±10px\n opacity: particle.opacity * 0.5, // Fade slightly\n duration: particle.duration,\n ease: \"sine.inOut\",\n });\n\n timelines.push(tl);\n });\n\n return () => {\n timelines.forEach((tl) => tl.kill());\n };\n },\n {\n dependencies: [particles, enabled],\n }\n );\n\n if (!enabled) return null;\n\n const particleColor = color || beamColor;\n\n return (\n <>\n {particles.map((particle) => (\n <div\n key={particle.id}\n id={particle.id}\n style={{\n position: \"absolute\",\n left: `${particle.x}%`,\n top: `${particle.y}%`,\n width: `${particle.size}px`,\n height: `${particle.size}px`,\n borderRadius: \"50%\",\n backgroundColor: particleColor,\n opacity: particle.opacity,\n pointerEvents: \"none\",\n willChange: \"transform, opacity\",\n }}\n />\n ))}\n </>\n );\n};\n","\"use client\";\nimport gsap from \"gsap\";\nimport {useGSAP} from \"@gsap/react\";\nimport {useMemo} from \"react\";\nimport {MistConfig} from \"../../types/types\";\n\ninterface MistEffectProps {\n config: MistConfig;\n beamColor: string;\n}\n\nexport const MistEffect = ({config, beamColor}: MistEffectProps) => {\n const {\n enabled = false,\n intensity = 0.3,\n speed = 1,\n layers = 2,\n } = config;\n\n // Generate mist layer data\n const mistLayers = useMemo(() => {\n if (!enabled) return [];\n\n return Array.from({length: layers}, (_, i) => {\n // Each layer has different characteristics for depth\n const layerOpacity = (intensity * 0.6) / (i + 1); // Deeper layers are more transparent\n const duration = (8 + i * 3) / speed; // Deeper layers move slower\n const delay = (i * 1.5) / speed; // Stagger start times\n const scale = 1 + i * 0.2; // Deeper layers are larger\n\n return {\n id: `mist-layer-${i}`,\n opacity: layerOpacity,\n duration,\n delay,\n scale,\n };\n });\n }, [enabled, intensity, speed, layers]);\n\n useGSAP(\n () => {\n if (!enabled || mistLayers.length === 0) return;\n\n const timelines: gsap.core.Timeline[] = [];\n\n mistLayers.forEach((layer) => {\n const element = document.getElementById(layer.id);\n if (!element) return;\n\n // Create drifting mist animation\n const tl = gsap.timeline({\n repeat: -1,\n yoyo: false,\n });\n\n // Horizontal drift animation\n tl.fromTo(\n element,\n {\n x: \"-100%\",\n opacity: 0,\n },\n {\n x: \"100%\",\n opacity: layer.opacity,\n duration: layer.duration,\n ease: \"none\",\n delay: layer.delay,\n }\n ).to(element, {\n opacity: 0,\n duration: layer.duration * 0.2,\n ease: \"power1.in\",\n });\n\n timelines.push(tl);\n });\n\n return () => {\n timelines.forEach((tl) => tl.kill());\n };\n },\n {\n dependencies: [mistLayers, enabled],\n }\n );\n\n if (!enabled) return null;\n\n // Parse beam color and create mist color with lower opacity\n const mistColor = beamColor.replace(/[\\d.]+\\)$/g, `${intensity})`);\n\n return (\n <>\n {mistLayers.map((layer) => (\n <div\n key={layer.id}\n id={layer.id}\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: \"100%\",\n background: `radial-gradient(ellipse 120% 80% at 50% 20%, ${mistColor}, transparent 70%)`,\n opacity: 0,\n pointerEvents: \"none\",\n willChange: \"transform, opacity\",\n transform: `scale(${layer.scale})`,\n filter: \"blur(40px)\",\n }}\n />\n ))}\n </>\n );\n};\n","\"use client\";\nimport gsap from \"gsap\";\nimport {useGSAP} from \"@gsap/react\";\nimport {PulseConfig} from \"../../types/types\";\n\ninterface PulseEffectProps {\n config: PulseConfig;\n containerRef: React.RefObject<HTMLDivElement | null>;\n}\n\nexport const PulseEffect = ({config, containerRef}: PulseEffectProps) => {\n const {\n enabled = false,\n duration = 2,\n intensity = 0.2,\n easing = \"sine.inOut\",\n } = config;\n\n useGSAP(\n () => {\n if (!enabled || !containerRef.current) return;\n\n const element = containerRef.current;\n\n // Create pulsing animation using GSAP timeline\n // Pulse multiplies the base opacity (from scroll) with pulse intensity\n const timeline = gsap.timeline({\n repeat: -1, // Infinite loop\n yoyo: true, // Reverse on each iteration\n });\n\n // Calculate pulse range based on intensity\n // Pulse multiplier varies from (1 - intensity) to (1 + intensity)\n const minMultiplier = Math.max(0, 1 - intensity);\n const maxMultiplier = Math.min(2, 1 + intensity);\n\n timeline.fromTo(\n element,\n {\n \"--pulse-multiplier\": 1,\n },\n {\n \"--pulse-multiplier\": maxMultiplier,\n duration: duration,\n ease: easing,\n }\n );\n\n // Apply combined opacity using calc()\n // opacity = base-opacity * pulse-multiplier\n const updateOpacity = () => {\n const baseOpacity = getComputedStyle(element).getPropertyValue('--base-opacity') || '1';\n const pulseMultiplier = getComputedStyle(element).getPropertyValue('--pulse-multiplier') || '1';\n element.style.opacity = `calc(${baseOpacity} * ${pulseMultiplier})`;\n };\n\n // Update opacity continuously during animation\n const ticker = gsap.ticker.add(updateOpacity);\n\n return () => {\n timeline.kill();\n gsap.ticker.remove(ticker);\n };\n },\n {\n dependencies: [enabled, duration, intensity, easing],\n scope: containerRef,\n }\n );\n\n // This component doesn't render anything - it just adds animations\n return null;\n};\n","\"use client\";\nimport gsap from \"gsap\";\nimport {ScrollTrigger} from \"gsap/ScrollTrigger\";\nimport {useGSAP} from \"@gsap/react\";\nimport React, {useEffect, useRef} from \"react\";\nimport {LightBeamProps} from \"../types/types\";\nimport {useIsDarkmode} from \"./hooks/useDarkmode\";\nimport {DustParticles} from \"./effects/DustParticles\";\nimport {MistEffect} from \"./effects/MistEffect\";\nimport {PulseEffect} from \"./effects/PulseEffect\";\n\n// Register GSAP plugins\ngsap.registerPlugin(ScrollTrigger, useGSAP);\n\n// Default inline styles using CSS variables for easy customization\n// Users can override via className by setting CSS variables\nconst defaultStyles: React.CSSProperties = {\n height: \"var(--react-light-beam-height, 500px)\",\n width: \"var(--react-light-beam-width, 100vw)\",\n // CRITICAL: NO transition on GSAP-controlled properties (background, opacity, mask)\n // Transitions would fight with GSAP's instant updates, causing visual glitches\n // especially when scroll direction changes\n transition: \"none\",\n willChange: \"background, opacity\", // Specific properties for better performance\n userSelect: \"none\",\n pointerEvents: \"none\",\n contain: \"layout style paint\", // CSS containment for better performance\n WebkitTransition: \"none\",\n WebkitUserSelect: \"none\",\n MozUserSelect: \"none\",\n};\n\nexport const LightBeam = ({\n className,\n style,\n colorLightmode = \"rgba(0,0,0, 0.5)\",\n colorDarkmode = \"rgba(255, 255, 255, 0.5)\",\n maskLightByProgress = false,\n fullWidth = 1.0, // Default to full width range\n invert = false,\n id = undefined,\n onLoaded = undefined,\n scrollElement,\n disableDefaultStyles = false,\n scrollStart = \"top bottom\",\n scrollEnd = \"top top\",\n dustParticles = {enabled: false},\n mist = {enabled: false},\n pulse = {enabled: false},\n }: LightBeamProps) => {\n const elementRef = useRef<HTMLDivElement>(null);\n const {isDarkmode} = useIsDarkmode();\n const chosenColor = isDarkmode ? colorDarkmode : colorLightmode;\n\n // Use refs to track current values without triggering useGSAP re-runs\n const colorRef = useRef(chosenColor);\n const invertRef = useRef(invert);\n const maskByProgressRef = useRef(maskLightByProgress);\n\n // Store ScrollTrigger instance for manual updates\n const scrollTriggerRef = useRef<ScrollTrigger | null>(null);\n\n // Update refs whenever values change\n useEffect(() => {\n colorRef.current = chosenColor;\n\n // PERFORMANCE: Update color CSS variable when color changes\n // This avoids recreating ScrollTrigger on color changes\n if (elementRef.current) {\n elementRef.current.style.setProperty('--beam-color', chosenColor);\n }\n }, [chosenColor, colorLightmode, colorDarkmode]);\n\n // Handle invert changes separately - needs immediate update\n useEffect(() => {\n const prevInvert = invertRef.current;\n invertRef.current = invert;\n\n // If invert changed and ScrollTrigger exists, immediately update\n if (prevInvert !== invert && scrollTriggerRef.current && elementRef.current) {\n const st = scrollTriggerRef.current;\n const element = elementRef.current;\n\n // Force immediate recalculation with new invert value\n // This prevents lag/jitter when toggling invert during scroll\n st.refresh();\n }\n }, [invert]);\n\n // Handle maskLightByProgress changes separately - needs structure rebuild\n useEffect(() => {\n const prevMaskByProgress = maskByProgressRef.current;\n maskByProgressRef.current = maskLightByProgress;\n\n // If maskLightByProgress changed and element exists, rebuild mask structure\n if (prevMaskByProgress !== maskLightByProgress && elementRef.current) {\n const element = elementRef.current;\n\n // Rebuild mask gradient structure with new setting\n if (maskLightByProgress) {\n // Initialize mask stop value\n element.style.setProperty('--beam-mask-stop', '50%');\n element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;\n element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;\n } else {\n // Static mask\n element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;\n element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;\n }\n\n // If ScrollTrigger exists, refresh to apply current state\n if (scrollTriggerRef.current) {\n scrollTriggerRef.current.refresh();\n }\n }\n }, [maskLightByProgress]);\n\n // Call onLoaded callback when component mounts\n useEffect(() => {\n onLoaded && onLoaded();\n }, []);\n\n // GSAP ScrollTrigger implementation\n useGSAP(\n () => {\n const element = elementRef.current;\n if (!element || typeof window === \"undefined\") return;\n\n // Pre-calculate constants for performance\n const opacityMin = 0.839322;\n const opacityRange = 0.160678; // 1 - 0.839322\n\n // Helper function to set color (only when color changes, not every frame)\n const updateColorVar = (color: string): void => {\n element.style.setProperty('--beam-color', color);\n };\n\n // Set initial gradient structure (once, not per frame!)\n const initGradientStructure = (color: string): void => {\n updateColorVar(color);\n const baseGradient = `conic-gradient(from 90deg at var(--beam-left-pos) 0%, var(--beam-color), transparent 180deg) 0% 0% / 50% var(--beam-left-size) no-repeat, conic-gradient(from 270deg at var(--beam-right-pos) 0%, transparent 180deg, var(--beam-color)) 100% 0% / 50% 100% no-repeat`;\n element.style.background = baseGradient;\n\n // Set mask structure\n if (maskByProgressRef.current) {\n element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;\n element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;\n } else {\n element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;\n element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;\n }\n };\n\n // FRAMER MOTION LOGIC (PRESERVED):\n // fullWidth controls the MAXIMUM beam width expansion during scroll\n // fullWidth=1.0 → beam expands from 0% to 100% of maximum width (full range)\n // fullWidth=0.8 → beam expands from 0% to 80% of maximum width\n // fullWidth=0.5 → beam expands from 0% to 50% of maximum width\n // fullWidth=0.2 → beam expands from 0% to 20% of maximum width\n const adjustedFullWidth = 1 - fullWidth;\n\n // Helper function to calculate progress (EXACTLY like Framer Motion)\n const calculateProgress = (rawProgress: number): number => {\n // ScrollTrigger rawProgress: 0 (element entering) → 1 (element at trigger end)\n // We convert to match Framer's rect.top / windowHeight logic\n // GSAP progress (0→1) is INVERSE of Framer's normalizedPosition (1→0)\n\n // Apply fullWidth ceiling (maximum progress value)\n // Math.max ensures progress never goes below (1 - fullWidth)\n // This limits how much the beam can expand\n const normalizedPosition = Math.max(\n adjustedFullWidth, // Floor value (1 - fullWidth)\n Math.min(1, 1 - rawProgress) // Inverted GSAP progress\n );\n\n // Apply invert logic (EXACTLY like Framer Motion)\n // Use invertRef to get current value without closure issues\n return invertRef.current ? normalizedPosition : 1 - normalizedPosition;\n };\n\n // Determine scroll container\n const scroller = scrollElement\n ? (scrollElement as Element | Window)\n : undefined;\n\n // PERFORMANCE OPTIMIZATION: Shared update function to avoid code duplication\n // Batches all style updates for better performance\n const applyProgressState = (progress: number): void => {\n // Pre-calculate all values\n const leftPos = 90 - progress * 90;\n const rightPos = 10 + progress * 90;\n const leftSize = 150 - progress * 50;\n const baseOpacity = opacityMin + opacityRange * progress;\n const maskStop = maskByProgressRef.current ? 50 + progress * 45 : undefined;\n\n // BATCH UPDATE: Use single gsap.set() call instead of multiple setProperty()\n // This is significantly faster as GSAP batches all updates in one frame\n const cssProps: any = {\n '--beam-left-pos': `${leftPos}%`,\n '--beam-right-pos': `${rightPos}%`,\n '--beam-left-size': `${leftSize}%`,\n '--base-opacity': baseOpacity,\n };\n\n // Conditionally add mask stop\n if (maskStop !== undefined) {\n cssProps['--beam-mask-stop'] = `${maskStop}%`;\n }\n\n // Conditionally add opacity (only if pulse not enabled)\n if (!pulse.enabled) {\n cssProps.opacity = baseOpacity;\n }\n\n // Single batch update via GSAP (more efficient than multiple setProperty calls)\n gsap.set(element, cssProps);\n };\n\n // Initialize gradient structure once\n initGradientStructure(colorRef.current);\n\n // Create ScrollTrigger with customizable start/end positions\n // SCRUB VALUE EXPLANATION:\n // - scrub: true (or 1) = 1 second catch-up delay (causes visible lag and stuttering)\n // - scrub: 0.3 = 300ms catch-up (fast and smooth, optimal for UI) ← CURRENT SETTING\n // - scrub: 0 = instant (may feel jittery on fast scrolls, no smoothing)\n // Research: Lower values (0.2-0.5) recommended for responsive scroll UX\n const st = ScrollTrigger.create({\n trigger: element,\n start: scrollStart, // When to start the animation\n end: scrollEnd, // When to end the animation\n scroller: scroller,\n scrub: 0.15, // Fast catch-up (300ms) for responsive scroll without jitter\n onUpdate: (self) => {\n // Calculate progress using Framer Motion logic\n const progress = calculateProgress(self.progress);\n\n // Apply all updates in single batch\n applyProgressState(progress);\n },\n onRefresh: (self) => {\n // CRITICAL: ScrollTrigger.refresh() is called on window resize, orientation change,\n // or when content changes. We need to recalculate and reapply styles to ensure\n // the beam renders correctly after layout changes.\n const progress = calculateProgress(self.progress);\n\n // Apply all updates in single batch\n applyProgressState(progress);\n },\n });\n\n // Store ScrollTrigger instance for manual updates (invert/maskLightByProgress changes)\n scrollTriggerRef.current = st;\n\n // Set initial state immediately using batched update\n const initialProgress = calculateProgress(st.progress);\n applyProgressState(initialProgress);\n\n // Refresh ScrollTrigger after a brief delay to ensure layout is settled\n // This is especially important for Next.js SSR/hydration\n const refreshTimeout = setTimeout(() => {\n ScrollTrigger.refresh();\n }, 100);\n\n // Cleanup function to kill ScrollTrigger and clear timeout\n return () => {\n st.kill();\n clearTimeout(refreshTimeout);\n };\n },\n {\n // CRITICAL: Use refs for frequently changing values!\n // colorRef, invertRef, maskByProgressRef allow updates without recreating ScrollTrigger\n // This prevents visual glitches when these values change mid-scroll\n // Only include values that affect ScrollTrigger's position/range calculations\n dependencies: [\n fullWidth, // Affects progress range calculation\n scrollElement, // Affects which element to watch\n scrollStart, // Affects when animation starts\n scrollEnd, // Affects when animation ends\n ],\n scope: elementRef,\n }\n );\n\n const combinedClassName = `react-light-beam ${className || \"\"}`.trim();\n\n // Prepare final styles\n const finalStyles = disableDefaultStyles\n ? {\n // No default styles, only user styles\n willChange: \"background, opacity\",\n contain: \"layout style paint\",\n ...style, // User styles override\n }\n : {\n // Merge default styles with user styles\n ...defaultStyles,\n ...style, // User styles override everything\n };\n\n return (\n <div\n ref={elementRef}\n className={combinedClassName}\n style={finalStyles}\n {...(id ? {id} : {})}\n >\n {/* Atmospheric Effects */}\n {dustParticles.enabled && (\n <DustParticles config={dustParticles} beamColor={chosenColor}/>\n )}\n {mist.enabled && (\n <MistEffect config={mist} beamColor={chosenColor}/>\n )}\n {pulse.enabled && (\n <PulseEffect config={pulse} containerRef={elementRef}/>\n )}\n </div>\n );\n};\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,59 +1,243 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import
|
|
2
|
+
import gsap3 from 'gsap';
|
|
3
3
|
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { useGSAP } from '@gsap/react';
|
|
5
|
+
import { useRef, useEffect, useState, useMemo } from 'react';
|
|
6
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
7
|
|
|
7
|
-
var useIsomorphicLayoutEffect = typeof document !== "undefined" ? useLayoutEffect : useEffect;
|
|
8
|
-
var isConfig = (value) => value && !Array.isArray(value) && typeof value === "object";
|
|
9
|
-
var emptyArray = [];
|
|
10
|
-
var defaultConfig = {};
|
|
11
|
-
var _gsap = gsap2;
|
|
12
|
-
var useGSAP = (callback, dependencies = emptyArray) => {
|
|
13
|
-
let config = defaultConfig;
|
|
14
|
-
if (isConfig(callback)) {
|
|
15
|
-
config = callback;
|
|
16
|
-
callback = null;
|
|
17
|
-
dependencies = "dependencies" in config ? config.dependencies : emptyArray;
|
|
18
|
-
} else if (isConfig(dependencies)) {
|
|
19
|
-
config = dependencies;
|
|
20
|
-
dependencies = "dependencies" in config ? config.dependencies : emptyArray;
|
|
21
|
-
}
|
|
22
|
-
callback && typeof callback !== "function" && console.warn("First parameter must be a function or config object");
|
|
23
|
-
const { scope, revertOnUpdate } = config, mounted = useRef(false), context = useRef(_gsap.context(() => {
|
|
24
|
-
}, scope)), contextSafe = useRef((func) => context.current.add(null, func)), deferCleanup = dependencies && dependencies.length && !revertOnUpdate;
|
|
25
|
-
deferCleanup && useIsomorphicLayoutEffect(() => {
|
|
26
|
-
mounted.current = true;
|
|
27
|
-
return () => context.current.revert();
|
|
28
|
-
}, emptyArray);
|
|
29
|
-
useIsomorphicLayoutEffect(() => {
|
|
30
|
-
callback && context.current.add(callback, scope);
|
|
31
|
-
if (!deferCleanup || !mounted.current) {
|
|
32
|
-
return () => context.current.revert();
|
|
33
|
-
}
|
|
34
|
-
}, dependencies);
|
|
35
|
-
return { context: context.current, contextSafe: contextSafe.current };
|
|
36
|
-
};
|
|
37
|
-
useGSAP.register = (core) => {
|
|
38
|
-
_gsap = core;
|
|
39
|
-
};
|
|
40
|
-
useGSAP.headless = true;
|
|
41
8
|
var useIsDarkmode = () => {
|
|
42
9
|
const [isDarkmode, setIsDarkmodeActive] = useState(false);
|
|
43
10
|
useEffect(() => {
|
|
44
11
|
const matchMedia = window.matchMedia("(prefers-color-scheme: dark)");
|
|
45
12
|
const handleChange = () => {
|
|
13
|
+
console.log("Darkmode match?", matchMedia.matches);
|
|
46
14
|
setIsDarkmodeActive(matchMedia.matches);
|
|
47
15
|
};
|
|
48
16
|
setIsDarkmodeActive(matchMedia.matches);
|
|
49
17
|
matchMedia.addEventListener("change", handleChange);
|
|
18
|
+
handleChange();
|
|
50
19
|
return () => {
|
|
51
20
|
matchMedia.removeEventListener("change", handleChange);
|
|
52
21
|
};
|
|
53
22
|
}, []);
|
|
54
23
|
return { isDarkmode };
|
|
55
24
|
};
|
|
56
|
-
|
|
25
|
+
var DustParticles = ({ config, beamColor }) => {
|
|
26
|
+
const {
|
|
27
|
+
enabled = false,
|
|
28
|
+
count = 30,
|
|
29
|
+
speed = 1,
|
|
30
|
+
sizeRange = [1, 3],
|
|
31
|
+
opacityRange = [0.2, 0.6],
|
|
32
|
+
color
|
|
33
|
+
} = config;
|
|
34
|
+
const particles = useMemo(() => {
|
|
35
|
+
if (!enabled) return [];
|
|
36
|
+
return Array.from({ length: count }, (_, i) => {
|
|
37
|
+
const x = Math.random() * 100;
|
|
38
|
+
const y = Math.random() * 100;
|
|
39
|
+
const size = sizeRange[0] + Math.random() * (sizeRange[1] - sizeRange[0]);
|
|
40
|
+
const opacity = opacityRange[0] + Math.random() * (opacityRange[1] - opacityRange[0]);
|
|
41
|
+
const duration = (3 + Math.random() * 4) / speed;
|
|
42
|
+
const delay = Math.random() * duration;
|
|
43
|
+
return {
|
|
44
|
+
id: `dust-${i}`,
|
|
45
|
+
x,
|
|
46
|
+
y,
|
|
47
|
+
size,
|
|
48
|
+
opacity,
|
|
49
|
+
duration,
|
|
50
|
+
delay
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}, [enabled, count, sizeRange, opacityRange, speed]);
|
|
54
|
+
useGSAP(
|
|
55
|
+
() => {
|
|
56
|
+
if (!enabled || particles.length === 0) return;
|
|
57
|
+
const timelines = [];
|
|
58
|
+
particles.forEach((particle) => {
|
|
59
|
+
const element = document.getElementById(particle.id);
|
|
60
|
+
if (!element) return;
|
|
61
|
+
const tl = gsap3.timeline({
|
|
62
|
+
repeat: -1,
|
|
63
|
+
yoyo: true,
|
|
64
|
+
delay: particle.delay
|
|
65
|
+
});
|
|
66
|
+
tl.to(element, {
|
|
67
|
+
y: `-=${20 + Math.random() * 30}`,
|
|
68
|
+
// Float upward 20-50px
|
|
69
|
+
x: `+=${Math.random() * 20 - 10}`,
|
|
70
|
+
// Slight horizontal drift ±10px
|
|
71
|
+
opacity: particle.opacity * 0.5,
|
|
72
|
+
// Fade slightly
|
|
73
|
+
duration: particle.duration,
|
|
74
|
+
ease: "sine.inOut"
|
|
75
|
+
});
|
|
76
|
+
timelines.push(tl);
|
|
77
|
+
});
|
|
78
|
+
return () => {
|
|
79
|
+
timelines.forEach((tl) => tl.kill());
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
dependencies: [particles, enabled]
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
if (!enabled) return null;
|
|
87
|
+
const particleColor = color || beamColor;
|
|
88
|
+
return /* @__PURE__ */ jsx(Fragment, { children: particles.map((particle) => /* @__PURE__ */ jsx(
|
|
89
|
+
"div",
|
|
90
|
+
{
|
|
91
|
+
id: particle.id,
|
|
92
|
+
style: {
|
|
93
|
+
position: "absolute",
|
|
94
|
+
left: `${particle.x}%`,
|
|
95
|
+
top: `${particle.y}%`,
|
|
96
|
+
width: `${particle.size}px`,
|
|
97
|
+
height: `${particle.size}px`,
|
|
98
|
+
borderRadius: "50%",
|
|
99
|
+
backgroundColor: particleColor,
|
|
100
|
+
opacity: particle.opacity,
|
|
101
|
+
pointerEvents: "none",
|
|
102
|
+
willChange: "transform, opacity"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
particle.id
|
|
106
|
+
)) });
|
|
107
|
+
};
|
|
108
|
+
var MistEffect = ({ config, beamColor }) => {
|
|
109
|
+
const {
|
|
110
|
+
enabled = false,
|
|
111
|
+
intensity = 0.3,
|
|
112
|
+
speed = 1,
|
|
113
|
+
layers = 2
|
|
114
|
+
} = config;
|
|
115
|
+
const mistLayers = useMemo(() => {
|
|
116
|
+
if (!enabled) return [];
|
|
117
|
+
return Array.from({ length: layers }, (_, i) => {
|
|
118
|
+
const layerOpacity = intensity * 0.6 / (i + 1);
|
|
119
|
+
const duration = (8 + i * 3) / speed;
|
|
120
|
+
const delay = i * 1.5 / speed;
|
|
121
|
+
const scale = 1 + i * 0.2;
|
|
122
|
+
return {
|
|
123
|
+
id: `mist-layer-${i}`,
|
|
124
|
+
opacity: layerOpacity,
|
|
125
|
+
duration,
|
|
126
|
+
delay,
|
|
127
|
+
scale
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
}, [enabled, intensity, speed, layers]);
|
|
131
|
+
useGSAP(
|
|
132
|
+
() => {
|
|
133
|
+
if (!enabled || mistLayers.length === 0) return;
|
|
134
|
+
const timelines = [];
|
|
135
|
+
mistLayers.forEach((layer) => {
|
|
136
|
+
const element = document.getElementById(layer.id);
|
|
137
|
+
if (!element) return;
|
|
138
|
+
const tl = gsap3.timeline({
|
|
139
|
+
repeat: -1,
|
|
140
|
+
yoyo: false
|
|
141
|
+
});
|
|
142
|
+
tl.fromTo(
|
|
143
|
+
element,
|
|
144
|
+
{
|
|
145
|
+
x: "-100%",
|
|
146
|
+
opacity: 0
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
x: "100%",
|
|
150
|
+
opacity: layer.opacity,
|
|
151
|
+
duration: layer.duration,
|
|
152
|
+
ease: "none",
|
|
153
|
+
delay: layer.delay
|
|
154
|
+
}
|
|
155
|
+
).to(element, {
|
|
156
|
+
opacity: 0,
|
|
157
|
+
duration: layer.duration * 0.2,
|
|
158
|
+
ease: "power1.in"
|
|
159
|
+
});
|
|
160
|
+
timelines.push(tl);
|
|
161
|
+
});
|
|
162
|
+
return () => {
|
|
163
|
+
timelines.forEach((tl) => tl.kill());
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
dependencies: [mistLayers, enabled]
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
if (!enabled) return null;
|
|
171
|
+
const mistColor = beamColor.replace(/[\d.]+\)$/g, `${intensity})`);
|
|
172
|
+
return /* @__PURE__ */ jsx(Fragment, { children: mistLayers.map((layer) => /* @__PURE__ */ jsx(
|
|
173
|
+
"div",
|
|
174
|
+
{
|
|
175
|
+
id: layer.id,
|
|
176
|
+
style: {
|
|
177
|
+
position: "absolute",
|
|
178
|
+
top: 0,
|
|
179
|
+
left: 0,
|
|
180
|
+
width: "100%",
|
|
181
|
+
height: "100%",
|
|
182
|
+
background: `radial-gradient(ellipse 120% 80% at 50% 20%, ${mistColor}, transparent 70%)`,
|
|
183
|
+
opacity: 0,
|
|
184
|
+
pointerEvents: "none",
|
|
185
|
+
willChange: "transform, opacity",
|
|
186
|
+
transform: `scale(${layer.scale})`,
|
|
187
|
+
filter: "blur(40px)"
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
layer.id
|
|
191
|
+
)) });
|
|
192
|
+
};
|
|
193
|
+
var PulseEffect = ({ config, containerRef }) => {
|
|
194
|
+
const {
|
|
195
|
+
enabled = false,
|
|
196
|
+
duration = 2,
|
|
197
|
+
intensity = 0.2,
|
|
198
|
+
easing = "sine.inOut"
|
|
199
|
+
} = config;
|
|
200
|
+
useGSAP(
|
|
201
|
+
() => {
|
|
202
|
+
if (!enabled || !containerRef.current) return;
|
|
203
|
+
const element = containerRef.current;
|
|
204
|
+
const timeline = gsap3.timeline({
|
|
205
|
+
repeat: -1,
|
|
206
|
+
// Infinite loop
|
|
207
|
+
yoyo: true
|
|
208
|
+
// Reverse on each iteration
|
|
209
|
+
});
|
|
210
|
+
const maxMultiplier = Math.min(2, 1 + intensity);
|
|
211
|
+
timeline.fromTo(
|
|
212
|
+
element,
|
|
213
|
+
{
|
|
214
|
+
"--pulse-multiplier": 1
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"--pulse-multiplier": maxMultiplier,
|
|
218
|
+
duration,
|
|
219
|
+
ease: easing
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
const updateOpacity = () => {
|
|
223
|
+
const baseOpacity = getComputedStyle(element).getPropertyValue("--base-opacity") || "1";
|
|
224
|
+
const pulseMultiplier = getComputedStyle(element).getPropertyValue("--pulse-multiplier") || "1";
|
|
225
|
+
element.style.opacity = `calc(${baseOpacity} * ${pulseMultiplier})`;
|
|
226
|
+
};
|
|
227
|
+
const ticker = gsap3.ticker.add(updateOpacity);
|
|
228
|
+
return () => {
|
|
229
|
+
timeline.kill();
|
|
230
|
+
gsap3.ticker.remove(ticker);
|
|
231
|
+
};
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
dependencies: [enabled, duration, intensity, easing],
|
|
235
|
+
scope: containerRef
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
return null;
|
|
239
|
+
};
|
|
240
|
+
gsap3.registerPlugin(ScrollTrigger, useGSAP);
|
|
57
241
|
var defaultStyles = {
|
|
58
242
|
height: "var(--react-light-beam-height, 500px)",
|
|
59
243
|
width: "var(--react-light-beam-width, 100vw)",
|
|
@@ -78,12 +262,17 @@ var LightBeam = ({
|
|
|
78
262
|
colorDarkmode = "rgba(255, 255, 255, 0.5)",
|
|
79
263
|
maskLightByProgress = false,
|
|
80
264
|
fullWidth = 1,
|
|
81
|
-
// Default to full width
|
|
265
|
+
// Default to full width range
|
|
82
266
|
invert = false,
|
|
83
267
|
id = void 0,
|
|
84
268
|
onLoaded = void 0,
|
|
85
269
|
scrollElement,
|
|
86
|
-
disableDefaultStyles = false
|
|
270
|
+
disableDefaultStyles = false,
|
|
271
|
+
scrollStart = "top bottom",
|
|
272
|
+
scrollEnd = "top top",
|
|
273
|
+
dustParticles = { enabled: false },
|
|
274
|
+
mist = { enabled: false },
|
|
275
|
+
pulse = { enabled: false }
|
|
87
276
|
}) => {
|
|
88
277
|
const elementRef = useRef(null);
|
|
89
278
|
const { isDarkmode } = useIsDarkmode();
|
|
@@ -91,11 +280,40 @@ var LightBeam = ({
|
|
|
91
280
|
const colorRef = useRef(chosenColor);
|
|
92
281
|
const invertRef = useRef(invert);
|
|
93
282
|
const maskByProgressRef = useRef(maskLightByProgress);
|
|
283
|
+
const scrollTriggerRef = useRef(null);
|
|
94
284
|
useEffect(() => {
|
|
95
285
|
colorRef.current = chosenColor;
|
|
286
|
+
if (elementRef.current) {
|
|
287
|
+
elementRef.current.style.setProperty("--beam-color", chosenColor);
|
|
288
|
+
}
|
|
289
|
+
}, [chosenColor, colorLightmode, colorDarkmode]);
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
const prevInvert = invertRef.current;
|
|
96
292
|
invertRef.current = invert;
|
|
293
|
+
if (prevInvert !== invert && scrollTriggerRef.current && elementRef.current) {
|
|
294
|
+
const st = scrollTriggerRef.current;
|
|
295
|
+
elementRef.current;
|
|
296
|
+
st.refresh();
|
|
297
|
+
}
|
|
298
|
+
}, [invert]);
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
const prevMaskByProgress = maskByProgressRef.current;
|
|
97
301
|
maskByProgressRef.current = maskLightByProgress;
|
|
98
|
-
|
|
302
|
+
if (prevMaskByProgress !== maskLightByProgress && elementRef.current) {
|
|
303
|
+
const element = elementRef.current;
|
|
304
|
+
if (maskLightByProgress) {
|
|
305
|
+
element.style.setProperty("--beam-mask-stop", "50%");
|
|
306
|
+
element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;
|
|
307
|
+
element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;
|
|
308
|
+
} else {
|
|
309
|
+
element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;
|
|
310
|
+
element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;
|
|
311
|
+
}
|
|
312
|
+
if (scrollTriggerRef.current) {
|
|
313
|
+
scrollTriggerRef.current.refresh();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}, [maskLightByProgress]);
|
|
99
317
|
useEffect(() => {
|
|
100
318
|
onLoaded && onLoaded();
|
|
101
319
|
}, []);
|
|
@@ -105,65 +323,74 @@ var LightBeam = ({
|
|
|
105
323
|
if (!element || typeof window === "undefined") return;
|
|
106
324
|
const opacityMin = 0.839322;
|
|
107
325
|
const opacityRange = 0.160678;
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
const rightPos = 10 + progress * 90;
|
|
111
|
-
const leftSize = 150 - progress * 50;
|
|
112
|
-
return `conic-gradient(from 90deg at ${leftPos}% 0%, ${color}, transparent 180deg) 0% 0% / 50% ${leftSize}% no-repeat, conic-gradient(from 270deg at ${rightPos}% 0%, transparent 180deg, ${color}) 100% 0% / 50% 100% no-repeat`;
|
|
326
|
+
const updateColorVar = (color) => {
|
|
327
|
+
element.style.setProperty("--beam-color", color);
|
|
113
328
|
};
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
329
|
+
const initGradientStructure = (color) => {
|
|
330
|
+
updateColorVar(color);
|
|
331
|
+
const baseGradient = `conic-gradient(from 90deg at var(--beam-left-pos) 0%, var(--beam-color), transparent 180deg) 0% 0% / 50% var(--beam-left-size) no-repeat, conic-gradient(from 270deg at var(--beam-right-pos) 0%, transparent 180deg, var(--beam-color)) 100% 0% / 50% 100% no-repeat`;
|
|
332
|
+
element.style.background = baseGradient;
|
|
333
|
+
if (maskByProgressRef.current) {
|
|
334
|
+
element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;
|
|
335
|
+
element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;
|
|
336
|
+
} else {
|
|
337
|
+
element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;
|
|
338
|
+
element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;
|
|
117
339
|
}
|
|
118
|
-
const stopPoint = 50 + progress * 45;
|
|
119
|
-
return `linear-gradient(to bottom, ${color} 0%, transparent ${stopPoint}%)`;
|
|
120
340
|
};
|
|
121
341
|
const adjustedFullWidth = 1 - fullWidth;
|
|
122
342
|
const calculateProgress = (rawProgress) => {
|
|
123
343
|
const normalizedPosition = Math.max(
|
|
124
344
|
adjustedFullWidth,
|
|
125
|
-
//
|
|
345
|
+
// Floor value (1 - fullWidth)
|
|
126
346
|
Math.min(1, 1 - rawProgress)
|
|
127
|
-
//
|
|
347
|
+
// Inverted GSAP progress
|
|
128
348
|
);
|
|
129
349
|
return invertRef.current ? normalizedPosition : 1 - normalizedPosition;
|
|
130
350
|
};
|
|
131
351
|
const scroller = scrollElement ? scrollElement : void 0;
|
|
352
|
+
const applyProgressState = (progress) => {
|
|
353
|
+
const leftPos = 90 - progress * 90;
|
|
354
|
+
const rightPos = 10 + progress * 90;
|
|
355
|
+
const leftSize = 150 - progress * 50;
|
|
356
|
+
const baseOpacity = opacityMin + opacityRange * progress;
|
|
357
|
+
const maskStop = maskByProgressRef.current ? 50 + progress * 45 : void 0;
|
|
358
|
+
const cssProps = {
|
|
359
|
+
"--beam-left-pos": `${leftPos}%`,
|
|
360
|
+
"--beam-right-pos": `${rightPos}%`,
|
|
361
|
+
"--beam-left-size": `${leftSize}%`,
|
|
362
|
+
"--base-opacity": baseOpacity
|
|
363
|
+
};
|
|
364
|
+
if (maskStop !== void 0) {
|
|
365
|
+
cssProps["--beam-mask-stop"] = `${maskStop}%`;
|
|
366
|
+
}
|
|
367
|
+
if (!pulse.enabled) {
|
|
368
|
+
cssProps.opacity = baseOpacity;
|
|
369
|
+
}
|
|
370
|
+
gsap3.set(element, cssProps);
|
|
371
|
+
};
|
|
372
|
+
initGradientStructure(colorRef.current);
|
|
132
373
|
const st = ScrollTrigger.create({
|
|
133
374
|
trigger: element,
|
|
134
|
-
start:
|
|
135
|
-
//
|
|
136
|
-
end:
|
|
137
|
-
//
|
|
375
|
+
start: scrollStart,
|
|
376
|
+
// When to start the animation
|
|
377
|
+
end: scrollEnd,
|
|
378
|
+
// When to end the animation
|
|
138
379
|
scroller,
|
|
139
|
-
scrub:
|
|
140
|
-
//
|
|
380
|
+
scrub: 0.15,
|
|
381
|
+
// Fast catch-up (300ms) for responsive scroll without jitter
|
|
141
382
|
onUpdate: (self) => {
|
|
142
383
|
const progress = calculateProgress(self.progress);
|
|
143
|
-
|
|
144
|
-
background: interpolateBackground(progress, colorRef.current),
|
|
145
|
-
opacity: opacityMin + opacityRange * progress,
|
|
146
|
-
maskImage: interpolateMask(progress, colorRef.current),
|
|
147
|
-
webkitMaskImage: interpolateMask(progress, colorRef.current)
|
|
148
|
-
});
|
|
384
|
+
applyProgressState(progress);
|
|
149
385
|
},
|
|
150
386
|
onRefresh: (self) => {
|
|
151
387
|
const progress = calculateProgress(self.progress);
|
|
152
|
-
|
|
153
|
-
background: interpolateBackground(progress, colorRef.current),
|
|
154
|
-
opacity: opacityMin + opacityRange * progress,
|
|
155
|
-
maskImage: interpolateMask(progress, colorRef.current),
|
|
156
|
-
webkitMaskImage: interpolateMask(progress, colorRef.current)
|
|
157
|
-
});
|
|
388
|
+
applyProgressState(progress);
|
|
158
389
|
}
|
|
159
390
|
});
|
|
391
|
+
scrollTriggerRef.current = st;
|
|
160
392
|
const initialProgress = calculateProgress(st.progress);
|
|
161
|
-
|
|
162
|
-
background: interpolateBackground(initialProgress, colorRef.current),
|
|
163
|
-
opacity: opacityMin + opacityRange * initialProgress,
|
|
164
|
-
maskImage: interpolateMask(initialProgress, colorRef.current),
|
|
165
|
-
webkitMaskImage: interpolateMask(initialProgress, colorRef.current)
|
|
166
|
-
});
|
|
393
|
+
applyProgressState(initialProgress);
|
|
167
394
|
const refreshTimeout = setTimeout(() => {
|
|
168
395
|
ScrollTrigger.refresh();
|
|
169
396
|
}, 100);
|
|
@@ -179,9 +406,13 @@ var LightBeam = ({
|
|
|
179
406
|
// Only include values that affect ScrollTrigger's position/range calculations
|
|
180
407
|
dependencies: [
|
|
181
408
|
fullWidth,
|
|
182
|
-
// Affects
|
|
183
|
-
scrollElement
|
|
409
|
+
// Affects progress range calculation
|
|
410
|
+
scrollElement,
|
|
184
411
|
// Affects which element to watch
|
|
412
|
+
scrollStart,
|
|
413
|
+
// Affects when animation starts
|
|
414
|
+
scrollEnd
|
|
415
|
+
// Affects when animation ends
|
|
185
416
|
],
|
|
186
417
|
scope: elementRef
|
|
187
418
|
}
|
|
@@ -199,29 +430,21 @@ var LightBeam = ({
|
|
|
199
430
|
...style
|
|
200
431
|
// User styles override everything
|
|
201
432
|
};
|
|
202
|
-
return /* @__PURE__ */
|
|
433
|
+
return /* @__PURE__ */ jsxs(
|
|
203
434
|
"div",
|
|
204
435
|
{
|
|
205
436
|
ref: elementRef,
|
|
206
437
|
className: combinedClassName,
|
|
207
438
|
style: finalStyles,
|
|
208
|
-
...id ? { id } : {}
|
|
439
|
+
...id ? { id } : {},
|
|
440
|
+
children: [
|
|
441
|
+
dustParticles.enabled && /* @__PURE__ */ jsx(DustParticles, { config: dustParticles, beamColor: chosenColor }),
|
|
442
|
+
mist.enabled && /* @__PURE__ */ jsx(MistEffect, { config: mist, beamColor: chosenColor }),
|
|
443
|
+
pulse.enabled && /* @__PURE__ */ jsx(PulseEffect, { config: pulse, containerRef: elementRef })
|
|
444
|
+
]
|
|
209
445
|
}
|
|
210
446
|
);
|
|
211
447
|
};
|
|
212
|
-
/*! Bundled license information:
|
|
213
|
-
|
|
214
|
-
@gsap/react/src/index.js:
|
|
215
|
-
(*!
|
|
216
|
-
* @gsap/react 2.1.2
|
|
217
|
-
* https://gsap.com
|
|
218
|
-
*
|
|
219
|
-
* Copyright 2008-2025, GreenSock. All rights reserved.
|
|
220
|
-
* Subject to the terms at https://gsap.com/standard-license or for
|
|
221
|
-
* Club GSAP members, the agreement issued with that membership.
|
|
222
|
-
* @author: Jack Doyle, jack@greensock.com
|
|
223
|
-
*)
|
|
224
|
-
*/
|
|
225
448
|
|
|
226
449
|
export { LightBeam };
|
|
227
450
|
//# sourceMappingURL=index.mjs.map
|