@stianlarsen/react-light-beam 3.1.0 → 3.1.2
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/LICENSE +0 -0
- package/README.md +76 -451
- package/dist/index.cjs +168 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +17 -0
- package/dist/index.d.ts +2 -123
- package/dist/index.js +100 -412
- package/dist/index.js.map +1 -1
- package/package.json +33 -31
- package/dist/index.d.mts +0 -138
- package/dist/index.mjs +0 -450
- package/dist/index.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,456 +1,144 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
'use strict';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var react$1 = require('react');
|
|
8
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
3
|
+
// src/index.tsx
|
|
4
|
+
import { motion, useMotionValue, useTransform } from "framer-motion";
|
|
5
|
+
import { useEffect as useEffect2, useRef } from "react";
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
// #style-inject:#style-inject
|
|
8
|
+
function styleInject(css, { insertAt } = {}) {
|
|
9
|
+
if (!css || typeof document === "undefined") return;
|
|
10
|
+
const head = document.head || document.getElementsByTagName("head")[0];
|
|
11
|
+
const style = document.createElement("style");
|
|
12
|
+
style.type = "text/css";
|
|
13
|
+
if (insertAt === "top") {
|
|
14
|
+
if (head.firstChild) {
|
|
15
|
+
head.insertBefore(style, head.firstChild);
|
|
16
|
+
} else {
|
|
17
|
+
head.appendChild(style);
|
|
18
|
+
}
|
|
19
|
+
} else {
|
|
20
|
+
head.appendChild(style);
|
|
21
|
+
}
|
|
22
|
+
if (style.styleSheet) {
|
|
23
|
+
style.styleSheet.cssText = css;
|
|
24
|
+
} else {
|
|
25
|
+
style.appendChild(document.createTextNode(css));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
11
28
|
|
|
12
|
-
|
|
29
|
+
// src/css/lightBeam.css
|
|
30
|
+
styleInject(".react__light__beam {\n height: 500px;\n width: 100vw;\n transition: all 0.25s ease;\n will-change: all;\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n pointer-events: none;\n -webkit-transition: all 0.25s ease;\n}\n");
|
|
13
31
|
|
|
32
|
+
// src/hooks/useDarkmode.tsx
|
|
33
|
+
import { useEffect, useState } from "react";
|
|
14
34
|
var useIsDarkmode = () => {
|
|
15
|
-
const [isDarkmode, setIsDarkmodeActive] =
|
|
16
|
-
|
|
35
|
+
const [isDarkmode, setIsDarkmodeActive] = useState(false);
|
|
36
|
+
useEffect(() => {
|
|
17
37
|
const matchMedia = window.matchMedia("(prefers-color-scheme: dark)");
|
|
18
38
|
const handleChange = () => {
|
|
19
|
-
console.log("Darkmode match?", matchMedia.matches);
|
|
20
39
|
setIsDarkmodeActive(matchMedia.matches);
|
|
21
40
|
};
|
|
22
41
|
setIsDarkmodeActive(matchMedia.matches);
|
|
23
42
|
matchMedia.addEventListener("change", handleChange);
|
|
24
|
-
handleChange();
|
|
25
43
|
return () => {
|
|
26
44
|
matchMedia.removeEventListener("change", handleChange);
|
|
27
45
|
};
|
|
28
46
|
}, []);
|
|
29
47
|
return { isDarkmode };
|
|
30
48
|
};
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
count = 30,
|
|
35
|
-
speed = 1,
|
|
36
|
-
sizeRange = [1, 3],
|
|
37
|
-
opacityRange = [0.2, 0.6],
|
|
38
|
-
color
|
|
39
|
-
} = config;
|
|
40
|
-
const particles = react$1.useMemo(() => {
|
|
41
|
-
if (!enabled) return [];
|
|
42
|
-
return Array.from({ length: count }, (_, i) => {
|
|
43
|
-
const x = Math.random() * 100;
|
|
44
|
-
const y = Math.random() * 100;
|
|
45
|
-
const size = sizeRange[0] + Math.random() * (sizeRange[1] - sizeRange[0]);
|
|
46
|
-
const opacity = opacityRange[0] + Math.random() * (opacityRange[1] - opacityRange[0]);
|
|
47
|
-
const duration = (3 + Math.random() * 4) / speed;
|
|
48
|
-
const delay = Math.random() * duration;
|
|
49
|
-
return {
|
|
50
|
-
id: `dust-${i}`,
|
|
51
|
-
x,
|
|
52
|
-
y,
|
|
53
|
-
size,
|
|
54
|
-
opacity,
|
|
55
|
-
duration,
|
|
56
|
-
delay
|
|
57
|
-
};
|
|
58
|
-
});
|
|
59
|
-
}, [enabled, count, sizeRange, opacityRange, speed]);
|
|
60
|
-
react.useGSAP(
|
|
61
|
-
() => {
|
|
62
|
-
if (!enabled || particles.length === 0) return;
|
|
63
|
-
const timelines = [];
|
|
64
|
-
particles.forEach((particle) => {
|
|
65
|
-
const element = document.getElementById(particle.id);
|
|
66
|
-
if (!element) return;
|
|
67
|
-
const tl = gsap3__default.default.timeline({
|
|
68
|
-
repeat: -1,
|
|
69
|
-
yoyo: true,
|
|
70
|
-
delay: particle.delay
|
|
71
|
-
});
|
|
72
|
-
tl.to(element, {
|
|
73
|
-
y: `-=${20 + Math.random() * 30}`,
|
|
74
|
-
// Float upward 20-50px
|
|
75
|
-
x: `+=${Math.random() * 20 - 10}`,
|
|
76
|
-
// Slight horizontal drift ±10px
|
|
77
|
-
opacity: particle.opacity * 0.5,
|
|
78
|
-
// Fade slightly
|
|
79
|
-
duration: particle.duration,
|
|
80
|
-
ease: "sine.inOut"
|
|
81
|
-
});
|
|
82
|
-
timelines.push(tl);
|
|
83
|
-
});
|
|
84
|
-
return () => {
|
|
85
|
-
timelines.forEach((tl) => tl.kill());
|
|
86
|
-
};
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
dependencies: [particles, enabled]
|
|
90
|
-
}
|
|
91
|
-
);
|
|
92
|
-
if (!enabled) return null;
|
|
93
|
-
const particleColor = color || beamColor;
|
|
94
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: particles.map((particle) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
95
|
-
"div",
|
|
96
|
-
{
|
|
97
|
-
id: particle.id,
|
|
98
|
-
style: {
|
|
99
|
-
position: "absolute",
|
|
100
|
-
left: `${particle.x}%`,
|
|
101
|
-
top: `${particle.y}%`,
|
|
102
|
-
width: `${particle.size}px`,
|
|
103
|
-
height: `${particle.size}px`,
|
|
104
|
-
borderRadius: "50%",
|
|
105
|
-
backgroundColor: particleColor,
|
|
106
|
-
opacity: particle.opacity,
|
|
107
|
-
pointerEvents: "none",
|
|
108
|
-
willChange: "transform, opacity"
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
particle.id
|
|
112
|
-
)) });
|
|
113
|
-
};
|
|
114
|
-
var MistEffect = ({ config, beamColor }) => {
|
|
115
|
-
const {
|
|
116
|
-
enabled = false,
|
|
117
|
-
intensity = 0.3,
|
|
118
|
-
speed = 1,
|
|
119
|
-
layers = 2
|
|
120
|
-
} = config;
|
|
121
|
-
const mistLayers = react$1.useMemo(() => {
|
|
122
|
-
if (!enabled) return [];
|
|
123
|
-
return Array.from({ length: layers }, (_, i) => {
|
|
124
|
-
const layerOpacity = intensity * 0.6 / (i + 1);
|
|
125
|
-
const duration = (8 + i * 3) / speed;
|
|
126
|
-
const delay = i * 1.5 / speed;
|
|
127
|
-
const scale = 1 + i * 0.2;
|
|
128
|
-
return {
|
|
129
|
-
id: `mist-layer-${i}`,
|
|
130
|
-
opacity: layerOpacity,
|
|
131
|
-
duration,
|
|
132
|
-
delay,
|
|
133
|
-
scale
|
|
134
|
-
};
|
|
135
|
-
});
|
|
136
|
-
}, [enabled, intensity, speed, layers]);
|
|
137
|
-
react.useGSAP(
|
|
138
|
-
() => {
|
|
139
|
-
if (!enabled || mistLayers.length === 0) return;
|
|
140
|
-
const timelines = [];
|
|
141
|
-
mistLayers.forEach((layer) => {
|
|
142
|
-
const element = document.getElementById(layer.id);
|
|
143
|
-
if (!element) return;
|
|
144
|
-
const tl = gsap3__default.default.timeline({
|
|
145
|
-
repeat: -1,
|
|
146
|
-
yoyo: false
|
|
147
|
-
});
|
|
148
|
-
tl.fromTo(
|
|
149
|
-
element,
|
|
150
|
-
{
|
|
151
|
-
x: "-100%",
|
|
152
|
-
opacity: 0
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
x: "100%",
|
|
156
|
-
opacity: layer.opacity,
|
|
157
|
-
duration: layer.duration,
|
|
158
|
-
ease: "none",
|
|
159
|
-
delay: layer.delay
|
|
160
|
-
}
|
|
161
|
-
).to(element, {
|
|
162
|
-
opacity: 0,
|
|
163
|
-
duration: layer.duration * 0.2,
|
|
164
|
-
ease: "power1.in"
|
|
165
|
-
});
|
|
166
|
-
timelines.push(tl);
|
|
167
|
-
});
|
|
168
|
-
return () => {
|
|
169
|
-
timelines.forEach((tl) => tl.kill());
|
|
170
|
-
};
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
dependencies: [mistLayers, enabled]
|
|
174
|
-
}
|
|
175
|
-
);
|
|
176
|
-
if (!enabled) return null;
|
|
177
|
-
const mistColor = beamColor.replace(/[\d.]+\)$/g, `${intensity})`);
|
|
178
|
-
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: mistLayers.map((layer) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
179
|
-
"div",
|
|
180
|
-
{
|
|
181
|
-
id: layer.id,
|
|
182
|
-
style: {
|
|
183
|
-
position: "absolute",
|
|
184
|
-
top: 0,
|
|
185
|
-
left: 0,
|
|
186
|
-
width: "100%",
|
|
187
|
-
height: "100%",
|
|
188
|
-
background: `radial-gradient(ellipse 120% 80% at 50% 20%, ${mistColor}, transparent 70%)`,
|
|
189
|
-
opacity: 0,
|
|
190
|
-
pointerEvents: "none",
|
|
191
|
-
willChange: "transform, opacity",
|
|
192
|
-
transform: `scale(${layer.scale})`,
|
|
193
|
-
filter: "blur(40px)"
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
layer.id
|
|
197
|
-
)) });
|
|
198
|
-
};
|
|
199
|
-
var PulseEffect = ({ config, containerRef }) => {
|
|
200
|
-
const {
|
|
201
|
-
enabled = false,
|
|
202
|
-
duration = 2,
|
|
203
|
-
intensity = 0.2,
|
|
204
|
-
easing = "sine.inOut"
|
|
205
|
-
} = config;
|
|
206
|
-
react.useGSAP(
|
|
207
|
-
() => {
|
|
208
|
-
if (!enabled || !containerRef.current) return;
|
|
209
|
-
const element = containerRef.current;
|
|
210
|
-
const timeline = gsap3__default.default.timeline({
|
|
211
|
-
repeat: -1,
|
|
212
|
-
// Infinite loop
|
|
213
|
-
yoyo: true
|
|
214
|
-
// Reverse on each iteration
|
|
215
|
-
});
|
|
216
|
-
const maxMultiplier = Math.min(2, 1 + intensity);
|
|
217
|
-
timeline.fromTo(
|
|
218
|
-
element,
|
|
219
|
-
{
|
|
220
|
-
"--pulse-multiplier": 1
|
|
221
|
-
},
|
|
222
|
-
{
|
|
223
|
-
"--pulse-multiplier": maxMultiplier,
|
|
224
|
-
duration,
|
|
225
|
-
ease: easing
|
|
226
|
-
}
|
|
227
|
-
);
|
|
228
|
-
const updateOpacity = () => {
|
|
229
|
-
const baseOpacity = getComputedStyle(element).getPropertyValue("--base-opacity") || "1";
|
|
230
|
-
const pulseMultiplier = getComputedStyle(element).getPropertyValue("--pulse-multiplier") || "1";
|
|
231
|
-
element.style.opacity = `calc(${baseOpacity} * ${pulseMultiplier})`;
|
|
232
|
-
};
|
|
233
|
-
const ticker = gsap3__default.default.ticker.add(updateOpacity);
|
|
234
|
-
return () => {
|
|
235
|
-
timeline.kill();
|
|
236
|
-
gsap3__default.default.ticker.remove(ticker);
|
|
237
|
-
};
|
|
238
|
-
},
|
|
239
|
-
{
|
|
240
|
-
dependencies: [enabled, duration, intensity, easing],
|
|
241
|
-
scope: containerRef
|
|
242
|
-
}
|
|
243
|
-
);
|
|
244
|
-
return null;
|
|
245
|
-
};
|
|
246
|
-
gsap3__default.default.registerPlugin(ScrollTrigger.ScrollTrigger, react.useGSAP);
|
|
247
|
-
var defaultStyles = {
|
|
248
|
-
height: "var(--react-light-beam-height, 500px)",
|
|
249
|
-
width: "var(--react-light-beam-width, 100vw)",
|
|
250
|
-
// CRITICAL: NO transition on GSAP-controlled properties (background, opacity, mask)
|
|
251
|
-
// Transitions would fight with GSAP's instant updates, causing visual glitches
|
|
252
|
-
// especially when scroll direction changes
|
|
253
|
-
transition: "none",
|
|
254
|
-
willChange: "background, opacity",
|
|
255
|
-
// Specific properties for better performance
|
|
256
|
-
userSelect: "none",
|
|
257
|
-
pointerEvents: "none",
|
|
258
|
-
contain: "layout style paint",
|
|
259
|
-
// CSS containment for better performance
|
|
260
|
-
WebkitTransition: "none",
|
|
261
|
-
WebkitUserSelect: "none",
|
|
262
|
-
MozUserSelect: "none"
|
|
263
|
-
};
|
|
49
|
+
|
|
50
|
+
// src/index.tsx
|
|
51
|
+
import { jsx } from "react/jsx-runtime";
|
|
264
52
|
var LightBeam = ({
|
|
265
53
|
className,
|
|
266
|
-
style,
|
|
267
54
|
colorLightmode = "rgba(0,0,0, 0.5)",
|
|
268
55
|
colorDarkmode = "rgba(255, 255, 255, 0.5)",
|
|
269
56
|
maskLightByProgress = false,
|
|
270
57
|
fullWidth = 1,
|
|
271
|
-
// Default to full width
|
|
58
|
+
// Default to full width
|
|
272
59
|
invert = false,
|
|
273
60
|
id = void 0,
|
|
274
61
|
onLoaded = void 0,
|
|
275
|
-
scrollElement
|
|
276
|
-
|
|
277
|
-
scrollStart = "top bottom",
|
|
278
|
-
scrollEnd = "top top",
|
|
279
|
-
dustParticles = { enabled: false },
|
|
280
|
-
mist = { enabled: false },
|
|
281
|
-
pulse = { enabled: false }
|
|
62
|
+
scrollElement
|
|
63
|
+
// Add this line
|
|
282
64
|
}) => {
|
|
283
|
-
const elementRef =
|
|
65
|
+
const elementRef = useRef(null);
|
|
66
|
+
const inViewProgress = useMotionValue(0);
|
|
67
|
+
const opacity = useMotionValue(0.839322);
|
|
284
68
|
const { isDarkmode } = useIsDarkmode();
|
|
285
69
|
const chosenColor = isDarkmode ? colorDarkmode : colorLightmode;
|
|
286
|
-
|
|
287
|
-
const invertRef = react$1.useRef(invert);
|
|
288
|
-
const maskByProgressRef = react$1.useRef(maskLightByProgress);
|
|
289
|
-
const scrollTriggerRef = react$1.useRef(null);
|
|
290
|
-
react$1.useEffect(() => {
|
|
291
|
-
colorRef.current = chosenColor;
|
|
292
|
-
if (elementRef.current) {
|
|
293
|
-
elementRef.current.style.setProperty("--beam-color", chosenColor);
|
|
294
|
-
}
|
|
295
|
-
}, [chosenColor, colorLightmode, colorDarkmode]);
|
|
296
|
-
react$1.useEffect(() => {
|
|
297
|
-
const prevInvert = invertRef.current;
|
|
298
|
-
invertRef.current = invert;
|
|
299
|
-
if (prevInvert !== invert && scrollTriggerRef.current && elementRef.current) {
|
|
300
|
-
const st = scrollTriggerRef.current;
|
|
301
|
-
elementRef.current;
|
|
302
|
-
st.refresh();
|
|
303
|
-
}
|
|
304
|
-
}, [invert]);
|
|
305
|
-
react$1.useEffect(() => {
|
|
306
|
-
const prevMaskByProgress = maskByProgressRef.current;
|
|
307
|
-
maskByProgressRef.current = maskLightByProgress;
|
|
308
|
-
if (prevMaskByProgress !== maskLightByProgress && elementRef.current) {
|
|
309
|
-
const element = elementRef.current;
|
|
310
|
-
if (maskLightByProgress) {
|
|
311
|
-
element.style.setProperty("--beam-mask-stop", "50%");
|
|
312
|
-
element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;
|
|
313
|
-
element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;
|
|
314
|
-
} else {
|
|
315
|
-
element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;
|
|
316
|
-
element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;
|
|
317
|
-
}
|
|
318
|
-
if (scrollTriggerRef.current) {
|
|
319
|
-
scrollTriggerRef.current.refresh();
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}, [maskLightByProgress]);
|
|
323
|
-
react$1.useEffect(() => {
|
|
70
|
+
useEffect2(() => {
|
|
324
71
|
onLoaded && onLoaded();
|
|
325
72
|
}, []);
|
|
326
|
-
|
|
327
|
-
()
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
updateColorVar(color);
|
|
337
|
-
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`;
|
|
338
|
-
element.style.background = baseGradient;
|
|
339
|
-
if (maskByProgressRef.current) {
|
|
340
|
-
element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;
|
|
341
|
-
element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 0%, transparent var(--beam-mask-stop))`;
|
|
342
|
-
} else {
|
|
343
|
-
element.style.maskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;
|
|
344
|
-
element.style.webkitMaskImage = `linear-gradient(to bottom, var(--beam-color) 25%, transparent 95%)`;
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
const adjustedFullWidth = 1 - fullWidth;
|
|
348
|
-
const calculateProgress = (rawProgress) => {
|
|
349
|
-
const normalizedPosition = Math.max(
|
|
350
|
-
adjustedFullWidth,
|
|
351
|
-
// Floor value (1 - fullWidth)
|
|
352
|
-
Math.min(1, 1 - rawProgress)
|
|
353
|
-
// Inverted GSAP progress
|
|
354
|
-
);
|
|
355
|
-
return invertRef.current ? normalizedPosition : 1 - normalizedPosition;
|
|
356
|
-
};
|
|
357
|
-
const scroller = scrollElement ? scrollElement : void 0;
|
|
358
|
-
const applyProgressState = (progress) => {
|
|
359
|
-
const leftPos = 90 - progress * 90;
|
|
360
|
-
const rightPos = 10 + progress * 90;
|
|
361
|
-
const leftSize = 150 - progress * 50;
|
|
362
|
-
const baseOpacity = opacityMin + opacityRange * progress;
|
|
363
|
-
const maskStop = maskByProgressRef.current ? 50 + progress * 45 : void 0;
|
|
364
|
-
const cssProps = {
|
|
365
|
-
"--beam-left-pos": `${leftPos}%`,
|
|
366
|
-
"--beam-right-pos": `${rightPos}%`,
|
|
367
|
-
"--beam-left-size": `${leftSize}%`,
|
|
368
|
-
"--base-opacity": baseOpacity
|
|
369
|
-
};
|
|
370
|
-
if (maskStop !== void 0) {
|
|
371
|
-
cssProps["--beam-mask-stop"] = `${maskStop}%`;
|
|
372
|
-
}
|
|
373
|
-
if (!pulse.enabled) {
|
|
374
|
-
cssProps.opacity = baseOpacity;
|
|
73
|
+
useEffect2(() => {
|
|
74
|
+
if (typeof window !== "undefined") {
|
|
75
|
+
const handleScroll = () => {
|
|
76
|
+
if (elementRef.current) {
|
|
77
|
+
const rect = elementRef.current.getBoundingClientRect();
|
|
78
|
+
const windowHeight = window.innerHeight;
|
|
79
|
+
const adjustedFullWidth = 1 - fullWidth;
|
|
80
|
+
const progress = invert ? 0 + Math.max(adjustedFullWidth, Math.min(1, rect.top / windowHeight)) : 1 - Math.max(adjustedFullWidth, Math.min(1, rect.top / windowHeight));
|
|
81
|
+
inViewProgress.set(progress);
|
|
82
|
+
opacity.set(0.839322 + (1 - 0.839322) * progress);
|
|
375
83
|
}
|
|
376
|
-
gsap3__default.default.set(element, cssProps);
|
|
377
84
|
};
|
|
378
|
-
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
end: scrollEnd,
|
|
384
|
-
// When to end the animation
|
|
385
|
-
scroller,
|
|
386
|
-
scrub: 0.15,
|
|
387
|
-
// Fast catch-up (300ms) for responsive scroll without jitter
|
|
388
|
-
onUpdate: (self) => {
|
|
389
|
-
const progress = calculateProgress(self.progress);
|
|
390
|
-
applyProgressState(progress);
|
|
391
|
-
},
|
|
392
|
-
onRefresh: (self) => {
|
|
393
|
-
const progress = calculateProgress(self.progress);
|
|
394
|
-
applyProgressState(progress);
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
scrollTriggerRef.current = st;
|
|
398
|
-
const initialProgress = calculateProgress(st.progress);
|
|
399
|
-
applyProgressState(initialProgress);
|
|
400
|
-
const refreshTimeout = setTimeout(() => {
|
|
401
|
-
ScrollTrigger.ScrollTrigger.refresh();
|
|
402
|
-
}, 100);
|
|
85
|
+
const handleScrollThrottled = throttle(handleScroll);
|
|
86
|
+
const target = scrollElement || window;
|
|
87
|
+
target.addEventListener("scroll", handleScrollThrottled);
|
|
88
|
+
window.addEventListener("resize", handleScrollThrottled);
|
|
89
|
+
handleScroll();
|
|
403
90
|
return () => {
|
|
404
|
-
|
|
405
|
-
|
|
91
|
+
target.removeEventListener("scroll", handleScrollThrottled);
|
|
92
|
+
window.removeEventListener("resize", handleScrollThrottled);
|
|
406
93
|
};
|
|
407
|
-
},
|
|
408
|
-
{
|
|
409
|
-
// CRITICAL: Use refs for frequently changing values!
|
|
410
|
-
// colorRef, invertRef, maskByProgressRef allow updates without recreating ScrollTrigger
|
|
411
|
-
// This prevents visual glitches when these values change mid-scroll
|
|
412
|
-
// Only include values that affect ScrollTrigger's position/range calculations
|
|
413
|
-
dependencies: [
|
|
414
|
-
fullWidth,
|
|
415
|
-
// Affects progress range calculation
|
|
416
|
-
scrollElement,
|
|
417
|
-
// Affects which element to watch
|
|
418
|
-
scrollStart,
|
|
419
|
-
// Affects when animation starts
|
|
420
|
-
scrollEnd
|
|
421
|
-
// Affects when animation ends
|
|
422
|
-
],
|
|
423
|
-
scope: elementRef
|
|
424
94
|
}
|
|
95
|
+
}, [inViewProgress, opacity, scrollElement]);
|
|
96
|
+
const backgroundPosition = useTransform(
|
|
97
|
+
inViewProgress,
|
|
98
|
+
[0, 1],
|
|
99
|
+
[
|
|
100
|
+
`conic-gradient(from 90deg at 90% 0%, ${chosenColor}, transparent 180deg) 0% 0% / 50% 150% no-repeat, conic-gradient(from 270deg at 10% 0%, transparent 180deg, ${chosenColor}) 100% 0% / 50% 100% no-repeat`,
|
|
101
|
+
`conic-gradient(from 90deg at 0% 0%, ${chosenColor}, transparent 180deg) 0% 0% / 50% 100% no-repeat, conic-gradient(from 270deg at 100% 0%, transparent 180deg, ${chosenColor}) 100% 0% / 50% 100% no-repeat`
|
|
102
|
+
]
|
|
425
103
|
);
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
// User styles override everything
|
|
438
|
-
};
|
|
439
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
440
|
-
"div",
|
|
104
|
+
const maskImageOpacity = useTransform(
|
|
105
|
+
inViewProgress,
|
|
106
|
+
[0, 1],
|
|
107
|
+
[
|
|
108
|
+
`linear-gradient(to bottom, ${chosenColor} 0%, transparent 50%)`,
|
|
109
|
+
`linear-gradient(to bottom, ${chosenColor} 0%, transparent 95%)`
|
|
110
|
+
]
|
|
111
|
+
);
|
|
112
|
+
const maskImage = maskLightByProgress ? maskImageOpacity : `linear-gradient(to bottom, ${chosenColor} 25%, transparent 95%)`;
|
|
113
|
+
return /* @__PURE__ */ jsx(
|
|
114
|
+
motion.div,
|
|
441
115
|
{
|
|
116
|
+
style: {
|
|
117
|
+
background: backgroundPosition,
|
|
118
|
+
opacity,
|
|
119
|
+
maskImage,
|
|
120
|
+
WebkitMaskImage: maskImage,
|
|
121
|
+
willChange: "background, opacity"
|
|
122
|
+
},
|
|
442
123
|
ref: elementRef,
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
...id ? { id } : {},
|
|
446
|
-
children: [
|
|
447
|
-
dustParticles.enabled && /* @__PURE__ */ jsxRuntime.jsx(DustParticles, { config: dustParticles, beamColor: chosenColor }),
|
|
448
|
-
mist.enabled && /* @__PURE__ */ jsxRuntime.jsx(MistEffect, { config: mist, beamColor: chosenColor }),
|
|
449
|
-
pulse.enabled && /* @__PURE__ */ jsxRuntime.jsx(PulseEffect, { config: pulse, containerRef: elementRef })
|
|
450
|
-
]
|
|
124
|
+
id,
|
|
125
|
+
className: `lightBeam ${className} react__light__beam`
|
|
451
126
|
}
|
|
452
127
|
);
|
|
453
128
|
};
|
|
454
|
-
|
|
455
|
-
|
|
129
|
+
var throttle = (func) => {
|
|
130
|
+
let ticking = false;
|
|
131
|
+
return function(...args) {
|
|
132
|
+
if (!ticking) {
|
|
133
|
+
requestAnimationFrame(() => {
|
|
134
|
+
func.apply(this, args);
|
|
135
|
+
ticking = false;
|
|
136
|
+
});
|
|
137
|
+
ticking = true;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
export {
|
|
142
|
+
LightBeam
|
|
143
|
+
};
|
|
456
144
|
//# sourceMappingURL=index.js.map
|