@stianlarsen/react-light-beam 2.1.2 → 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/dist/index.mjs CHANGED
@@ -1,59 +1,243 @@
1
1
  "use client";
2
- import gsap from 'gsap';
2
+ import gsap3 from 'gsap';
3
3
  import { ScrollTrigger } from 'gsap/ScrollTrigger';
4
- import { useRef, useLayoutEffect, useEffect, useState } from 'react';
5
- import { jsx } from 'react/jsx-runtime';
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 = gsap;
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
- gsap.registerPlugin(ScrollTrigger, useGSAP);
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
- }, [chosenColor, colorLightmode, colorDarkmode, invert, maskLightByProgress]);
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,59 +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 interpolateBackground = (progress, color) => {
109
- const leftPos = 90 - progress * 90;
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 interpolateMask = (progress, color) => {
115
- if (!maskByProgressRef.current) {
116
- return `linear-gradient(to bottom, ${color} 25%, transparent 95%)`;
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
- // Minimum (floor)
345
+ // Floor value (1 - fullWidth)
126
346
  Math.min(1, 1 - rawProgress)
127
- // Convert GSAP progress to Framer's normalized position
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: "top bottom",
135
- // Element top hits viewport bottom
136
- end: "top top",
137
- // Element top hits viewport top
375
+ start: scrollStart,
376
+ // When to start the animation
377
+ end: scrollEnd,
378
+ // When to end the animation
138
379
  scroller,
139
- scrub: true,
140
- // Instant scrubbing
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
- element.style.background = interpolateBackground(progress, colorRef.current);
144
- element.style.opacity = String(opacityMin + opacityRange * progress);
145
- element.style.maskImage = interpolateMask(progress, colorRef.current);
146
- element.style.webkitMaskImage = interpolateMask(progress, colorRef.current);
384
+ applyProgressState(progress);
147
385
  },
148
386
  onRefresh: (self) => {
149
387
  const progress = calculateProgress(self.progress);
150
- element.style.background = interpolateBackground(progress, colorRef.current);
151
- element.style.opacity = String(opacityMin + opacityRange * progress);
152
- element.style.maskImage = interpolateMask(progress, colorRef.current);
153
- element.style.webkitMaskImage = interpolateMask(progress, colorRef.current);
388
+ applyProgressState(progress);
154
389
  }
155
390
  });
391
+ scrollTriggerRef.current = st;
156
392
  const initialProgress = calculateProgress(st.progress);
157
- element.style.background = interpolateBackground(initialProgress, colorRef.current);
158
- element.style.opacity = String(opacityMin + opacityRange * initialProgress);
159
- element.style.maskImage = interpolateMask(initialProgress, colorRef.current);
160
- element.style.webkitMaskImage = interpolateMask(initialProgress, colorRef.current);
393
+ applyProgressState(initialProgress);
161
394
  const refreshTimeout = setTimeout(() => {
162
395
  ScrollTrigger.refresh();
163
396
  }, 100);
@@ -173,9 +406,13 @@ var LightBeam = ({
173
406
  // Only include values that affect ScrollTrigger's position/range calculations
174
407
  dependencies: [
175
408
  fullWidth,
176
- // Affects trigger range
177
- scrollElement
409
+ // Affects progress range calculation
410
+ scrollElement,
178
411
  // Affects which element to watch
412
+ scrollStart,
413
+ // Affects when animation starts
414
+ scrollEnd
415
+ // Affects when animation ends
179
416
  ],
180
417
  scope: elementRef
181
418
  }
@@ -193,29 +430,21 @@ var LightBeam = ({
193
430
  ...style
194
431
  // User styles override everything
195
432
  };
196
- return /* @__PURE__ */ jsx(
433
+ return /* @__PURE__ */ jsxs(
197
434
  "div",
198
435
  {
199
436
  ref: elementRef,
200
437
  className: combinedClassName,
201
438
  style: finalStyles,
202
- ...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
+ ]
203
445
  }
204
446
  );
205
447
  };
206
- /*! Bundled license information:
207
-
208
- @gsap/react/src/index.js:
209
- (*!
210
- * @gsap/react 2.1.2
211
- * https://gsap.com
212
- *
213
- * Copyright 2008-2025, GreenSock. All rights reserved.
214
- * Subject to the terms at https://gsap.com/standard-license or for
215
- * Club GSAP members, the agreement issued with that membership.
216
- * @author: Jack Doyle, jack@greensock.com
217
- *)
218
- */
219
448
 
220
449
  export { LightBeam };
221
450
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../node_modules/@gsap/react/src/index.js","../src/hooks/useDarkmode.tsx","../src/index.tsx"],"names":["useEffect","gsap","useRef"],"mappings":"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","file":"index.mjs","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 directly (not via gsap.set to avoid CSS variable parsing issues)\n element.style.background = interpolateBackground(progress, colorRef.current);\n element.style.opacity = String(opacityMin + opacityRange * progress);\n element.style.maskImage = interpolateMask(progress, colorRef.current);\n element.style.webkitMaskImage = interpolateMask(progress, colorRef.current);\n },\n onRefresh: (self) => {\n // Set initial state when ScrollTrigger refreshes\n const progress = calculateProgress(self.progress);\n element.style.background = interpolateBackground(progress, colorRef.current);\n element.style.opacity = String(opacityMin + opacityRange * progress);\n element.style.maskImage = interpolateMask(progress, colorRef.current);\n element.style.webkitMaskImage = interpolateMask(progress, colorRef.current);\n },\n });\n\n // Set initial state immediately\n const initialProgress = calculateProgress(st.progress);\n element.style.background = interpolateBackground(initialProgress, colorRef.current);\n element.style.opacity = String(opacityMin + opacityRange * initialProgress);\n element.style.maskImage = interpolateMask(initialProgress, colorRef.current);\n element.style.webkitMaskImage = interpolateMask(initialProgress, colorRef.current);\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":["gsap","useMemo","useGSAP","jsx","Fragment","useEffect"],"mappings":"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","file":"index.mjs","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"]}