@principal-ai/logo-component 0.1.5 → 0.1.6
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/OpenTypeTextReveal.d.ts +47 -0
- package/dist/OpenTypeTextReveal.js +471 -0
- package/dist/TelemetryReveal.d.ts +28 -0
- package/dist/TelemetryReveal.js +185 -0
- package/dist/TextReveal.d.ts +34 -0
- package/dist/TextReveal.js +196 -0
- package/dist/esm/OpenTypeTextReveal.js +431 -0
- package/dist/esm/TelemetryReveal.js +148 -0
- package/dist/esm/TextReveal.js +159 -0
- package/dist/esm/index.js +5 -2
- package/dist/esm/presets.js +88 -0
- package/dist/esm/strokeCharacters.js +517 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.esm.js +5 -2
- package/dist/index.js +12 -5
- package/dist/presets.d.ts +10 -0
- package/dist/presets.js +91 -0
- package/dist/strokeCharacters.d.ts +33 -0
- package/dist/strokeCharacters.js +521 -0
- package/package.json +5 -1
- package/dist/MazeDemo.d.ts +0 -12
- package/dist/MazeDemo.js +0 -376
- package/dist/MazeDemoNew.d.ts +0 -12
- package/dist/MazeDemoNew.js +0 -376
- package/dist/esm/MazeDemo.js +0 -339
- package/dist/esm/MazeDemoNew.js +0 -339
- package/dist/esm/utils/mazeGenerator.js +0 -266
- package/dist/utils/mazeGenerator.d.ts +0 -56
- package/dist/utils/mazeGenerator.js +0 -271
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TelemetryReveal = void 0;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const presets_1 = require("./presets");
|
|
39
|
+
let globalIdCounter = 0;
|
|
40
|
+
/**
|
|
41
|
+
* Estimates path length from simple SVG path data (M/L commands).
|
|
42
|
+
*/
|
|
43
|
+
function estimatePathLength(d) {
|
|
44
|
+
const coords = d.match(/-?\d+\.?\d*/g);
|
|
45
|
+
if (!coords || coords.length < 4)
|
|
46
|
+
return 100;
|
|
47
|
+
let totalLength = 0;
|
|
48
|
+
for (let i = 2; i < coords.length; i += 2) {
|
|
49
|
+
const x1 = parseFloat(coords[i - 2]);
|
|
50
|
+
const y1 = parseFloat(coords[i - 1]);
|
|
51
|
+
const x2 = parseFloat(coords[i]);
|
|
52
|
+
const y2 = parseFloat(coords[i + 1]);
|
|
53
|
+
totalLength += Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
|
54
|
+
}
|
|
55
|
+
return totalLength || 100;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse start and end points from path data (M x1,y1 L x2,y2)
|
|
59
|
+
*/
|
|
60
|
+
function getPathEndpoints(d) {
|
|
61
|
+
const coords = d.match(/-?\d+\.?\d*/g);
|
|
62
|
+
if (!coords || coords.length < 4) {
|
|
63
|
+
return { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } };
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
start: { x: parseFloat(coords[0]), y: parseFloat(coords[1]) },
|
|
67
|
+
end: { x: parseFloat(coords[coords.length - 2]), y: parseFloat(coords[coords.length - 1]) },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Seeded random for consistent chaos patterns
|
|
72
|
+
*/
|
|
73
|
+
function seededRandom(seed) {
|
|
74
|
+
return () => {
|
|
75
|
+
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
|
|
76
|
+
return seed / 0x7fffffff;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const TelemetryReveal = ({ width = 200, height = 200, paths: customPaths, preset = "network", viewBox, chaosMode = "none", chaosDuration = 3, dotsDuration = 2, flowDuration = 2, flowDelay = 0.3, particlesPerPath = 2, particleRadius = 3, color = "currentColor", particleColor, strokeWidth = 2, opacity = 0.9, showGlow = true, fadeAfterAssembly = true, fadeOpacity = 0.5, loop = true, loopDelay = 1, }) => {
|
|
80
|
+
const idRef = (0, react_1.useRef)(null);
|
|
81
|
+
if (idRef.current === null) {
|
|
82
|
+
idRef.current = `tr${globalIdCounter++}`;
|
|
83
|
+
}
|
|
84
|
+
const uniqueId = idRef.current;
|
|
85
|
+
const finalParticleColor = particleColor || color;
|
|
86
|
+
const isFragmented = chaosMode === "fragmented";
|
|
87
|
+
// Loop via remount
|
|
88
|
+
const [cycle, setCycle] = (0, react_1.useState)(0);
|
|
89
|
+
// Resolve paths from custom or preset
|
|
90
|
+
const resolvedPaths = (0, react_1.useMemo)(() => {
|
|
91
|
+
if (customPaths)
|
|
92
|
+
return customPaths;
|
|
93
|
+
const presetConfig = presets_1.TELEMETRY_PRESETS[preset];
|
|
94
|
+
return (presetConfig === null || presetConfig === void 0 ? void 0 : presetConfig.paths) || [];
|
|
95
|
+
}, [customPaths, preset]);
|
|
96
|
+
// Resolve viewBox
|
|
97
|
+
const resolvedViewBox = (0, react_1.useMemo)(() => {
|
|
98
|
+
var _a;
|
|
99
|
+
if (viewBox)
|
|
100
|
+
return viewBox;
|
|
101
|
+
if (!customPaths && ((_a = presets_1.TELEMETRY_PRESETS[preset]) === null || _a === void 0 ? void 0 : _a.viewBox)) {
|
|
102
|
+
return presets_1.TELEMETRY_PRESETS[preset].viewBox;
|
|
103
|
+
}
|
|
104
|
+
return "0 0 200 200";
|
|
105
|
+
}, [viewBox, customPaths, preset]);
|
|
106
|
+
// Calculate path lengths and endpoints
|
|
107
|
+
const pathData = (0, react_1.useMemo)(() => {
|
|
108
|
+
return resolvedPaths.map((p) => ({
|
|
109
|
+
length: estimatePathLength(p.d),
|
|
110
|
+
endpoints: getPathEndpoints(p.d),
|
|
111
|
+
}));
|
|
112
|
+
}, [resolvedPaths]);
|
|
113
|
+
// Generate offsets for fragmented mode
|
|
114
|
+
const fragmentOffsets = (0, react_1.useMemo)(() => {
|
|
115
|
+
const random = seededRandom(42);
|
|
116
|
+
return resolvedPaths.map(() => ({
|
|
117
|
+
x: (random() - 0.5) * 80,
|
|
118
|
+
y: (random() - 0.5) * 80,
|
|
119
|
+
}));
|
|
120
|
+
}, [resolvedPaths]);
|
|
121
|
+
// Timing calculations
|
|
122
|
+
// In fragmented mode: dots appear, extend to lines, then assemble
|
|
123
|
+
// Phase 1: Dots appear one at a time (dotsDuration / 2)
|
|
124
|
+
// Phase 2: Lines draw one at a time (dotsDuration / 2)
|
|
125
|
+
// Phase 3: Lines move to final position one at a time (chaosDuration)
|
|
126
|
+
// Phase 4: Particles flow
|
|
127
|
+
const numPaths = resolvedPaths.length || 1;
|
|
128
|
+
const perItemDotsDuration = (dotsDuration * 0.5) / numPaths;
|
|
129
|
+
const perItemLinesDuration = (dotsDuration * 0.5) / numPaths;
|
|
130
|
+
const perItemAssemblyDuration = chaosDuration / numPaths;
|
|
131
|
+
const dotsPhaseEnd = dotsDuration * 0.5;
|
|
132
|
+
const linesPhaseEnd = dotsDuration;
|
|
133
|
+
const assemblyEndTime = isFragmented ? dotsDuration + chaosDuration : 0;
|
|
134
|
+
const flowBeginTime = assemblyEndTime + flowDelay;
|
|
135
|
+
// Total animation duration (last particle finishes at flowBeginTime + flowDuration)
|
|
136
|
+
const totalDuration = flowBeginTime + flowDuration;
|
|
137
|
+
// Remount-based loop
|
|
138
|
+
(0, react_1.useEffect)(() => {
|
|
139
|
+
if (!loop)
|
|
140
|
+
return;
|
|
141
|
+
const timeout = setTimeout(() => {
|
|
142
|
+
setCycle((c) => c + 1);
|
|
143
|
+
}, (totalDuration + loopDelay) * 1000);
|
|
144
|
+
return () => clearTimeout(timeout);
|
|
145
|
+
}, [loop, totalDuration, loopDelay, cycle]);
|
|
146
|
+
return (react_1.default.createElement("svg", { key: cycle, width: width, height: height, viewBox: resolvedViewBox, xmlns: "http://www.w3.org/2000/svg", style: { opacity } },
|
|
147
|
+
react_1.default.createElement("defs", null,
|
|
148
|
+
showGlow && (react_1.default.createElement("filter", { id: `glow-${uniqueId}`, x: "-50%", y: "-50%", width: "200%", height: "200%" },
|
|
149
|
+
react_1.default.createElement("feGaussianBlur", { stdDeviation: "2", result: "blur" }),
|
|
150
|
+
react_1.default.createElement("feMerge", null,
|
|
151
|
+
react_1.default.createElement("feMergeNode", { in: "blur" }),
|
|
152
|
+
react_1.default.createElement("feMergeNode", { in: "SourceGraphic" })))),
|
|
153
|
+
react_1.default.createElement("radialGradient", { id: `particleGradient-${uniqueId}`, cx: "50%", cy: "50%", r: "50%" },
|
|
154
|
+
react_1.default.createElement("stop", { offset: "0%", style: { stopColor: finalParticleColor, stopOpacity: 1 } }),
|
|
155
|
+
react_1.default.createElement("stop", { offset: "100%", style: { stopColor: finalParticleColor, stopOpacity: 0.3 } }))),
|
|
156
|
+
react_1.default.createElement("g", { className: "telemetry-paths" }, resolvedPaths.map((path, index) => {
|
|
157
|
+
const offset = fragmentOffsets[index];
|
|
158
|
+
const { length: pathLength, endpoints } = pathData[index] || { length: 100, endpoints: { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } } };
|
|
159
|
+
const dotRadius = strokeWidth / 2;
|
|
160
|
+
return (react_1.default.createElement("g", { key: path.id || `path-${index}`, transform: isFragmented ? `translate(${offset.x}, ${offset.y})` : undefined },
|
|
161
|
+
isFragmented && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
162
|
+
react_1.default.createElement("circle", { cx: endpoints.start.x, cy: endpoints.start.y, r: dotRadius, fill: color, opacity: "0" },
|
|
163
|
+
react_1.default.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.15s", begin: `${index * perItemDotsDuration}s`, fill: "freeze" }),
|
|
164
|
+
react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: "0", dur: "0.2s", begin: `${dotsPhaseEnd + (index + 1) * perItemLinesDuration}s`, fill: "freeze" })),
|
|
165
|
+
react_1.default.createElement("circle", { cx: endpoints.end.x, cy: endpoints.end.y, r: dotRadius, fill: color, opacity: "0" },
|
|
166
|
+
react_1.default.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.15s", begin: `${index * perItemDotsDuration}s`, fill: "freeze" }),
|
|
167
|
+
react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: "0", dur: "0.2s", begin: `${dotsPhaseEnd + (index + 1) * perItemLinesDuration}s`, fill: "freeze" })))),
|
|
168
|
+
react_1.default.createElement("path", { d: path.d, fill: "none", stroke: color, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", opacity: isFragmented ? 0 : 1, strokeDasharray: isFragmented ? pathLength : undefined, strokeDashoffset: isFragmented ? pathLength : undefined },
|
|
169
|
+
isFragmented && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
170
|
+
react_1.default.createElement("animate", { attributeName: "opacity", values: `0;1;1;${fadeAfterAssembly ? fadeOpacity : 1}`, keyTimes: "0;0.1;0.9;1", dur: `${perItemLinesDuration + chaosDuration}s`, begin: `${dotsPhaseEnd + index * perItemLinesDuration}s`, fill: "freeze" }),
|
|
171
|
+
react_1.default.createElement("animate", { attributeName: "stroke-dashoffset", from: pathLength, to: "0", dur: `${perItemLinesDuration}s`, begin: `${dotsPhaseEnd + index * perItemLinesDuration}s`, fill: "freeze", calcMode: "spline", keySplines: "0.4 0 0.2 1", keyTimes: "0;1" }))),
|
|
172
|
+
!isFragmented && fadeAfterAssembly && (react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: fadeOpacity, dur: "0.5s", begin: `${flowBeginTime}s`, fill: "freeze" }))),
|
|
173
|
+
isFragmented && (react_1.default.createElement("animateTransform", { attributeName: "transform", type: "translate", from: `${offset.x} ${offset.y}`, to: "0 0", dur: `${perItemAssemblyDuration}s`, begin: `${linesPhaseEnd + index * perItemAssemblyDuration}s`, fill: "freeze", calcMode: "spline", keySplines: "0.33 0 0.2 1", keyTimes: "0;1" }))));
|
|
174
|
+
})),
|
|
175
|
+
react_1.default.createElement("g", { className: "telemetry-particles", filter: showGlow ? `url(#glow-${uniqueId})` : undefined }, resolvedPaths.flatMap((path, pathIndex) => {
|
|
176
|
+
return Array.from({ length: particlesPerPath }).map((_, particleIndex) => {
|
|
177
|
+
const particleDelay = (particleIndex / particlesPerPath) * flowDuration;
|
|
178
|
+
const beginTime = flowBeginTime + particleDelay;
|
|
179
|
+
return (react_1.default.createElement("circle", { key: `particle-${pathIndex}-${particleIndex}`, r: particleRadius, fill: `url(#particleGradient-${uniqueId})`, opacity: "0" },
|
|
180
|
+
react_1.default.createElement("animateMotion", { dur: `${flowDuration}s`, begin: `${beginTime}s`, fill: "freeze", path: path.d }),
|
|
181
|
+
react_1.default.createElement("animate", { attributeName: "opacity", values: "0;1;1;0", keyTimes: "0;0.1;0.9;1", dur: `${flowDuration}s`, begin: `${beginTime}s`, fill: "freeze" })));
|
|
182
|
+
});
|
|
183
|
+
}))));
|
|
184
|
+
};
|
|
185
|
+
exports.TelemetryReveal = TelemetryReveal;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
type ChaosMode = "none" | "fragmented";
|
|
3
|
+
interface TextRevealProps {
|
|
4
|
+
/** The text to display */
|
|
5
|
+
text: string;
|
|
6
|
+
/** Width of the SVG container */
|
|
7
|
+
width?: number;
|
|
8
|
+
/** Height of the SVG container */
|
|
9
|
+
height?: number;
|
|
10
|
+
/** Scale factor for the text (default 1) */
|
|
11
|
+
scale?: number;
|
|
12
|
+
/** Letter spacing in units (default 4) */
|
|
13
|
+
letterSpacing?: number;
|
|
14
|
+
/** Center the text in the container */
|
|
15
|
+
centerText?: boolean;
|
|
16
|
+
chaosMode?: ChaosMode;
|
|
17
|
+
chaosDuration?: number;
|
|
18
|
+
dotsDuration?: number;
|
|
19
|
+
flowDuration?: number;
|
|
20
|
+
flowDelay?: number;
|
|
21
|
+
particlesPerPath?: number;
|
|
22
|
+
particleRadius?: number;
|
|
23
|
+
color?: string;
|
|
24
|
+
particleColor?: string;
|
|
25
|
+
strokeWidth?: number;
|
|
26
|
+
opacity?: number;
|
|
27
|
+
showGlow?: boolean;
|
|
28
|
+
fadeAfterAssembly?: boolean;
|
|
29
|
+
fadeOpacity?: number;
|
|
30
|
+
loop?: boolean;
|
|
31
|
+
loopDelay?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare const TextReveal: React.FC<TextRevealProps>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TextReveal = void 0;
|
|
37
|
+
const react_1 = __importStar(require("react"));
|
|
38
|
+
const strokeCharacters_1 = require("./strokeCharacters");
|
|
39
|
+
let globalIdCounter = 0;
|
|
40
|
+
/**
|
|
41
|
+
* Estimates path length from SVG path data.
|
|
42
|
+
*/
|
|
43
|
+
function estimatePathLength(d) {
|
|
44
|
+
const coords = d.match(/-?\d+\.?\d*/g);
|
|
45
|
+
if (!coords || coords.length < 4)
|
|
46
|
+
return 100;
|
|
47
|
+
let totalLength = 0;
|
|
48
|
+
for (let i = 2; i < coords.length; i += 2) {
|
|
49
|
+
const x1 = parseFloat(coords[i - 2]);
|
|
50
|
+
const y1 = parseFloat(coords[i - 1]);
|
|
51
|
+
const x2 = parseFloat(coords[i]);
|
|
52
|
+
const y2 = parseFloat(coords[i + 1]);
|
|
53
|
+
totalLength += Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
|
54
|
+
}
|
|
55
|
+
return totalLength || 100;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse start and end points from path data
|
|
59
|
+
*/
|
|
60
|
+
function getPathEndpoints(d) {
|
|
61
|
+
const coords = d.match(/-?\d+\.?\d*/g);
|
|
62
|
+
if (!coords || coords.length < 4) {
|
|
63
|
+
return { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } };
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
start: { x: parseFloat(coords[0]), y: parseFloat(coords[1]) },
|
|
67
|
+
end: { x: parseFloat(coords[coords.length - 2]), y: parseFloat(coords[coords.length - 1]) },
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Seeded random for consistent chaos patterns
|
|
72
|
+
*/
|
|
73
|
+
function seededRandom(seed) {
|
|
74
|
+
return () => {
|
|
75
|
+
seed = (seed * 1103515245 + 12345) & 0x7fffffff;
|
|
76
|
+
return seed / 0x7fffffff;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const TextReveal = ({ text, width = 400, height = 100, scale = 1, letterSpacing = 4, centerText = true, chaosMode = "none", chaosDuration = 2, dotsDuration = 1.5, flowDuration = 2, flowDelay = 0.3, particlesPerPath = 1, particleRadius = 2.5, color = "currentColor", particleColor, strokeWidth = 2, opacity = 0.9, showGlow = true, fadeAfterAssembly = true, fadeOpacity = 0.5, loop = true, loopDelay = 1, }) => {
|
|
80
|
+
const idRef = (0, react_1.useRef)(null);
|
|
81
|
+
if (idRef.current === null) {
|
|
82
|
+
idRef.current = `txr${globalIdCounter++}`;
|
|
83
|
+
}
|
|
84
|
+
const uniqueId = idRef.current;
|
|
85
|
+
const finalParticleColor = particleColor || color;
|
|
86
|
+
const isFragmented = chaosMode === "fragmented";
|
|
87
|
+
// Loop via remount
|
|
88
|
+
const [cycle, setCycle] = (0, react_1.useState)(0);
|
|
89
|
+
// Layout the text into paths
|
|
90
|
+
const { paths: layoutPaths, width: textWidth, height: textHeight } = (0, react_1.useMemo)(() => {
|
|
91
|
+
return (0, strokeCharacters_1.layoutText)(text, { letterSpacing, scale });
|
|
92
|
+
}, [text, letterSpacing, scale]);
|
|
93
|
+
// Calculate centering offset
|
|
94
|
+
const offsetX = centerText ? (width - textWidth) / 2 : 0;
|
|
95
|
+
const offsetY = centerText ? (height - textHeight) / 2 : 0;
|
|
96
|
+
// Apply offset to paths
|
|
97
|
+
const resolvedPaths = (0, react_1.useMemo)(() => {
|
|
98
|
+
return layoutPaths.map((path) => (Object.assign(Object.assign({}, path), { d: offsetPath(path.d, offsetX, offsetY) })));
|
|
99
|
+
}, [layoutPaths, offsetX, offsetY]);
|
|
100
|
+
// Calculate path data
|
|
101
|
+
const pathData = (0, react_1.useMemo)(() => {
|
|
102
|
+
return resolvedPaths.map((p) => ({
|
|
103
|
+
length: estimatePathLength(p.d),
|
|
104
|
+
endpoints: getPathEndpoints(p.d),
|
|
105
|
+
}));
|
|
106
|
+
}, [resolvedPaths]);
|
|
107
|
+
// Generate offsets for fragmented mode
|
|
108
|
+
const fragmentOffsets = (0, react_1.useMemo)(() => {
|
|
109
|
+
const random = seededRandom(42);
|
|
110
|
+
return resolvedPaths.map(() => ({
|
|
111
|
+
x: (random() - 0.5) * 120,
|
|
112
|
+
y: (random() - 0.5) * 80,
|
|
113
|
+
}));
|
|
114
|
+
}, [resolvedPaths]);
|
|
115
|
+
// Timing calculations
|
|
116
|
+
const numPaths = resolvedPaths.length || 1;
|
|
117
|
+
const perItemDotsDuration = (dotsDuration * 0.5) / numPaths;
|
|
118
|
+
const perItemLinesDuration = (dotsDuration * 0.5) / numPaths;
|
|
119
|
+
const perItemAssemblyDuration = chaosDuration / numPaths;
|
|
120
|
+
const dotsPhaseEnd = dotsDuration * 0.5;
|
|
121
|
+
const linesPhaseEnd = dotsDuration;
|
|
122
|
+
const assemblyEndTime = isFragmented ? dotsDuration + chaosDuration : 0;
|
|
123
|
+
const flowBeginTime = assemblyEndTime + flowDelay;
|
|
124
|
+
// Total animation duration
|
|
125
|
+
const totalDuration = flowBeginTime + flowDuration;
|
|
126
|
+
// Remount-based loop
|
|
127
|
+
(0, react_1.useEffect)(() => {
|
|
128
|
+
if (!loop)
|
|
129
|
+
return;
|
|
130
|
+
const timeout = setTimeout(() => {
|
|
131
|
+
setCycle((c) => c + 1);
|
|
132
|
+
}, (totalDuration + loopDelay) * 1000);
|
|
133
|
+
return () => clearTimeout(timeout);
|
|
134
|
+
}, [loop, totalDuration, loopDelay, cycle]);
|
|
135
|
+
// Calculate viewBox to fit content
|
|
136
|
+
const viewBox = `0 0 ${width} ${height}`;
|
|
137
|
+
return (react_1.default.createElement("svg", { key: cycle, width: width, height: height, viewBox: viewBox, xmlns: "http://www.w3.org/2000/svg", style: { opacity } },
|
|
138
|
+
react_1.default.createElement("defs", null,
|
|
139
|
+
showGlow && (react_1.default.createElement("filter", { id: `glow-${uniqueId}`, x: "-50%", y: "-50%", width: "200%", height: "200%" },
|
|
140
|
+
react_1.default.createElement("feGaussianBlur", { stdDeviation: "2", result: "blur" }),
|
|
141
|
+
react_1.default.createElement("feMerge", null,
|
|
142
|
+
react_1.default.createElement("feMergeNode", { in: "blur" }),
|
|
143
|
+
react_1.default.createElement("feMergeNode", { in: "SourceGraphic" })))),
|
|
144
|
+
react_1.default.createElement("radialGradient", { id: `particleGradient-${uniqueId}`, cx: "50%", cy: "50%", r: "50%" },
|
|
145
|
+
react_1.default.createElement("stop", { offset: "0%", style: { stopColor: finalParticleColor, stopOpacity: 1 } }),
|
|
146
|
+
react_1.default.createElement("stop", { offset: "100%", style: { stopColor: finalParticleColor, stopOpacity: 0.3 } }))),
|
|
147
|
+
react_1.default.createElement("g", { className: "text-paths" }, resolvedPaths.map((path, index) => {
|
|
148
|
+
const offset = fragmentOffsets[index];
|
|
149
|
+
const { length: pathLength, endpoints } = pathData[index] || {
|
|
150
|
+
length: 100,
|
|
151
|
+
endpoints: { start: { x: 0, y: 0 }, end: { x: 0, y: 0 } },
|
|
152
|
+
};
|
|
153
|
+
const dotRadius = strokeWidth / 2;
|
|
154
|
+
return (react_1.default.createElement("g", { key: path.id, transform: isFragmented ? `translate(${offset.x}, ${offset.y})` : undefined },
|
|
155
|
+
isFragmented && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
156
|
+
react_1.default.createElement("circle", { cx: endpoints.start.x, cy: endpoints.start.y, r: dotRadius, fill: color, opacity: "0" },
|
|
157
|
+
react_1.default.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.15s", begin: `${index * perItemDotsDuration}s`, fill: "freeze" }),
|
|
158
|
+
react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: "0", dur: "0.2s", begin: `${dotsPhaseEnd + (index + 1) * perItemLinesDuration}s`, fill: "freeze" })),
|
|
159
|
+
react_1.default.createElement("circle", { cx: endpoints.end.x, cy: endpoints.end.y, r: dotRadius, fill: color, opacity: "0" },
|
|
160
|
+
react_1.default.createElement("animate", { attributeName: "opacity", from: "0", to: "1", dur: "0.15s", begin: `${index * perItemDotsDuration}s`, fill: "freeze" }),
|
|
161
|
+
react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: "0", dur: "0.2s", begin: `${dotsPhaseEnd + (index + 1) * perItemLinesDuration}s`, fill: "freeze" })))),
|
|
162
|
+
react_1.default.createElement("path", { d: path.d, fill: "none", stroke: color, strokeWidth: strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", opacity: isFragmented ? 0 : 1, strokeDasharray: isFragmented ? pathLength : undefined, strokeDashoffset: isFragmented ? pathLength : undefined },
|
|
163
|
+
isFragmented && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
164
|
+
react_1.default.createElement("animate", { attributeName: "opacity", values: `0;1;1;${fadeAfterAssembly ? fadeOpacity : 1}`, keyTimes: "0;0.1;0.9;1", dur: `${perItemLinesDuration + chaosDuration}s`, begin: `${dotsPhaseEnd + index * perItemLinesDuration}s`, fill: "freeze" }),
|
|
165
|
+
react_1.default.createElement("animate", { attributeName: "stroke-dashoffset", from: pathLength, to: "0", dur: `${perItemLinesDuration}s`, begin: `${dotsPhaseEnd + index * perItemLinesDuration}s`, fill: "freeze", calcMode: "spline", keySplines: "0.4 0 0.2 1", keyTimes: "0;1" }))),
|
|
166
|
+
!isFragmented && fadeAfterAssembly && (react_1.default.createElement("animate", { attributeName: "opacity", from: "1", to: fadeOpacity, dur: "0.5s", begin: `${flowBeginTime}s`, fill: "freeze" }))),
|
|
167
|
+
isFragmented && (react_1.default.createElement("animateTransform", { attributeName: "transform", type: "translate", from: `${offset.x} ${offset.y}`, to: "0 0", dur: `${perItemAssemblyDuration}s`, begin: `${linesPhaseEnd + index * perItemAssemblyDuration}s`, fill: "freeze", calcMode: "spline", keySplines: "0.33 0 0.2 1", keyTimes: "0;1" }))));
|
|
168
|
+
})),
|
|
169
|
+
react_1.default.createElement("g", { className: "text-particles", filter: showGlow ? `url(#glow-${uniqueId})` : undefined }, resolvedPaths.flatMap((path, pathIndex) => {
|
|
170
|
+
return Array.from({ length: particlesPerPath }).map((_, particleIndex) => {
|
|
171
|
+
const particleDelay = (particleIndex / particlesPerPath) * flowDuration;
|
|
172
|
+
const beginTime = flowBeginTime + particleDelay;
|
|
173
|
+
return (react_1.default.createElement("circle", { key: `particle-${pathIndex}-${particleIndex}`, r: particleRadius, fill: `url(#particleGradient-${uniqueId})`, opacity: "0" },
|
|
174
|
+
react_1.default.createElement("animateMotion", { dur: `${flowDuration}s`, begin: `${beginTime}s`, fill: "freeze", path: path.d }),
|
|
175
|
+
react_1.default.createElement("animate", { attributeName: "opacity", values: "0;1;1;0", keyTimes: "0;0.1;0.9;1", dur: `${flowDuration}s`, begin: `${beginTime}s`, fill: "freeze" })));
|
|
176
|
+
});
|
|
177
|
+
}))));
|
|
178
|
+
};
|
|
179
|
+
exports.TextReveal = TextReveal;
|
|
180
|
+
/**
|
|
181
|
+
* Offset all coordinates in a path by the given amounts
|
|
182
|
+
*/
|
|
183
|
+
function offsetPath(d, offsetX, offsetY) {
|
|
184
|
+
let coordIndex = 0;
|
|
185
|
+
return d.replace(/-?\d+\.?\d*/g, (match) => {
|
|
186
|
+
const num = parseFloat(match);
|
|
187
|
+
const isY = coordIndex % 2 === 1;
|
|
188
|
+
coordIndex++;
|
|
189
|
+
if (isY) {
|
|
190
|
+
return String(num + offsetY);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
return String(num + offsetX);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}
|