@once-ui-system/core 1.5.1 → 1.5.3
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 +54 -0
- package/dist/README.md +54 -0
- package/dist/components/Avatar.d.ts +2 -0
- package/dist/components/Avatar.d.ts.map +1 -1
- package/dist/components/Avatar.js +2 -2
- package/dist/components/Avatar.js.map +1 -1
- package/dist/components/CelebrationFx.d.ts +16 -0
- package/dist/components/CelebrationFx.d.ts.map +1 -0
- package/dist/components/CelebrationFx.js +310 -0
- package/dist/components/CelebrationFx.js.map +1 -0
- package/dist/components/CountdownFx.d.ts +11 -0
- package/dist/components/CountdownFx.d.ts.map +1 -0
- package/dist/components/CountdownFx.js +49 -0
- package/dist/components/CountdownFx.js.map +1 -0
- package/dist/components/Heading.d.ts.map +1 -1
- package/dist/components/MatrixFx.d.ts +11 -1
- package/dist/components/MatrixFx.d.ts.map +1 -1
- package/dist/components/MatrixFx.js +480 -118
- package/dist/components/MatrixFx.js.map +1 -1
- package/dist/components/Media.d.ts.map +1 -1
- package/dist/components/Media.js +10 -5
- package/dist/components/Media.js.map +1 -1
- package/dist/components/Pulse.d.ts.map +1 -1
- package/dist/components/Pulse.js +1 -0
- package/dist/components/Pulse.js.map +1 -1
- package/dist/components/SegmentedControl.d.ts +1 -0
- package/dist/components/SegmentedControl.d.ts.map +1 -1
- package/dist/components/SegmentedControl.js +3 -3
- package/dist/components/SegmentedControl.js.map +1 -1
- package/dist/components/ShineFx.d.ts +1 -0
- package/dist/components/ShineFx.d.ts.map +1 -1
- package/dist/components/ShineFx.js +3 -2
- package/dist/components/ShineFx.js.map +1 -1
- package/dist/components/ShineFx.module.scss +18 -1
- package/dist/components/StylePanel.js +1 -1
- package/dist/components/StylePanel.js.map +1 -1
- package/dist/components/WeatherFx.d.ts +17 -0
- package/dist/components/WeatherFx.d.ts.map +1 -0
- package/dist/components/WeatherFx.js +769 -0
- package/dist/components/WeatherFx.js.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +3 -0
- package/dist/components/index.js.map +1 -1
- package/dist/data/emoji-data.json +10 -8
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +3 -0
- package/dist/icons.js.map +1 -1
- package/dist/modules/code/CodeBlock.js +1 -1
- package/dist/modules/code/CodeBlock.js.map +1 -1
- package/dist/modules/data/BarChart.d.ts +2 -0
- package/dist/modules/data/BarChart.d.ts.map +1 -1
- package/dist/modules/data/BarChart.js +43 -41
- package/dist/modules/data/BarChart.js.map +1 -1
- package/dist/modules/data/Gauge.module.css +27 -0
- package/dist/modules/data/LineBarChart.d.ts +2 -0
- package/dist/modules/data/LineBarChart.d.ts.map +1 -1
- package/dist/modules/data/LineBarChart.js +35 -33
- package/dist/modules/data/LineBarChart.js.map +1 -1
- package/dist/modules/data/LineChart.d.ts +2 -0
- package/dist/modules/data/LineChart.d.ts.map +1 -1
- package/dist/modules/data/LineChart.js +43 -41
- package/dist/modules/data/LineChart.js.map +1 -1
- package/dist/modules/data/LinearGauge.d.ts +18 -0
- package/dist/modules/data/LinearGauge.d.ts.map +1 -0
- package/dist/modules/data/LinearGauge.js +78 -0
- package/dist/modules/data/LinearGauge.js.map +1 -0
- package/dist/modules/data/RadialGauge.d.ts +25 -0
- package/dist/modules/data/RadialGauge.d.ts.map +1 -0
- package/dist/modules/data/RadialGauge.js +94 -0
- package/dist/modules/data/RadialGauge.js.map +1 -0
- package/dist/modules/data/index.d.ts +2 -0
- package/dist/modules/data/index.d.ts.map +1 -1
- package/dist/modules/data/index.js +2 -0
- package/dist/modules/data/index.js.map +1 -1
- package/dist/modules/index.d.ts +3 -16
- package/dist/modules/index.d.ts.map +1 -1
- package/dist/modules/index.js +2 -15
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/navigation/MegaMenu.d.ts.map +1 -1
- package/dist/modules/navigation/MegaMenu.js +11 -11
- package/dist/modules/navigation/MegaMenu.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React, { useEffect, useRef } from "react";
|
|
4
4
|
import { Flex } from ".";
|
|
5
|
-
const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"], size = 3, spacing = 3, revealFrom = "center", trigger = "instant", flicker = false, children, ...rest }, forwardedRef) => {
|
|
5
|
+
const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"], size = 3, spacing = 3, revealFrom = "center", trigger = "instant", active = false, flicker = false, bulge, children, ...rest }, forwardedRef) => {
|
|
6
6
|
const containerRef = useRef(null);
|
|
7
7
|
const canvasRef = useRef(null);
|
|
8
8
|
const animationRef = useRef(undefined);
|
|
@@ -12,6 +12,8 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
12
12
|
const hideStartProgressRef = useRef(0);
|
|
13
13
|
const isHoveredRef = useRef(false);
|
|
14
14
|
const mountAnimationCompleteRef = useRef(false);
|
|
15
|
+
const bulgeStartTimeRef = useRef(Date.now());
|
|
16
|
+
const dotsRef = useRef([]);
|
|
15
17
|
useEffect(() => {
|
|
16
18
|
if (forwardedRef) {
|
|
17
19
|
if ("current" in forwardedRef) {
|
|
@@ -51,59 +53,126 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
51
53
|
const computedColor = getComputedStyle(container).getPropertyValue(`--${color}`);
|
|
52
54
|
return computedColor || color;
|
|
53
55
|
});
|
|
54
|
-
// Create dot grid
|
|
56
|
+
// Create dot grid with padding to prevent edge gaps during displacement
|
|
55
57
|
const totalSize = size + spacing;
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
58
|
+
const maxDisplacement = (bulge?.intensity ?? 10) * 2; // Account for max possible displacement
|
|
59
|
+
const paddedWidth = canvasWidth + maxDisplacement * 2;
|
|
60
|
+
const paddedHeight = canvasHeight + maxDisplacement * 2;
|
|
61
|
+
const cols = Math.ceil(paddedWidth / totalSize);
|
|
62
|
+
const rows = Math.ceil(paddedHeight / totalSize);
|
|
63
|
+
// Only create new dots if grid doesn't exist or dimensions/size changed
|
|
64
|
+
let dots = dotsRef.current;
|
|
65
|
+
let maxDistance = 0;
|
|
66
|
+
if (dots.length === 0 || dots[0]?.gridSize !== totalSize || dots[0]?.canvasW !== canvasWidth || dots[0]?.canvasH !== canvasHeight) {
|
|
67
|
+
// Create new dot grid
|
|
68
|
+
dots = [];
|
|
69
|
+
for (let row = 0; row < rows; row++) {
|
|
70
|
+
for (let col = 0; col < cols; col++) {
|
|
71
|
+
const x = col * totalSize + size / 2 - maxDisplacement;
|
|
72
|
+
const y = row * totalSize + size / 2 - maxDisplacement;
|
|
73
|
+
// Calculate distance from reveal origin
|
|
74
|
+
let distanceFromOrigin = 0;
|
|
75
|
+
const centerX = canvasWidth / 2;
|
|
76
|
+
const centerY = canvasHeight / 2;
|
|
77
|
+
switch (revealFrom) {
|
|
78
|
+
case "center":
|
|
79
|
+
const dx = x - centerX;
|
|
80
|
+
const dy = y - centerY;
|
|
81
|
+
distanceFromOrigin = Math.sqrt(dx * dx + dy * dy);
|
|
82
|
+
break;
|
|
83
|
+
case "top":
|
|
84
|
+
distanceFromOrigin = y;
|
|
85
|
+
break;
|
|
86
|
+
case "bottom":
|
|
87
|
+
distanceFromOrigin = canvasHeight - y;
|
|
88
|
+
break;
|
|
89
|
+
case "left":
|
|
90
|
+
distanceFromOrigin = x;
|
|
91
|
+
break;
|
|
92
|
+
case "right":
|
|
93
|
+
distanceFromOrigin = canvasWidth - x;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
dots.push({
|
|
97
|
+
x,
|
|
98
|
+
y,
|
|
99
|
+
gridX: col,
|
|
100
|
+
gridY: row,
|
|
101
|
+
color: parsedColors[Math.floor(Math.random() * parsedColors.length)],
|
|
102
|
+
baseOpacity: 0.3 + Math.random() * 0.7,
|
|
103
|
+
distanceFromOrigin,
|
|
104
|
+
randomOffset: Math.random() * 0.3,
|
|
105
|
+
flickerPhase: Math.random() * Math.PI * 2,
|
|
106
|
+
flickerSpeed: 0.8 + Math.random() * 0.4,
|
|
107
|
+
gridSize: totalSize,
|
|
108
|
+
canvasW: canvasWidth,
|
|
109
|
+
canvasH: canvasHeight,
|
|
110
|
+
});
|
|
85
111
|
}
|
|
86
|
-
dots.push({
|
|
87
|
-
x,
|
|
88
|
-
y,
|
|
89
|
-
gridX: col,
|
|
90
|
-
gridY: row,
|
|
91
|
-
color: parsedColors[Math.floor(Math.random() * parsedColors.length)],
|
|
92
|
-
baseOpacity: 0.3 + Math.random() * 0.7,
|
|
93
|
-
distanceFromOrigin,
|
|
94
|
-
randomOffset: Math.random() * 0.3,
|
|
95
|
-
flickerPhase: Math.random() * Math.PI * 2, // Random starting point
|
|
96
|
-
flickerSpeed: 0.8 + Math.random() * 0.4, // Random speed between 0.8 and 1.2
|
|
97
|
-
});
|
|
98
112
|
}
|
|
113
|
+
// Find max distance for normalization
|
|
114
|
+
maxDistance = Math.max(...dots.map((d) => d.distanceFromOrigin));
|
|
115
|
+
dotsRef.current = dots;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
// Update colors on existing dots
|
|
119
|
+
dots.forEach((dot) => {
|
|
120
|
+
dot.color = parsedColors[Math.floor(Math.random() * parsedColors.length)];
|
|
121
|
+
});
|
|
122
|
+
maxDistance = Math.max(...dots.map((d) => d.distanceFromOrigin));
|
|
99
123
|
}
|
|
100
|
-
// Find max distance for normalization
|
|
101
|
-
const maxDistance = Math.max(...dots.map((d) => d.distanceFromOrigin));
|
|
102
124
|
// Animation loop
|
|
103
125
|
const startTime = Date.now();
|
|
126
|
+
// Bulge configuration
|
|
127
|
+
const bulgeEnabled = !!bulge;
|
|
128
|
+
const bulgeType = bulge?.type ?? "ripple"; // Default: ripple
|
|
129
|
+
const bulgeDuration = bulge?.duration ?? 3; // Default: 3 seconds per wave
|
|
130
|
+
const bulgeIntensity = bulge?.intensity ?? 10; // Default: 10px displacement
|
|
131
|
+
const bulgeRepeat = bulge?.repeat ?? true; // Default: true
|
|
132
|
+
const bulgeDelay = bulge?.delay ?? 0; // Default: 0ms
|
|
133
|
+
// Calculate center point for circular wave
|
|
134
|
+
const centerX = canvasWidth / 2;
|
|
135
|
+
const centerY = canvasHeight / 2;
|
|
136
|
+
const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);
|
|
104
137
|
const animate = () => {
|
|
105
138
|
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
106
139
|
const time = (Date.now() - startTime) / 1000; // Time in seconds
|
|
140
|
+
// Calculate circular wave radius (travels from center to edge)
|
|
141
|
+
let waveProgress = 0;
|
|
142
|
+
let showBulge = false;
|
|
143
|
+
let bulgeFadeOut = 1; // Multiplier for fading out bulge effect (1 = full effect, 0 = no effect)
|
|
144
|
+
if (bulgeEnabled) {
|
|
145
|
+
const bulgeElapsed = (Date.now() - bulgeStartTimeRef.current) / 1000;
|
|
146
|
+
const delaySeconds = bulgeDelay / 1000;
|
|
147
|
+
const totalCycleDuration = bulgeDuration + delaySeconds;
|
|
148
|
+
const adjustedTime = bulgeElapsed - delaySeconds;
|
|
149
|
+
const fadeStartPercent = 0.6; // Start fading at 60% of wave duration
|
|
150
|
+
if (adjustedTime >= 0) {
|
|
151
|
+
showBulge = true;
|
|
152
|
+
if (bulgeRepeat) {
|
|
153
|
+
// Continuous repeating wave
|
|
154
|
+
const cycleTime = adjustedTime % totalCycleDuration;
|
|
155
|
+
waveProgress = cycleTime < bulgeDuration ? cycleTime / bulgeDuration : 0;
|
|
156
|
+
showBulge = cycleTime < bulgeDuration; // Only show during active wave, not delay
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
// Single wave with opacity fade during last 40%
|
|
160
|
+
if (adjustedTime <= bulgeDuration) {
|
|
161
|
+
waveProgress = adjustedTime / bulgeDuration;
|
|
162
|
+
// Start fading opacity at 60% of duration
|
|
163
|
+
if (waveProgress >= fadeStartPercent) {
|
|
164
|
+
const fadeProgress = (waveProgress - fadeStartPercent) / (1 - fadeStartPercent);
|
|
165
|
+
bulgeFadeOut = 1 - fadeProgress; // Fade from 1 to 0
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
waveProgress = 0;
|
|
170
|
+
showBulge = false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const waveRadius = waveProgress * maxRadius * 1.5; // Travel beyond edges for smooth cycle
|
|
107
176
|
// For instant trigger, show all dots immediately at full opacity
|
|
108
177
|
if (trigger === "instant") {
|
|
109
178
|
dots.forEach((dot) => {
|
|
@@ -115,8 +184,74 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
115
184
|
const flickerMultiplier = 0.6 + (flickerValue * 0.4); // Oscillate between 0.6 and 1.0
|
|
116
185
|
opacity *= flickerMultiplier;
|
|
117
186
|
}
|
|
118
|
-
|
|
119
|
-
|
|
187
|
+
// Calculate bulge displacement
|
|
188
|
+
let offsetX = 0;
|
|
189
|
+
let offsetY = 0;
|
|
190
|
+
let sizeMultiplier = 1;
|
|
191
|
+
let bulgeOpacity = 1;
|
|
192
|
+
if (bulgeEnabled && showBulge) {
|
|
193
|
+
if (bulgeType === "ripple") {
|
|
194
|
+
// Ripple: circular wave from center
|
|
195
|
+
const dx = dot.x - centerX;
|
|
196
|
+
const dy = dot.y - centerY;
|
|
197
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
198
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
199
|
+
const waveWidth = maxRadius * 0.15;
|
|
200
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
201
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
202
|
+
const angle = Math.atan2(dy, dx);
|
|
203
|
+
const displacementAmount = waveFactor * bulgeIntensity * bulgeFadeOut;
|
|
204
|
+
offsetX = Math.cos(angle) * displacementAmount;
|
|
205
|
+
offsetY = Math.sin(angle) * displacementAmount;
|
|
206
|
+
sizeMultiplier = 1 + waveFactor * 0.8 * bulgeFadeOut;
|
|
207
|
+
const waveOpacity = 0.3 + waveFactor * 0.7;
|
|
208
|
+
bulgeOpacity = 1 + (waveOpacity - 1) * bulgeFadeOut;
|
|
209
|
+
}
|
|
210
|
+
else if (bulgeType === "wave") {
|
|
211
|
+
// Wave: Organic S-curve from bottom-left to top-right with rotation
|
|
212
|
+
const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
|
|
213
|
+
// Start wave off-screen before bottom-left, end off-screen after top-right
|
|
214
|
+
const wavePosOnDiagonal = (waveProgress * diagonalLength * 1.4) - (diagonalLength * 0.2);
|
|
215
|
+
// Calculate distance along the diagonal (from bottom-left to top-right)
|
|
216
|
+
const normalizedX = dot.x / canvasWidth;
|
|
217
|
+
const normalizedY = 1 - (dot.y / canvasHeight);
|
|
218
|
+
const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
|
|
219
|
+
// Distance from wave front
|
|
220
|
+
const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
|
|
221
|
+
const waveWidth = diagonalLength * 0.25;
|
|
222
|
+
const distanceNorm = distanceToWaveFront / waveWidth;
|
|
223
|
+
// Smooth wave factor with wider influence
|
|
224
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 2.5);
|
|
225
|
+
// Create rotating S-curve with multiple frequency components
|
|
226
|
+
const perpendicularOffset = normalizedY - normalizedX;
|
|
227
|
+
// Primary S-curve that rotates over time
|
|
228
|
+
const rotationPhase = waveProgress * Math.PI * 3; // Rotates 1.5 times during animation
|
|
229
|
+
const primaryFreq = 3; // Number of S-curves along the wave
|
|
230
|
+
const sCurvePrimary = Math.sin(perpendicularOffset * Math.PI * primaryFreq + rotationPhase);
|
|
231
|
+
// Secondary curve for complexity (higher frequency, lower amplitude)
|
|
232
|
+
const secondaryFreq = 7;
|
|
233
|
+
const sCurveSecondary = Math.sin(perpendicularOffset * Math.PI * secondaryFreq - rotationPhase * 1.5) * 0.4;
|
|
234
|
+
// Combine curves for organic motion
|
|
235
|
+
const sCurveFactor = sCurvePrimary + sCurveSecondary;
|
|
236
|
+
// Wave straightens at the edges (distanceNorm affects curve strength)
|
|
237
|
+
const curveStrength = 1 - Math.abs(distanceNorm) * 0.5;
|
|
238
|
+
const modulatedCurve = sCurveFactor * curveStrength;
|
|
239
|
+
// Apply displacement
|
|
240
|
+
const baseDisplacement = waveFactor * bulgeIntensity * bulgeFadeOut;
|
|
241
|
+
const diagonalAngle = Math.PI / 4;
|
|
242
|
+
// Perpendicular displacement creates the S-curve
|
|
243
|
+
const perpAngle = diagonalAngle + Math.PI / 2;
|
|
244
|
+
offsetX = Math.cos(diagonalAngle) * baseDisplacement + Math.cos(perpAngle) * modulatedCurve * baseDisplacement * 0.8;
|
|
245
|
+
offsetY = -Math.sin(diagonalAngle) * baseDisplacement - Math.sin(perpAngle) * modulatedCurve * baseDisplacement * 0.8;
|
|
246
|
+
sizeMultiplier = 1 + waveFactor * 0.5 * bulgeFadeOut;
|
|
247
|
+
const waveOpacity = 0.4 + waveFactor * 0.6;
|
|
248
|
+
bulgeOpacity = 1 + (waveOpacity - 1) * bulgeFadeOut;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
252
|
+
const adjustedSize = size * sizeMultiplier;
|
|
253
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
254
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
120
255
|
});
|
|
121
256
|
ctx.globalAlpha = 1;
|
|
122
257
|
animationRef.current = requestAnimationFrame(animate);
|
|
@@ -149,9 +284,53 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
149
284
|
}
|
|
150
285
|
}
|
|
151
286
|
if (opacity > 0) {
|
|
287
|
+
// Calculate bulge displacement
|
|
288
|
+
let offsetX = 0;
|
|
289
|
+
let offsetY = 0;
|
|
290
|
+
let sizeMultiplier = 1;
|
|
291
|
+
let bulgeOpacity = 1;
|
|
292
|
+
if (bulgeEnabled) {
|
|
293
|
+
if (bulgeType === "ripple") {
|
|
294
|
+
const dx = dot.x - centerX;
|
|
295
|
+
const dy = dot.y - centerY;
|
|
296
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
297
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
298
|
+
const waveWidth = maxRadius * 0.15;
|
|
299
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
300
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
301
|
+
const angle = Math.atan2(dy, dx);
|
|
302
|
+
const displacementAmount = waveFactor * bulgeIntensity;
|
|
303
|
+
offsetX = Math.cos(angle) * displacementAmount;
|
|
304
|
+
offsetY = Math.sin(angle) * displacementAmount;
|
|
305
|
+
sizeMultiplier = 1 + waveFactor * 0.8;
|
|
306
|
+
bulgeOpacity = 0.3 + waveFactor * 0.7;
|
|
307
|
+
}
|
|
308
|
+
else if (bulgeType === "wave") {
|
|
309
|
+
const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
|
|
310
|
+
const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
|
|
311
|
+
const normalizedX = dot.x / canvasWidth;
|
|
312
|
+
const normalizedY = 1 - (dot.y / canvasHeight);
|
|
313
|
+
const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
|
|
314
|
+
const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
|
|
315
|
+
const waveWidth = diagonalLength * 0.2;
|
|
316
|
+
const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
|
|
317
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
|
|
318
|
+
const perpendicularOffset = normalizedY - normalizedX;
|
|
319
|
+
const sCurvePhase = perpendicularOffset * Math.PI * 2;
|
|
320
|
+
const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
|
|
321
|
+
const baseDisplacement = waveFactor * bulgeIntensity;
|
|
322
|
+
const diagonalAngle = Math.PI / 4;
|
|
323
|
+
offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
324
|
+
offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
325
|
+
sizeMultiplier = 1 + waveFactor * 0.6;
|
|
326
|
+
bulgeOpacity = 0.4 + waveFactor * 0.6;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
152
329
|
ctx.fillStyle = dot.color;
|
|
153
|
-
ctx.globalAlpha = opacity;
|
|
154
|
-
|
|
330
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
331
|
+
const adjustedSize = size * sizeMultiplier;
|
|
332
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
333
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
155
334
|
}
|
|
156
335
|
});
|
|
157
336
|
}
|
|
@@ -165,82 +344,194 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
165
344
|
const flickerMultiplier = 0.6 + (flickerValue * 0.4);
|
|
166
345
|
opacity *= flickerMultiplier;
|
|
167
346
|
}
|
|
168
|
-
|
|
169
|
-
|
|
347
|
+
// Calculate bulge displacement
|
|
348
|
+
let offsetX = 0;
|
|
349
|
+
let offsetY = 0;
|
|
350
|
+
let sizeMultiplier = 1;
|
|
351
|
+
let bulgeOpacity = 1;
|
|
352
|
+
if (bulgeEnabled) {
|
|
353
|
+
if (bulgeType === "ripple") {
|
|
354
|
+
const dx = dot.x - centerX;
|
|
355
|
+
const dy = dot.y - centerY;
|
|
356
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
357
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
358
|
+
const waveWidth = maxRadius * 0.15;
|
|
359
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
360
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
361
|
+
const angle = Math.atan2(dy, dx);
|
|
362
|
+
const displacementAmount = waveFactor * bulgeIntensity;
|
|
363
|
+
offsetX = Math.cos(angle) * displacementAmount;
|
|
364
|
+
offsetY = Math.sin(angle) * displacementAmount;
|
|
365
|
+
sizeMultiplier = 1 + waveFactor * 0.8;
|
|
366
|
+
bulgeOpacity = 0.3 + waveFactor * 0.7;
|
|
367
|
+
}
|
|
368
|
+
else if (bulgeType === "wave") {
|
|
369
|
+
const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
|
|
370
|
+
const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
|
|
371
|
+
const normalizedX = dot.x / canvasWidth;
|
|
372
|
+
const normalizedY = 1 - (dot.y / canvasHeight);
|
|
373
|
+
const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
|
|
374
|
+
const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
|
|
375
|
+
const waveWidth = diagonalLength * 0.2;
|
|
376
|
+
const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
|
|
377
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
|
|
378
|
+
const perpendicularOffset = normalizedY - normalizedX;
|
|
379
|
+
const sCurvePhase = perpendicularOffset * Math.PI * 2;
|
|
380
|
+
const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
|
|
381
|
+
const baseDisplacement = waveFactor * bulgeIntensity;
|
|
382
|
+
const diagonalAngle = Math.PI / 4;
|
|
383
|
+
offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
384
|
+
offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
385
|
+
sizeMultiplier = 1 + waveFactor * 0.6;
|
|
386
|
+
bulgeOpacity = 0.4 + waveFactor * 0.6;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
390
|
+
const adjustedSize = size * sizeMultiplier;
|
|
391
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
392
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
170
393
|
});
|
|
171
394
|
}
|
|
172
395
|
ctx.globalAlpha = 1;
|
|
173
396
|
animationRef.current = requestAnimationFrame(animate);
|
|
174
397
|
return;
|
|
175
398
|
}
|
|
176
|
-
// For hover
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
399
|
+
// For hover, click, and manual triggers with animation
|
|
400
|
+
if (trigger === "hover" || trigger === "click" || trigger === "manual") {
|
|
401
|
+
if (isHoveredRef.current) {
|
|
402
|
+
// Revealing animation with explosive easing
|
|
403
|
+
const now = Date.now();
|
|
404
|
+
const elapsed = (now - revealStartTimeRef.current) / 1000;
|
|
405
|
+
// Cubic easing: starts very slow, then explodes
|
|
406
|
+
const revealProgress = Math.pow(elapsed, 3) * speed * 3;
|
|
407
|
+
// Cap progress to prevent infinite growth (max offset is ~1.3)
|
|
408
|
+
const cappedProgress = Math.min(revealProgress, 2.0);
|
|
409
|
+
// Track maximum progress for reverse animation
|
|
410
|
+
maxRevealProgressRef.current = cappedProgress;
|
|
411
|
+
dots.forEach((dot) => {
|
|
412
|
+
const normalizedDistance = dot.distanceFromOrigin / maxDistance;
|
|
413
|
+
const introOffset = normalizedDistance * 0.8 + dot.randomOffset * 0.5; // Much lower threshold
|
|
414
|
+
let opacity = 0;
|
|
415
|
+
if (cappedProgress > introOffset) {
|
|
416
|
+
// Explosive opacity increase
|
|
417
|
+
const fadeIn = (cappedProgress - introOffset) * 8; // Faster fade-in
|
|
418
|
+
opacity = Math.min(1, fadeIn * fadeIn) * dot.baseOpacity;
|
|
419
|
+
// Apply flicker effect if enabled
|
|
420
|
+
if (flicker) {
|
|
421
|
+
const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
|
|
422
|
+
const flickerMultiplier = 0.6 + (flickerValue * 0.4); // Oscillate between 0.6 and 1.0
|
|
423
|
+
opacity *= flickerMultiplier;
|
|
424
|
+
}
|
|
200
425
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
|
|
426
|
+
if (opacity > 0) {
|
|
427
|
+
// Calculate bulge displacement
|
|
428
|
+
let offsetX = 0;
|
|
429
|
+
let offsetY = 0;
|
|
430
|
+
let sizeMultiplier = 1;
|
|
431
|
+
let bulgeOpacity = 1;
|
|
432
|
+
if (bulgeEnabled) {
|
|
433
|
+
if (bulgeType === "ripple") {
|
|
434
|
+
const dx = dot.x - centerX;
|
|
435
|
+
const dy = dot.y - centerY;
|
|
436
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
437
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
438
|
+
const waveWidth = maxRadius * 0.15;
|
|
439
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
440
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
441
|
+
const angle = Math.atan2(dy, dx);
|
|
442
|
+
const displacementAmount = waveFactor * bulgeIntensity;
|
|
443
|
+
offsetX = Math.cos(angle) * displacementAmount;
|
|
444
|
+
offsetY = Math.sin(angle) * displacementAmount;
|
|
445
|
+
sizeMultiplier = 1 + waveFactor * 0.8;
|
|
446
|
+
bulgeOpacity = 0.3 + waveFactor * 0.7;
|
|
447
|
+
}
|
|
448
|
+
else if (bulgeType === "wave") {
|
|
449
|
+
const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
|
|
450
|
+
const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
|
|
451
|
+
const normalizedX = dot.x / canvasWidth;
|
|
452
|
+
const normalizedY = 1 - (dot.y / canvasHeight);
|
|
453
|
+
const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
|
|
454
|
+
const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
|
|
455
|
+
const waveWidth = diagonalLength * 0.2;
|
|
456
|
+
const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
|
|
457
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
|
|
458
|
+
const perpendicularOffset = normalizedY - normalizedX;
|
|
459
|
+
const sCurvePhase = perpendicularOffset * Math.PI * 2;
|
|
460
|
+
const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
|
|
461
|
+
const baseDisplacement = waveFactor * bulgeIntensity;
|
|
462
|
+
const diagonalAngle = Math.PI / 4;
|
|
463
|
+
offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
464
|
+
offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
|
|
465
|
+
sizeMultiplier = 1 + waveFactor * 0.6;
|
|
466
|
+
bulgeOpacity = 0.4 + waveFactor * 0.6;
|
|
232
467
|
}
|
|
233
468
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
469
|
+
ctx.fillStyle = dot.color;
|
|
470
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
471
|
+
const adjustedSize = size * sizeMultiplier;
|
|
472
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
473
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
// Reverse animation when hover ends - only run if there's something to hide
|
|
479
|
+
if (hideStartProgressRef.current > 0) {
|
|
480
|
+
const elapsed = (Date.now() - hideStartTimeRef.current) / 1000;
|
|
481
|
+
// Use quadratic easing for faster hide (power of 2)
|
|
482
|
+
const hideSpeed = speed * 6; // Faster to handle max progress of 2.0
|
|
483
|
+
const hideProgress = Math.pow(elapsed, 2) * hideSpeed;
|
|
484
|
+
// Reverse from the progress we had when hide started
|
|
485
|
+
const reverseProgress = Math.max(0, hideStartProgressRef.current - hideProgress);
|
|
486
|
+
if (reverseProgress > 0.01) { // Small threshold to ensure completion
|
|
487
|
+
// Still have dots to hide
|
|
488
|
+
dots.forEach((dot) => {
|
|
489
|
+
const normalizedDistance = dot.distanceFromOrigin / maxDistance;
|
|
490
|
+
const introOffset = normalizedDistance * 0.8 + dot.randomOffset * 0.5;
|
|
491
|
+
let opacity = 0;
|
|
492
|
+
if (reverseProgress > introOffset) {
|
|
493
|
+
const fadeIn = (reverseProgress - introOffset) * 8;
|
|
494
|
+
opacity = Math.min(1, fadeIn * fadeIn) * dot.baseOpacity;
|
|
495
|
+
// Apply flicker effect if enabled
|
|
496
|
+
if (flicker) {
|
|
497
|
+
const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
|
|
498
|
+
const flickerMultiplier = 0.6 + (flickerValue * 0.4);
|
|
499
|
+
opacity *= flickerMultiplier;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
if (opacity > 0) {
|
|
503
|
+
// Calculate bulge displacement
|
|
504
|
+
let offsetX = 0;
|
|
505
|
+
let offsetY = 0;
|
|
506
|
+
let sizeMultiplier = 1;
|
|
507
|
+
let bulgeOpacity = 1;
|
|
508
|
+
if (bulgeEnabled) {
|
|
509
|
+
const dx = dot.x - centerX;
|
|
510
|
+
const dy = dot.y - centerY;
|
|
511
|
+
const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
|
|
512
|
+
const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
|
|
513
|
+
const waveWidth = maxRadius * 0.15;
|
|
514
|
+
const distanceNorm = distanceToWave / waveWidth;
|
|
515
|
+
const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
|
|
516
|
+
const angle = Math.atan2(dy, dx);
|
|
517
|
+
const displacementAmount = waveFactor * bulgeIntensity;
|
|
518
|
+
offsetX = Math.cos(angle) * displacementAmount * 0.3;
|
|
519
|
+
offsetY = Math.sin(angle) * displacementAmount * 0.3 - waveFactor * bulgeIntensity;
|
|
520
|
+
sizeMultiplier = 1 + waveFactor * 0.8;
|
|
521
|
+
bulgeOpacity = 0.3 + waveFactor * 0.7;
|
|
522
|
+
}
|
|
523
|
+
ctx.fillStyle = dot.color;
|
|
524
|
+
ctx.globalAlpha = opacity * bulgeOpacity;
|
|
525
|
+
const adjustedSize = size * sizeMultiplier;
|
|
526
|
+
const sizeOffset = (adjustedSize - size) / 2;
|
|
527
|
+
ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
// Hide animation complete, reset progress and clear canvas
|
|
533
|
+
hideStartProgressRef.current = 0;
|
|
534
|
+
}
|
|
244
535
|
}
|
|
245
536
|
}
|
|
246
537
|
}
|
|
@@ -254,7 +545,42 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
254
545
|
cancelAnimationFrame(animationRef.current);
|
|
255
546
|
}
|
|
256
547
|
};
|
|
257
|
-
}, [colors, size, spacing, speed, revealFrom, trigger, flicker]);
|
|
548
|
+
}, [colors, size, spacing, speed, revealFrom, trigger, flicker, bulge]);
|
|
549
|
+
// Manual trigger control via `active` prop
|
|
550
|
+
useEffect(() => {
|
|
551
|
+
if (trigger !== "manual")
|
|
552
|
+
return;
|
|
553
|
+
const now = Date.now();
|
|
554
|
+
if (active) {
|
|
555
|
+
// Mimic mouse enter
|
|
556
|
+
if (hideStartProgressRef.current > 0) {
|
|
557
|
+
const hideElapsed = (now - hideStartTimeRef.current) / 1000;
|
|
558
|
+
const hideSpeed = speed * 6;
|
|
559
|
+
const hideProgress = Math.pow(hideElapsed, 2) * hideSpeed;
|
|
560
|
+
const currentProgress = Math.max(0, hideStartProgressRef.current - hideProgress);
|
|
561
|
+
const effectiveElapsed = Math.pow(currentProgress / (speed * 3), 1 / 3);
|
|
562
|
+
const simulatedStartTime = now - effectiveElapsed * 1000;
|
|
563
|
+
revealStartTimeRef.current = simulatedStartTime;
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
revealStartTimeRef.current = now;
|
|
567
|
+
}
|
|
568
|
+
if (bulge && !bulge.repeat) {
|
|
569
|
+
bulgeStartTimeRef.current = now;
|
|
570
|
+
}
|
|
571
|
+
isHoveredRef.current = true;
|
|
572
|
+
hideStartProgressRef.current = 0;
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
// Mimic mouse leave
|
|
576
|
+
if (isHoveredRef.current) {
|
|
577
|
+
const currentProgress = maxRevealProgressRef.current;
|
|
578
|
+
hideStartTimeRef.current = Date.now();
|
|
579
|
+
hideStartProgressRef.current = currentProgress;
|
|
580
|
+
isHoveredRef.current = false;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}, [active, trigger, speed, bulge]);
|
|
258
584
|
const handleMouseEnter = () => {
|
|
259
585
|
if (trigger === "hover" && !isHoveredRef.current) {
|
|
260
586
|
const now = Date.now();
|
|
@@ -275,6 +601,10 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
275
601
|
// Fresh start
|
|
276
602
|
revealStartTimeRef.current = now;
|
|
277
603
|
}
|
|
604
|
+
// Reset bulge animation on each hover when repeat is false
|
|
605
|
+
if (bulge && !bulge.repeat) {
|
|
606
|
+
bulgeStartTimeRef.current = now;
|
|
607
|
+
}
|
|
278
608
|
isHoveredRef.current = true;
|
|
279
609
|
hideStartProgressRef.current = 0; // Clear hide state
|
|
280
610
|
}
|
|
@@ -293,7 +623,39 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
|
|
|
293
623
|
isHoveredRef.current = false;
|
|
294
624
|
}
|
|
295
625
|
};
|
|
296
|
-
|
|
626
|
+
const handleClick = () => {
|
|
627
|
+
if (trigger !== "click")
|
|
628
|
+
return;
|
|
629
|
+
if (!isHoveredRef.current) {
|
|
630
|
+
// Enter
|
|
631
|
+
const now = Date.now();
|
|
632
|
+
if (hideStartProgressRef.current > 0) {
|
|
633
|
+
const hideElapsed = (now - hideStartTimeRef.current) / 1000;
|
|
634
|
+
const hideSpeed = speed * 6;
|
|
635
|
+
const hideProgress = Math.pow(hideElapsed, 2) * hideSpeed;
|
|
636
|
+
const currentProgress = Math.max(0, hideStartProgressRef.current - hideProgress);
|
|
637
|
+
const effectiveElapsed = Math.pow(currentProgress / (speed * 3), 1 / 3);
|
|
638
|
+
const simulatedStartTime = now - effectiveElapsed * 1000;
|
|
639
|
+
revealStartTimeRef.current = simulatedStartTime;
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
revealStartTimeRef.current = now;
|
|
643
|
+
}
|
|
644
|
+
if (bulge && !bulge.repeat) {
|
|
645
|
+
bulgeStartTimeRef.current = now;
|
|
646
|
+
}
|
|
647
|
+
isHoveredRef.current = true;
|
|
648
|
+
hideStartProgressRef.current = 0;
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
// Leave
|
|
652
|
+
const currentProgress = maxRevealProgressRef.current;
|
|
653
|
+
hideStartTimeRef.current = Date.now();
|
|
654
|
+
hideStartProgressRef.current = currentProgress;
|
|
655
|
+
isHoveredRef.current = false;
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
return (_jsxs(Flex, { ref: containerRef, fill: true, overflow: "hidden", onMouseEnter: handleMouseEnter, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, onClick: handleClick, ...rest, children: [_jsx("canvas", { ref: canvasRef, style: {
|
|
297
659
|
position: "absolute",
|
|
298
660
|
top: 0,
|
|
299
661
|
left: 0,
|