@pixerate/flair 0.1.1 → 0.3.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/CHANGELOG.md +21 -0
- package/README.md +65 -14
- package/dist/catalog/index.d.ts +5 -4
- package/dist/catalog/index.js +12 -4
- package/dist/{chunk-JWZCPMFG.js → chunk-D7NLHC7K.js} +117 -674
- package/dist/chunk-D7NLHC7K.js.map +1 -0
- package/dist/{chunk-3FAUZOGL.js → chunk-FIJOMGPG.js} +2 -2
- package/dist/{chunk-UQQNZSYL.js → chunk-GTIAOYOK.js} +211 -7
- package/dist/chunk-GTIAOYOK.js.map +1 -0
- package/dist/{chunk-MWVTQFV5.js → chunk-IWKM5FJK.js} +410 -59
- package/dist/chunk-IWKM5FJK.js.map +1 -0
- package/dist/{chunk-HKXOHRCD.js → chunk-VA74HBPK.js} +2 -2
- package/dist/chunk-WAMA45G3.js +998 -0
- package/dist/chunk-WAMA45G3.js.map +1 -0
- package/dist/{chunk-J55UBNAT.js → chunk-WC3VADZC.js} +42 -36
- package/dist/chunk-WC3VADZC.js.map +1 -0
- package/dist/cli.js +12 -11
- package/dist/cli.js.map +1 -1
- package/dist/{controls-DdXMbovi.d.ts → controls-Syr6drzO.d.ts} +4 -3
- package/dist/editor/index.d.ts +101 -49
- package/dist/editor/index.js +198 -99
- package/dist/editor/index.js.map +1 -1
- package/dist/{export-FEBPEEO7.js → export-DH2HMACO.js} +4 -3
- package/dist/index.d.ts +193 -17
- package/dist/index.js +69 -33
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +2 -2
- package/dist/recorder-KEX3U35D.js +10 -0
- package/dist/{renderer-Oyynz-fs.d.ts → renderer-mzvhKzKa.d.ts} +1 -1
- package/dist/types-CsFUZZIv.d.ts +102 -0
- package/package.json +3 -3
- package/dist/chunk-36DTNSHU.js +0 -160
- package/dist/chunk-36DTNSHU.js.map +0 -1
- package/dist/chunk-J55UBNAT.js.map +0 -1
- package/dist/chunk-JWZCPMFG.js.map +0 -1
- package/dist/chunk-MWVTQFV5.js.map +0 -1
- package/dist/chunk-UQQNZSYL.js.map +0 -1
- package/dist/recorder-WJTWMEQN.js +0 -10
- package/dist/types-CkKkavvB.d.ts +0 -71
- /package/dist/{chunk-3FAUZOGL.js.map → chunk-FIJOMGPG.js.map} +0 -0
- /package/dist/{chunk-HKXOHRCD.js.map → chunk-VA74HBPK.js.map} +0 -0
- /package/dist/{export-FEBPEEO7.js.map → export-DH2HMACO.js.map} +0 -0
- /package/dist/{recorder-WJTWMEQN.js.map → recorder-KEX3U35D.js.map} +0 -0
|
@@ -2,11 +2,13 @@ import {
|
|
|
2
2
|
getTransitionGLSL
|
|
3
3
|
} from "./chunk-EHDJ3CPJ.js";
|
|
4
4
|
import {
|
|
5
|
+
checkControlValue,
|
|
6
|
+
controlFromDeclaration,
|
|
5
7
|
controlValue,
|
|
6
8
|
hexToRgb,
|
|
7
9
|
parseControls,
|
|
8
10
|
rgbToHex
|
|
9
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-WC3VADZC.js";
|
|
10
12
|
|
|
11
13
|
// src/catalog/glsl.ts
|
|
12
14
|
var HASH = `
|
|
@@ -806,6 +808,60 @@ uniform float uSpin; // @control min=0 max=4 default=1 label="Spin"`,
|
|
|
806
808
|
}`
|
|
807
809
|
)
|
|
808
810
|
};
|
|
811
|
+
var lightRibbons = {
|
|
812
|
+
kind: "shader",
|
|
813
|
+
id: "light-ribbons",
|
|
814
|
+
name: "Light ribbons",
|
|
815
|
+
description: "A bundle of thin iridescent light strands weaving across a dark frame.",
|
|
816
|
+
category: "light",
|
|
817
|
+
tags: ["background", "hero", "tech", "iridescent", "flow"],
|
|
818
|
+
source: glsl(
|
|
819
|
+
`// Light ribbons: thin glowing strands weaving across the frame, shading from one colour to the next.
|
|
820
|
+
uniform vec3 uBackground; // @control color default=#05060D label="Background"
|
|
821
|
+
uniform vec3 uColorA; // @control color default=#4FD1FF label="Colour A"
|
|
822
|
+
uniform vec3 uColorB; // @control color default=#B06BFF label="Colour B"
|
|
823
|
+
uniform vec3 uColorC; // @control color default=#FF7AC6 label="Colour C"
|
|
824
|
+
uniform float uCount; // @control min=2 max=24 default=12 step=1 label="Strands"
|
|
825
|
+
uniform float uSpread; // @control min=0.02 max=0.6 default=0.18 label="Spread"
|
|
826
|
+
uniform float uWave; // @control min=0 max=0.5 default=0.22 label="Wave"
|
|
827
|
+
uniform float uTilt; // @control min=-1 max=1 default=0.3 label="Tilt"
|
|
828
|
+
uniform float uSpeed; // @control min=0 max=2 default=0.5 label="Speed"
|
|
829
|
+
uniform float uGlow; // @control min=0 max=2 default=1 label="Glow"`,
|
|
830
|
+
`// Three colours round a loop, so the shading drifts without a seam.
|
|
831
|
+
vec3 shade(float g) {
|
|
832
|
+
g = fract(g) * 3.0;
|
|
833
|
+
return g < 1.0 ? mix(uColorA, uColorB, g) : g < 2.0 ? mix(uColorB, uColorC, g - 1.0) : mix(uColorC, uColorA, g - 2.0);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
837
|
+
vec2 p = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
|
|
838
|
+
float px = 1.0 / iResolution.y;
|
|
839
|
+
float t = iTime * uSpeed;
|
|
840
|
+
float halfWidth = 0.5 * iResolution.x / iResolution.y;
|
|
841
|
+
float x = p.x;
|
|
842
|
+
float y = p.y - x * uTilt;
|
|
843
|
+
// The bundle pinches and opens along its length, like ribbons twisting.
|
|
844
|
+
float pinch = 0.35 + 0.65 * abs(sin(x * 1.3 - t * 0.7));
|
|
845
|
+
vec3 light = vec3(0.0);
|
|
846
|
+
for (int i = 0; i < 24; i++) {
|
|
847
|
+
if (float(i) >= uCount) break;
|
|
848
|
+
float f = float(i) / max(uCount - 1.0, 1.0) - 0.5;
|
|
849
|
+
float centre = f * uSpread * pinch
|
|
850
|
+
+ uWave * (0.6 * sin(x * 1.7 + t + f * 2.0) + 0.3 * sin(x * 3.3 - t * 1.3 + f * 5.0));
|
|
851
|
+
float d = abs(y - centre);
|
|
852
|
+
float core = 1.0 - smoothstep(0.0, px * 1.5, d);
|
|
853
|
+
float glow = uGlow * 0.006 / (d + 0.006);
|
|
854
|
+
light += shade(f * 0.6 + x * 0.12 + t * 0.05) * (core * 0.9 + glow * 0.4);
|
|
855
|
+
}
|
|
856
|
+
light *= 1.0 - 0.75 * pow(min(abs(x) / halfWidth, 1.0), 2.0);
|
|
857
|
+
fragColor = vec4(uBackground + (1.0 - exp(-light)), 1.0);
|
|
858
|
+
}`
|
|
859
|
+
),
|
|
860
|
+
presets: [
|
|
861
|
+
{ name: "Gold", values: { uBackground: [0.03, 0.03, 0.04], uColorA: [1, 0.78, 0.4], uColorB: [0.75, 0.5, 0.2], uColorC: [1, 0.93, 0.75], uCount: 8, uSpread: 0.4 } },
|
|
862
|
+
{ name: "Aurora", values: { uColorA: [0.2, 1, 0.7], uColorB: [0.2, 0.6, 1], uColorC: [0.6, 0.3, 1], uTilt: -0.2, uWave: 0.3 } }
|
|
863
|
+
]
|
|
864
|
+
};
|
|
809
865
|
|
|
810
866
|
// src/catalog/shaders/organic.ts
|
|
811
867
|
var marble = {
|
|
@@ -1234,6 +1290,119 @@ uniform vec3 uTint; // @control color default=#FFFFFF label="Tint"`,
|
|
|
1234
1290
|
}`
|
|
1235
1291
|
)
|
|
1236
1292
|
};
|
|
1293
|
+
var neonCircuit = {
|
|
1294
|
+
kind: "shader",
|
|
1295
|
+
id: "neon-circuit",
|
|
1296
|
+
name: "Neon circuit",
|
|
1297
|
+
description: "Glowing neon traces routed up a circuit board, with pulses of light racing along them.",
|
|
1298
|
+
category: "pattern",
|
|
1299
|
+
tags: ["background", "neon", "circuit", "tech", "80s", "vaporwave"],
|
|
1300
|
+
source: glsl(
|
|
1301
|
+
`// Neon circuit: glowing traces climbing a board in rounded jogs, with pulses running along them.
|
|
1302
|
+
uniform vec3 uBackground; // @control color default=#170A2E label="Background"
|
|
1303
|
+
uniform vec3 uColorA; // @control color default=#FF5FA2 label="Trace A"
|
|
1304
|
+
uniform vec3 uColorB; // @control color default=#43E5FF label="Trace B"
|
|
1305
|
+
uniform vec3 uColorC; // @control color default=#FFD23F label="Trace C"
|
|
1306
|
+
uniform vec3 uColorD; // @control color default=#9D7BFF label="Trace D"
|
|
1307
|
+
uniform float uLanes; // @control min=4 max=40 default=14 label="Lanes"
|
|
1308
|
+
uniform float uDensity; // @control min=0.1 max=1 default=0.7 label="Density"
|
|
1309
|
+
uniform float uWidth; // @control min=0.04 max=0.4 default=0.16 label="Line width"
|
|
1310
|
+
uniform float uGlow; // @control min=0 max=2 default=1 label="Glow"
|
|
1311
|
+
uniform float uSpeed; // @control min=0 max=3 default=1 label="Pulse speed"
|
|
1312
|
+
uniform float uPulses; // @control min=0 max=1 default=0.5 label="Pulses"
|
|
1313
|
+
uniform float uScroll; // @control min=-2 max=2 default=0.4 label="Scroll"
|
|
1314
|
+
uniform float uGrid; // @control min=0 max=1 default=0.3 label="Grid"`,
|
|
1315
|
+
HASH,
|
|
1316
|
+
`// Each trace k owns lanes 3k..3k+2 and climbs in runs of RUN lanes, jogging
|
|
1317
|
+
// sideways between them on rounded corners, so traces never cross.
|
|
1318
|
+
const float RUN = 2.5;
|
|
1319
|
+
const float RADIUS = 0.35;
|
|
1320
|
+
|
|
1321
|
+
float laneOf(float k, float j) {
|
|
1322
|
+
return 3.0 * k + floor(hash12(vec2(k, j) + 0.37) * 3.0) + 0.5;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
vec3 traceColor(float k) {
|
|
1326
|
+
float h = hash12(vec2(k, 7.3));
|
|
1327
|
+
return h < 0.25 ? uColorA : h < 0.5 ? uColorB : h < 0.75 ? uColorC : uColorD;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// Distance to a quarter circle of RADIUS round c, in the quadrant whose signs are q.
|
|
1331
|
+
float arcDist(vec2 p, vec2 c, vec2 q) {
|
|
1332
|
+
vec2 d = p - c;
|
|
1333
|
+
return d.x * q.x >= 0.0 && d.y * q.y >= 0.0 ? abs(length(d) - RADIUS) : 1e5;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// Trace k's run j: distance from p to its climb (x) and how far up it p is (y), then
|
|
1337
|
+
// the same for its jog into run j + 1 (z, w), measured along the run.
|
|
1338
|
+
vec4 run(vec2 p, float k, float j) {
|
|
1339
|
+
float x0 = laneOf(k, j - 1.0);
|
|
1340
|
+
float x1 = laneOf(k, j);
|
|
1341
|
+
float x2 = laneOf(k, j + 1.0);
|
|
1342
|
+
float y0 = j * RUN;
|
|
1343
|
+
float y1 = y0 + RUN;
|
|
1344
|
+
float lo = y0 + (x0 == x1 ? 0.0 : RADIUS);
|
|
1345
|
+
float hi = y1 - (x2 == x1 ? 0.0 : RADIUS);
|
|
1346
|
+
vec4 r = vec4(length(p - vec2(x1, clamp(p.y, lo, hi))), p.y - y0, 1e5, 0.0);
|
|
1347
|
+
if (x2 != x1) {
|
|
1348
|
+
float sx = sign(x2 - x1);
|
|
1349
|
+
float h = length(p - vec2(clamp(p.x, min(x1, x2) + RADIUS, max(x1, x2) - RADIUS), y1));
|
|
1350
|
+
h = min(h, arcDist(p, vec2(x1 + sx * RADIUS, y1 - RADIUS), vec2(-sx, 1.0)));
|
|
1351
|
+
h = min(h, arcDist(p, vec2(x2 - sx * RADIUS, y1 + RADIUS), vec2(sx, -1.0)));
|
|
1352
|
+
r.zw = vec2(h, RUN + abs(p.x - x1));
|
|
1353
|
+
}
|
|
1354
|
+
return r;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// How bright a pulse is at s along run j of trace k: a bright head with a fading tail.
|
|
1358
|
+
// A pulse crosses one run per cycle and carries on into the next with the next cycle.
|
|
1359
|
+
float pulseAt(float k, float j, float s) {
|
|
1360
|
+
float cycle = iTime * uSpeed * 1.5 + hash12(vec2(k, 8.8));
|
|
1361
|
+
float behind = fract(cycle) * (RUN + abs(laneOf(k, j + 1.0) - laneOf(k, j))) - s;
|
|
1362
|
+
float lit = step(hash12(vec2(k, floor(cycle) - j)), uPulses);
|
|
1363
|
+
return lit * (behind >= 0.0 ? exp(-behind * 0.8) : exp(behind * 30.0));
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
|
|
1367
|
+
float px = uLanes / iResolution.y;
|
|
1368
|
+
vec2 p = fragCoord * px;
|
|
1369
|
+
p.y += iTime * uScroll;
|
|
1370
|
+
|
|
1371
|
+
vec2 g = abs(fract(p / 1.5) - 0.5);
|
|
1372
|
+
float grid = 1.0 - smoothstep(0.0, px * 1.5, (0.5 - max(g.x, g.y)) * 1.5);
|
|
1373
|
+
vec3 light = uColorD * 0.12 * uGrid * grid;
|
|
1374
|
+
|
|
1375
|
+
float home = floor(p.x / 3.0);
|
|
1376
|
+
for (int i = -1; i <= 1; i++) {
|
|
1377
|
+
float k = home + float(i);
|
|
1378
|
+
if (hash12(vec2(k, 1.9)) > uDensity) continue;
|
|
1379
|
+
vec2 q = vec2(p.x, p.y + hash12(vec2(k, 4.1)) * RUN);
|
|
1380
|
+
float jq = floor(q.y / RUN);
|
|
1381
|
+
float w = uWidth * 0.5;
|
|
1382
|
+
// The trace's nearest point lights it; each piece's pulse lights only round that piece.
|
|
1383
|
+
float d = 1e5;
|
|
1384
|
+
float pulse = 0.0;
|
|
1385
|
+
for (int b = -1; b <= 1; b++) {
|
|
1386
|
+
float j = jq + float(b);
|
|
1387
|
+
vec4 r = run(q, k, j);
|
|
1388
|
+
d = min(d, min(r.x, r.z));
|
|
1389
|
+
pulse = max(pulse, pulseAt(k, j, r.y) * exp(-r.x / uWidth));
|
|
1390
|
+
if (r.z < 1e4) pulse = max(pulse, pulseAt(k, j, r.w) * exp(-r.z / uWidth));
|
|
1391
|
+
}
|
|
1392
|
+
float core = 1.0 - smoothstep(w - px, w + px, d);
|
|
1393
|
+
float glow = uGlow * (0.5 * exp(-d / uWidth) + 0.2 * exp(-d / (uWidth * 5.0)));
|
|
1394
|
+
vec3 c = traceColor(k);
|
|
1395
|
+
light += c * (core * 0.8 + glow + uGlow * pulse) + mix(c, vec3(1.0), 0.7) * core * pulse * 3.0;
|
|
1396
|
+
}
|
|
1397
|
+
fragColor = vec4(uBackground + (1.0 - exp(-light * 1.4)), 1.0);
|
|
1398
|
+
}`
|
|
1399
|
+
),
|
|
1400
|
+
presets: [
|
|
1401
|
+
{ name: "Mainframe", values: { uBackground: [0, 0.04, 0.02], uColorA: [0.2, 1, 0.45], uColorB: [0.1, 0.75, 0.35], uColorC: [0.6, 1, 0.7], uColorD: [0.1, 0.5, 0.25] } },
|
|
1402
|
+
{ name: "Blueprint", values: { uBackground: [0.02, 0.06, 0.16], uColorA: [0.35, 0.75, 1], uColorB: [0.6, 0.9, 1], uColorC: [1, 1, 1], uColorD: [0.25, 0.45, 0.9], uGlow: 0.6, uPulses: 0.3 } },
|
|
1403
|
+
{ name: "Dense", values: { uLanes: 26, uDensity: 1, uWidth: 0.12, uPulses: 0.8 } }
|
|
1404
|
+
]
|
|
1405
|
+
};
|
|
1237
1406
|
|
|
1238
1407
|
// src/catalog/shaders/retro.ts
|
|
1239
1408
|
var synthwaveGrid = {
|
|
@@ -1393,11 +1562,58 @@ var EFFECT_CATEGORIES = [
|
|
|
1393
1562
|
"data",
|
|
1394
1563
|
"icon"
|
|
1395
1564
|
];
|
|
1565
|
+
var VECTOR_CONTROL_TYPES = ["float", "int", "bool", "color", "text"];
|
|
1566
|
+
var PLACEHOLDER = /\{\{\s*([A-Za-z_]\w*)\s*(?::\s*([A-Za-z0-9]*)([^}]*))?\}\}/g;
|
|
1567
|
+
function motionStrings(motion) {
|
|
1568
|
+
const out = [];
|
|
1569
|
+
const walk = (v) => {
|
|
1570
|
+
if (typeof v === "string") out.push(v);
|
|
1571
|
+
else if (Array.isArray(v)) v.forEach(walk);
|
|
1572
|
+
else if (v && typeof v === "object") Object.values(v).forEach(walk);
|
|
1573
|
+
};
|
|
1574
|
+
walk(motion);
|
|
1575
|
+
return out;
|
|
1576
|
+
}
|
|
1577
|
+
function vectorControls(motion) {
|
|
1578
|
+
const controls = [];
|
|
1579
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1580
|
+
for (const text of motionStrings(motion)) {
|
|
1581
|
+
for (const [, name, type, attributes] of text.matchAll(PLACEHOLDER)) {
|
|
1582
|
+
if (type === void 0 || seen.has(name) || !VECTOR_CONTROL_TYPES.includes(type)) continue;
|
|
1583
|
+
seen.add(name);
|
|
1584
|
+
const colour = type === "color";
|
|
1585
|
+
controls.push(controlFromDeclaration(name, colour ? "vec3" : type, `${attributes}${colour ? " color" : ""}`, controls.length + 1));
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
return controls;
|
|
1589
|
+
}
|
|
1590
|
+
function vectorEffectControls(effect) {
|
|
1591
|
+
const declared = vectorControls(effect.motion);
|
|
1592
|
+
const names = new Set(declared.map((c) => c.name));
|
|
1593
|
+
const listed = (effect.controls ?? []).filter((c) => !names.has(c.name));
|
|
1594
|
+
return [...declared, ...listed.map((c, i) => vectorControlDef(c, declared.length + i + 1))];
|
|
1595
|
+
}
|
|
1596
|
+
function vectorControlIssues(motion) {
|
|
1597
|
+
const issues = [];
|
|
1598
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
1599
|
+
for (const text of motionStrings(motion)) {
|
|
1600
|
+
for (const [whole, name, type] of text.matchAll(PLACEHOLDER)) {
|
|
1601
|
+
if (type === void 0) continue;
|
|
1602
|
+
if (!VECTOR_CONTROL_TYPES.includes(type))
|
|
1603
|
+
issues.push(`${whole}: a control's type is one of ${VECTOR_CONTROL_TYPES.join(", ")}`);
|
|
1604
|
+
else if (declarations.has(name) && declarations.get(name) !== whole)
|
|
1605
|
+
issues.push(`"${name}" is declared twice; declare it once and write {{${name}}} everywhere else`);
|
|
1606
|
+
else declarations.set(name, whole);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return issues;
|
|
1610
|
+
}
|
|
1396
1611
|
function written(control, value) {
|
|
1397
1612
|
if (control.color && Array.isArray(value)) return rgbToHex(value);
|
|
1398
1613
|
if (Array.isArray(value)) return value.join(" ");
|
|
1399
1614
|
return value;
|
|
1400
1615
|
}
|
|
1616
|
+
var escapeMarkup = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1401
1617
|
function vectorControlDef(control, line = 0) {
|
|
1402
1618
|
const min = control.min ?? 0;
|
|
1403
1619
|
return {
|
|
@@ -1415,18 +1631,30 @@ function vectorControlDef(control, line = 0) {
|
|
|
1415
1631
|
function renderVectorEffect(effect, values = {}) {
|
|
1416
1632
|
const filled = {};
|
|
1417
1633
|
const placeholders = /* @__PURE__ */ new Map();
|
|
1418
|
-
for (const control of effect
|
|
1419
|
-
|
|
1634
|
+
for (const control of vectorEffectControls(effect)) {
|
|
1635
|
+
const given = values[control.name];
|
|
1636
|
+
const check = given === void 0 ? void 0 : checkControlValue(control, given);
|
|
1637
|
+
filled[control.name] = check?.ok ? check.value : control.default;
|
|
1420
1638
|
placeholders.set(control.name, written(control, filled[control.name]));
|
|
1421
1639
|
}
|
|
1422
1640
|
for (const [name, value] of Object.entries(effect.derived?.(filled) ?? {})) placeholders.set(name, value);
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1641
|
+
const fill = (text, markup) => {
|
|
1642
|
+
const whole = /^\{\{\s*([A-Za-z_]\w*)\s*(?::[^}]*)?\}\}$/.exec(text);
|
|
1643
|
+
if (!markup && whole && placeholders.has(whole[1])) return placeholders.get(whole[1]);
|
|
1644
|
+
return text.replace(PLACEHOLDER, (match, name) => {
|
|
1645
|
+
if (!placeholders.has(name)) return match;
|
|
1646
|
+
const value = String(placeholders.get(name));
|
|
1647
|
+
return markup ? escapeMarkup(value) : value;
|
|
1648
|
+
});
|
|
1649
|
+
};
|
|
1650
|
+
const walk = (v) => {
|
|
1651
|
+
if (typeof v === "string") return fill(v, false);
|
|
1652
|
+
if (Array.isArray(v)) return v.map(walk);
|
|
1653
|
+
if (v && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));
|
|
1654
|
+
return v;
|
|
1655
|
+
};
|
|
1656
|
+
const { svg, ...rest } = effect.motion;
|
|
1657
|
+
return { svg: fill(svg.replace(/<!--[\s\S]*?-->/g, ""), true), ...walk(rest) };
|
|
1430
1658
|
}
|
|
1431
1659
|
function colorControl(name, hex, label) {
|
|
1432
1660
|
return { name, type: "vec3", color: true, default: hexToRgb(hex) ?? [1, 1, 1], label: label ?? name };
|
|
@@ -1444,9 +1672,8 @@ var spinner = {
|
|
|
1444
1672
|
category: "loader",
|
|
1445
1673
|
tags: ["loader", "ui", "minimal"],
|
|
1446
1674
|
hold: 0,
|
|
1447
|
-
controls: [colorControl("accent", "#6366F1", "Arc"), colorControl("track", "#E5E7EB", "Track"), numberControl("width", 8, 2, 20, "Stroke width", 1)],
|
|
1448
1675
|
motion: {
|
|
1449
|
-
svg: `<svg viewBox="0 0 100 100"
|
|
1676
|
+
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#6366F1 label='Arc'}} {{track: color default=#E5E7EB label='Track'}} {{width: float min=2 max=20 default=8 step=1 label='Stroke width'}} --><circle cx="50" cy="50" r="38" fill="none" stroke="{{track}}" stroke-width="{{width}}"/><g id="spin" transform="rotate(0 50 50)"><circle id="arc" cx="50" cy="50" r="38" fill="none" stroke="{{accent}}" stroke-width="{{width}}" stroke-linecap="round" pathLength="1" stroke-dasharray="0.25 1"/></g></svg>`,
|
|
1450
1677
|
animate: [
|
|
1451
1678
|
{ id: "spin", attrs: { transform: ["rotate(0 50 50)", "rotate(720 50 50)"] }, ms: 1600, ease: "linear" },
|
|
1452
1679
|
{ id: "arc", attrs: { "stroke-dasharray": ["0.08 1", "0.6 1"] }, ms: 800, ease: "in-out" },
|
|
@@ -1462,9 +1689,8 @@ var bouncingDots = {
|
|
|
1462
1689
|
category: "loader",
|
|
1463
1690
|
tags: ["loader", "chat", "typing", "ui"],
|
|
1464
1691
|
hold: 0,
|
|
1465
|
-
controls: [colorControl("accent", "#0EA5E9", "Dots"), numberControl("height", 18, 4, 30, "Bounce height", 1)],
|
|
1466
1692
|
motion: {
|
|
1467
|
-
svg: `<svg viewBox="0 0 120 60"
|
|
1693
|
+
svg: `<svg viewBox="0 0 120 60"><!-- {{accent: color default=#0EA5E9 label='Dots'}} {{height: float min=4 max=30 default=18 step=1 label='Bounce height'}} --><circle id="a" cx="30" cy="40" r="9" fill="{{accent}}"/><circle id="b" cx="60" cy="40" r="9" fill="{{accent}}"/><circle id="c" cx="90" cy="40" r="9" fill="{{accent}}"/></svg>`,
|
|
1468
1694
|
animate: ["a", "b", "c"].flatMap((id, i) => [
|
|
1469
1695
|
{ id, attrs: { transform: ["translate(0 0)", "translate(0 -{{height}})"] }, delay: i * 150, ms: 250, ease: "out" },
|
|
1470
1696
|
{ id, attrs: { transform: ["translate(0 -{{height}})", "translate(0 0)"] }, delay: i * 150 + 250, ms: 250, ease: "in-out" },
|
|
@@ -1480,15 +1706,9 @@ var progressRing = {
|
|
|
1480
1706
|
description: "A ring filling up to a percentage while the number counts with it.",
|
|
1481
1707
|
category: "data",
|
|
1482
1708
|
tags: ["progress", "kpi", "dashboard", "loader"],
|
|
1483
|
-
controls: [
|
|
1484
|
-
colorControl("accent", "#22C55E", "Ring"),
|
|
1485
|
-
colorControl("track", "#E5E7EB", "Track"),
|
|
1486
|
-
colorControl("text", "#111827", "Text"),
|
|
1487
|
-
numberControl("value", 72, 0, 100, "Percent", 1)
|
|
1488
|
-
],
|
|
1489
1709
|
derived: (v) => ({ fraction: Math.min(1, Math.max(0, Number(v.value) / 100)) }),
|
|
1490
1710
|
motion: {
|
|
1491
|
-
svg: `<svg viewBox="0 0 120 120" font-family="system-ui, sans-serif"
|
|
1711
|
+
svg: `<svg viewBox="0 0 120 120" font-family="system-ui, sans-serif"><!-- {{accent: color default=#22C55E label='Ring'}} {{track: color default=#E5E7EB label='Track'}} {{text: color default=#111827 label='Text'}} {{value: float min=0 max=100 default=72 step=1 label='Percent'}} --><circle cx="60" cy="60" r="48" fill="none" stroke="{{track}}" stroke-width="10"/><circle id="ring" cx="60" cy="60" r="48" fill="none" stroke="{{accent}}" stroke-width="10" stroke-linecap="round" transform="rotate(-90 60 60)"/><text id="label" x="60" y="68" text-anchor="middle" font-size="24" font-weight="700" fill="{{text}}">0%</text></svg>`,
|
|
1492
1712
|
animate: [
|
|
1493
1713
|
{ id: "ring", draw: [0, "{{fraction}}"], ms: 1600, ease: "out" },
|
|
1494
1714
|
{ id: "label", count: [0, "{{value}}"], format: "{n}%", ms: 1600, ease: "out" }
|
|
@@ -1503,9 +1723,8 @@ var pulsePing = {
|
|
|
1503
1723
|
category: "loader",
|
|
1504
1724
|
tags: ["live", "notification", "location", "status"],
|
|
1505
1725
|
hold: 0,
|
|
1506
|
-
controls: [colorControl("accent", "#EF4444", "Colour")],
|
|
1507
1726
|
motion: {
|
|
1508
|
-
svg: `<svg viewBox="0 0 100 100"
|
|
1727
|
+
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#EF4444 label='Colour'}} --><circle id="r1" cx="50" cy="50" r="8" fill="none" stroke="{{accent}}" stroke-width="3"/><circle id="r2" cx="50" cy="50" r="8" fill="none" stroke="{{accent}}" stroke-width="3"/><circle cx="50" cy="50" r="9" fill="{{accent}}"/></svg>`,
|
|
1509
1728
|
animate: [
|
|
1510
1729
|
{ id: "r1", attrs: { r: [8, 44], opacity: [0.9, 0], "stroke-width": [4, 1] }, ms: 1400, ease: "out" },
|
|
1511
1730
|
{ id: "r2", attrs: { r: [8, 44], opacity: [0.9, 0], "stroke-width": [4, 1] }, delay: 700, ms: 1400, ease: "out" }
|
|
@@ -1520,9 +1739,8 @@ var successCheck = {
|
|
|
1520
1739
|
category: "icon",
|
|
1521
1740
|
tags: ["success", "confirmation", "ui", "feedback"],
|
|
1522
1741
|
hold: 1200,
|
|
1523
|
-
controls: [colorControl("accent", "#16A34A", "Colour"), numberControl("width", 6, 2, 12, "Stroke width", 1)],
|
|
1524
1742
|
motion: {
|
|
1525
|
-
svg: `<svg viewBox="0 0 100 100"
|
|
1743
|
+
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#16A34A label='Colour'}} {{width: float min=2 max=12 default=6 step=1 label='Stroke width'}} --><g id="badge" transform="translate(50 50) scale(1) translate(-50 -50)"><circle id="circle" cx="50" cy="50" r="40" fill="none" stroke="{{accent}}" stroke-width="{{width}}" stroke-linecap="round" transform="rotate(-90 50 50)"/><path id="tick" d="M31 52 L44 64 L70 37" fill="none" stroke="{{accent}}" stroke-width="{{width}}" stroke-linecap="round" stroke-linejoin="round"/></g></svg>`,
|
|
1526
1744
|
animate: [
|
|
1527
1745
|
{ id: "circle", draw: true, ms: 600, ease: "in-out" },
|
|
1528
1746
|
{ id: "tick", draw: true, delay: 500, ms: 400, ease: "out" },
|
|
@@ -1539,9 +1757,8 @@ var errorCross = {
|
|
|
1539
1757
|
category: "icon",
|
|
1540
1758
|
tags: ["error", "failure", "ui", "feedback"],
|
|
1541
1759
|
hold: 1200,
|
|
1542
|
-
controls: [colorControl("accent", "#DC2626", "Colour"), numberControl("width", 6, 2, 12, "Stroke width", 1)],
|
|
1543
1760
|
motion: {
|
|
1544
|
-
svg: `<svg viewBox="0 0 100 100"
|
|
1761
|
+
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#DC2626 label='Colour'}} {{width: float min=2 max=12 default=6 step=1 label='Stroke width'}} --><g id="badge" transform="translate(0 0)"><circle id="circle" cx="50" cy="50" r="40" fill="none" stroke="{{accent}}" stroke-width="{{width}}" stroke-linecap="round" transform="rotate(-90 50 50)"/><path id="a" d="M36 36 L64 64" fill="none" stroke="{{accent}}" stroke-width="{{width}}" stroke-linecap="round"/><path id="b" d="M64 36 L36 64" fill="none" stroke="{{accent}}" stroke-width="{{width}}" stroke-linecap="round"/></g></svg>`,
|
|
1545
1762
|
animate: [
|
|
1546
1763
|
{ id: "circle", draw: true, ms: 600, ease: "in-out" },
|
|
1547
1764
|
{ id: "a", draw: true, delay: 500, ms: 250, ease: "out" },
|
|
@@ -1560,9 +1777,8 @@ var heartbeat = {
|
|
|
1560
1777
|
category: "icon",
|
|
1561
1778
|
tags: ["health", "like", "love", "pulse"],
|
|
1562
1779
|
hold: 300,
|
|
1563
|
-
controls: [colorControl("accent", "#F43F5E", "Heart"), colorControl("line", "#94A3B8", "Line")],
|
|
1564
1780
|
motion: {
|
|
1565
|
-
svg: `<svg viewBox="0 0 200 120"
|
|
1781
|
+
svg: `<svg viewBox="0 0 200 120"><!-- {{accent: color default=#F43F5E label='Heart'}} {{line: color default=#94A3B8 label='Line'}} --><g id="heart" transform="translate(100 46) scale(1)"><path d="M0 22 C-30 0 -30 -26 -10 -26 C-3 -26 0 -20 0 -16 C0 -20 3 -26 10 -26 C30 -26 30 0 0 22Z" fill="{{accent}}"/></g><path id="ecg" d="M10 100 H70 L80 86 L90 112 L100 76 L110 104 L118 100 H190" fill="none" stroke="{{line}}" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/></svg>`,
|
|
1566
1782
|
animate: [
|
|
1567
1783
|
{ id: "heart", attrs: { transform: ["translate(100 46) scale(1)", "translate(100 46) scale(1.18)"] }, ms: 120, ease: "out" },
|
|
1568
1784
|
{ id: "heart", attrs: { transform: ["translate(100 46) scale(1.18)", "translate(100 46) scale(1)"] }, delay: 120, ms: 160, ease: "in-out" },
|
|
@@ -1580,14 +1796,6 @@ var barChart = {
|
|
|
1580
1796
|
category: "data",
|
|
1581
1797
|
tags: ["chart", "dashboard", "report", "explainer"],
|
|
1582
1798
|
hold: 1500,
|
|
1583
|
-
controls: [
|
|
1584
|
-
colorControl("accent", "#6366F1", "Bars"),
|
|
1585
|
-
colorControl("text", "#334155", "Text"),
|
|
1586
|
-
numberControl("a", 40, 0, 100, "Bar 1", 1),
|
|
1587
|
-
numberControl("b", 65, 0, 100, "Bar 2", 1),
|
|
1588
|
-
numberControl("c", 50, 0, 100, "Bar 3", 1),
|
|
1589
|
-
numberControl("d", 90, 0, 100, "Bar 4", 1)
|
|
1590
|
-
],
|
|
1591
1799
|
derived: (v) => Object.fromEntries(
|
|
1592
1800
|
["a", "b", "c", "d"].flatMap((k) => {
|
|
1593
1801
|
const h = Math.min(100, Math.max(0, Number(v[k]))) * 1.1;
|
|
@@ -1598,7 +1806,7 @@ var barChart = {
|
|
|
1598
1806
|
})
|
|
1599
1807
|
),
|
|
1600
1808
|
motion: {
|
|
1601
|
-
svg: `<svg viewBox="0 0 240 160" font-family="system-ui, sans-serif"
|
|
1809
|
+
svg: `<svg viewBox="0 0 240 160" font-family="system-ui, sans-serif"><!-- {{accent: color default=#6366F1 label='Bars'}} {{text: color default=#334155 label='Text'}} {{a: float min=0 max=100 default=40 step=1 label='Bar 1'}} {{b: float min=0 max=100 default=65 step=1 label='Bar 2'}} {{c: float min=0 max=100 default=50 step=1 label='Bar 3'}} {{d: float min=0 max=100 default=90 step=1 label='Bar 4'}} --><line x1="20" y1="140" x2="220" y2="140" stroke="{{text}}" stroke-opacity="0.3" stroke-width="2"/>${["a", "b", "c", "d"].map(
|
|
1602
1810
|
(k, i) => `<g id="bar-${k}" transform="translate(${40 + i * 48} 140) scale(1 0)"><rect x="0" y="-1" width="32" height="1" rx="0.02" fill="{{accent}}"/></g><text id="label-${k}" x="${56 + i * 48}" y="132" text-anchor="middle" font-size="12" fill="{{text}}" opacity="0">0</text>`
|
|
1603
1811
|
).join("")}</svg>`,
|
|
1604
1812
|
animate: ["a", "b", "c", "d"].flatMap((k, i) => [
|
|
@@ -1616,9 +1824,8 @@ var lineChart = {
|
|
|
1616
1824
|
category: "data",
|
|
1617
1825
|
tags: ["chart", "trend", "growth", "dashboard"],
|
|
1618
1826
|
hold: 1500,
|
|
1619
|
-
controls: [colorControl("accent", "#0EA5E9", "Line"), colorControl("grid", "#CBD5E1", "Grid")],
|
|
1620
1827
|
motion: {
|
|
1621
|
-
svg: `<svg viewBox="0 0 240 140"
|
|
1828
|
+
svg: `<svg viewBox="0 0 240 140"><!-- {{accent: color default=#0EA5E9 label='Line'}} {{grid: color default=#CBD5E1 label='Grid'}} --><defs><linearGradient id="fill" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="{{accent}}" stop-opacity="0.35"/><stop offset="1" stop-color="{{accent}}" stop-opacity="0"/></linearGradient></defs><g stroke="{{grid}}" stroke-width="1"><line x1="20" y1="30" x2="220" y2="30"/><line x1="20" y1="70" x2="220" y2="70"/><line x1="20" y1="110" x2="220" y2="110"/></g><path id="area" d="M20 110 L60 92 L100 98 L140 64 L180 70 L220 30 L220 120 L20 120Z" fill="url(#fill)" opacity="0"/><path id="trend" d="M20 110 L60 92 L100 98 L140 64 L180 70 L220 30" fill="none" stroke="{{accent}}" stroke-width="3" stroke-linejoin="round" stroke-linecap="round"/><circle id="marker" r="5" fill="#fff" stroke="{{accent}}" stroke-width="3"/></svg>`,
|
|
1622
1829
|
animate: [
|
|
1623
1830
|
{ id: "trend", draw: true, ms: 1400, ease: "in-out" },
|
|
1624
1831
|
{ id: "marker", along: "trend", ms: 1400, ease: "in-out" },
|
|
@@ -1634,13 +1841,8 @@ var statCounter = {
|
|
|
1634
1841
|
category: "data",
|
|
1635
1842
|
tags: ["kpi", "number", "headline", "explainer"],
|
|
1636
1843
|
hold: 1500,
|
|
1637
|
-
controls: [
|
|
1638
|
-
colorControl("accent", "#F59E0B", "Accent"),
|
|
1639
|
-
colorControl("text", "#0F172A", "Text"),
|
|
1640
|
-
numberControl("value", 1250, 0, 1e5, "Value", 1)
|
|
1641
|
-
],
|
|
1642
1844
|
motion: {
|
|
1643
|
-
svg: `<svg viewBox="0 0 240 120" font-family="system-ui, sans-serif"
|
|
1845
|
+
svg: `<svg viewBox="0 0 240 120" font-family="system-ui, sans-serif"><!-- {{accent: color default=#F59E0B label='Accent'}} {{text: color default=#0F172A label='Text'}} {{value: float min=0 max=100000 default=1250 step=1 label='Value'}} {{caption: text default='happy customers' label='Caption'}} --><text id="number" x="120" y="62" text-anchor="middle" font-size="44" font-weight="800" fill="{{text}}">0</text><path id="underline" d="M60 78 H180" stroke="{{accent}}" stroke-width="6" stroke-linecap="round"/><text id="caption" x="120" y="104" text-anchor="middle" font-size="13" fill="{{text}}" opacity="0">{{caption}}</text></svg>`,
|
|
1644
1846
|
animate: [
|
|
1645
1847
|
{ id: "number", count: [0, "{{value}}"], ms: 1500, ease: "out" },
|
|
1646
1848
|
{ id: "underline", draw: true, delay: 300, ms: 900, ease: "in-out" },
|
|
@@ -1656,9 +1858,8 @@ var route = {
|
|
|
1656
1858
|
category: "data",
|
|
1657
1859
|
tags: ["map", "travel", "delivery", "journey"],
|
|
1658
1860
|
hold: 1200,
|
|
1659
|
-
controls: [colorControl("accent", "#8B5CF6", "Route"), colorControl("pin", "#0F172A", "Pins")],
|
|
1660
1861
|
motion: {
|
|
1661
|
-
svg: `<svg viewBox="0 0 240 150"
|
|
1862
|
+
svg: `<svg viewBox="0 0 240 150"><!-- {{accent: color default=#8B5CF6 label='Route'}} {{pin: color default=#0F172A label='Pins'}} --><path id="path" d="M30 120 C60 40 120 140 150 70 S210 30 210 30" fill="none" stroke="{{accent}}" stroke-width="4" stroke-linecap="round"/><circle cx="30" cy="120" r="7" fill="{{pin}}"/><g id="end" opacity="0"><circle cx="210" cy="30" r="9" fill="{{pin}}"/><circle cx="210" cy="30" r="3.5" fill="#fff"/></g><g id="traveller"><circle r="7" fill="{{accent}}"/><circle r="3" fill="#fff"/></g></svg>`,
|
|
1662
1863
|
animate: [
|
|
1663
1864
|
{ id: "path", draw: true, ms: 1800, ease: "in-out" },
|
|
1664
1865
|
{ id: "traveller", along: "path", ms: 1800, ease: "in-out" },
|
|
@@ -1674,7 +1875,6 @@ var blobMorph = {
|
|
|
1674
1875
|
category: "organic",
|
|
1675
1876
|
tags: ["shape", "background", "playful", "brand"],
|
|
1676
1877
|
hold: 0,
|
|
1677
|
-
controls: [colorControl("colorA", "#F472B6", "Colour A"), colorControl("colorB", "#818CF8", "Colour B")],
|
|
1678
1878
|
motion: (() => {
|
|
1679
1879
|
const shapes = [
|
|
1680
1880
|
"M100 30 C140 30 170 60 170 100 C170 140 140 170 100 170 C60 170 30 140 30 100 C30 60 60 30 100 30Z",
|
|
@@ -1683,7 +1883,7 @@ var blobMorph = {
|
|
|
1683
1883
|
];
|
|
1684
1884
|
const step = 1200;
|
|
1685
1885
|
return {
|
|
1686
|
-
svg: `<svg viewBox="0 0 200 200"
|
|
1886
|
+
svg: `<svg viewBox="0 0 200 200"><!-- {{colorA: color default=#F472B6 label='Colour A'}} {{colorB: color default=#818CF8 label='Colour B'}} --><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="{{colorA}}"/><stop offset="1" stop-color="{{colorB}}"/></linearGradient></defs><path id="blob" d="${shapes[0]}" fill="url(#g)"/></svg>`,
|
|
1687
1887
|
animate: shapes.map((d, i) => ({
|
|
1688
1888
|
id: "blob",
|
|
1689
1889
|
attrs: { d: [d, shapes[(i + 1) % shapes.length]] },
|
|
@@ -1702,9 +1902,8 @@ var signature = {
|
|
|
1702
1902
|
category: "icon",
|
|
1703
1903
|
tags: ["handwriting", "draw-on", "personal", "reveal"],
|
|
1704
1904
|
hold: 1500,
|
|
1705
|
-
controls: [colorControl("ink", "#1E293B", "Ink"), numberControl("width", 3, 1, 8, "Pen width", 0.5)],
|
|
1706
1905
|
motion: {
|
|
1707
|
-
svg: `<svg viewBox="0 0 260 120"
|
|
1906
|
+
svg: `<svg viewBox="0 0 260 120"><!-- {{ink: color default=#1E293B label='Ink'}} {{width: float min=1 max=8 default=3 step=0.5 label='Pen width'}} --><path id="sig" d="M20 80 C40 20 60 20 55 70 C52 95 70 95 85 60 C95 40 100 75 110 70 C120 65 125 40 135 55 C145 70 150 75 165 50 C175 35 185 60 190 65 C200 75 215 40 240 45" fill="none" stroke="{{ink}}" stroke-width="{{width}}" stroke-linecap="round" stroke-linejoin="round"/><path id="dash" d="M70 100 Q140 92 220 98" fill="none" stroke="{{ink}}" stroke-width="{{width}}" stroke-linecap="round"/></svg>`,
|
|
1708
1907
|
animate: [
|
|
1709
1908
|
{ id: "sig", draw: true, ms: 1800, ease: "in-out" },
|
|
1710
1909
|
{ id: "dash", draw: true, delay: 1700, ms: 400, ease: "out" }
|
|
@@ -1719,7 +1918,6 @@ var equalizer = {
|
|
|
1719
1918
|
category: "loader",
|
|
1720
1919
|
tags: ["music", "audio", "voice", "now-playing"],
|
|
1721
1920
|
hold: 0,
|
|
1722
|
-
controls: [colorControl("accent", "#10B981", "Bars")],
|
|
1723
1921
|
motion: (() => {
|
|
1724
1922
|
const heights = [
|
|
1725
1923
|
[0.3, 0.9, 0.5, 0.3],
|
|
@@ -1730,7 +1928,7 @@ var equalizer = {
|
|
|
1730
1928
|
];
|
|
1731
1929
|
const beat = 250;
|
|
1732
1930
|
return {
|
|
1733
|
-
svg: `<svg viewBox="0 0 100 100"
|
|
1931
|
+
svg: `<svg viewBox="0 0 100 100"><!-- {{accent: color default=#10B981 label='Bars'}} -->${heights.map((_, i) => `<g id="bar${i}" transform="translate(${14 + i * 16} 85) scale(1 ${heights[i][0]})"><rect x="0" y="-70" width="10" height="70" rx="5" fill="{{accent}}"/></g>`).join("")}</svg>`,
|
|
1734
1932
|
animate: heights.flatMap(
|
|
1735
1933
|
(h, i) => [0, 1, 2].map((k) => ({
|
|
1736
1934
|
id: `bar${i}`,
|
|
@@ -1751,7 +1949,6 @@ var confetti = {
|
|
|
1751
1949
|
category: "icon",
|
|
1752
1950
|
tags: ["celebration", "success", "party", "reward"],
|
|
1753
1951
|
hold: 400,
|
|
1754
|
-
controls: [colorControl("a", "#F43F5E", "Colour A"), colorControl("b", "#FACC15", "Colour B"), colorControl("c", "#22D3EE", "Colour C")],
|
|
1755
1952
|
motion: (() => {
|
|
1756
1953
|
const pieces = Array.from({ length: 18 }, (_, i) => {
|
|
1757
1954
|
const angle = i / 18 * Math.PI * 2 + i % 3 * 0.2;
|
|
@@ -1762,7 +1959,7 @@ var confetti = {
|
|
|
1762
1959
|
return { i, x, y, spin, fill: ["{{a}}", "{{b}}", "{{c}}"][i % 3] };
|
|
1763
1960
|
});
|
|
1764
1961
|
return {
|
|
1765
|
-
svg: `<svg viewBox="0 0 200 200"
|
|
1962
|
+
svg: `<svg viewBox="0 0 200 200"><!-- {{a: color default=#F43F5E label='Colour A'}} {{b: color default=#FACC15 label='Colour B'}} {{c: color default=#22D3EE label='Colour C'}} -->${pieces.map((p) => `<rect id="p${p.i}" x="-4" y="-2" width="8" height="4" rx="1" fill="${p.fill}" transform="translate(100 110) rotate(0)" opacity="0"/>`).join("")}</svg>`,
|
|
1766
1963
|
animate: pieces.flatMap((p) => [
|
|
1767
1964
|
{ id: `p${p.i}`, attrs: { opacity: [0, 1] }, ms: 50 },
|
|
1768
1965
|
{
|
|
@@ -1785,6 +1982,152 @@ var confetti = {
|
|
|
1785
1982
|
};
|
|
1786
1983
|
})()
|
|
1787
1984
|
};
|
|
1985
|
+
var NODES = {
|
|
1986
|
+
s: [40, 120],
|
|
1987
|
+
a: [120, 60],
|
|
1988
|
+
b: [120, 180],
|
|
1989
|
+
c: [280, 50],
|
|
1990
|
+
d: [290, 130],
|
|
1991
|
+
e: [280, 200],
|
|
1992
|
+
t: [360, 120]
|
|
1993
|
+
};
|
|
1994
|
+
var HUB = [200, 120];
|
|
1995
|
+
var link = ([x1, y1], [x2, y2]) => {
|
|
1996
|
+
const mid = (x1 + x2) / 2;
|
|
1997
|
+
return `M${x1} ${y1} C${mid} ${y1} ${mid} ${y2} ${x2} ${y2}`;
|
|
1998
|
+
};
|
|
1999
|
+
var LINKS = [
|
|
2000
|
+
{ from: NODES.s, to: NODES.a, column: 0 },
|
|
2001
|
+
{ from: NODES.s, to: NODES.b, column: 0 },
|
|
2002
|
+
{ from: NODES.a, to: HUB, column: 1 },
|
|
2003
|
+
{ from: NODES.b, to: HUB, column: 1 },
|
|
2004
|
+
{ from: HUB, to: NODES.c, column: 2 },
|
|
2005
|
+
{ from: HUB, to: NODES.d, column: 2 },
|
|
2006
|
+
{ from: HUB, to: NODES.e, column: 2 },
|
|
2007
|
+
{ from: NODES.c, to: NODES.t, column: 3 },
|
|
2008
|
+
{ from: NODES.d, to: NODES.t, column: 3 },
|
|
2009
|
+
{ from: NODES.e, to: NODES.t, column: 3 }
|
|
2010
|
+
];
|
|
2011
|
+
var CRITICAL = [NODES.s, NODES.b, HUB, NODES.c, NODES.t].map((p, i, all) => i === 0 ? "" : link(all[i - 1], p).replace(/^M[\d.]+ [\d.]+ /, i === 1 ? `M${all[0][0]} ${all[0][1]} ` : "")).filter(Boolean).join(" ");
|
|
2012
|
+
var NODE_COLUMN = { s: 0, a: 1, b: 1, c: 3, d: 3, e: 3, t: 4 };
|
|
2013
|
+
var COLUMN_MS = 280;
|
|
2014
|
+
var PATH_START = 1500;
|
|
2015
|
+
var PATH_MS = 1800;
|
|
2016
|
+
var flowNetwork = {
|
|
2017
|
+
kind: "vector",
|
|
2018
|
+
id: "flow-network",
|
|
2019
|
+
name: "Flow network",
|
|
2020
|
+
description: "A node graph linking up, then a glowing critical path racing through it to a pulsing hub.",
|
|
2021
|
+
category: "data",
|
|
2022
|
+
tags: ["network", "graph", "dag", "workflow", "tech", "explainer"],
|
|
2023
|
+
hold: 1200,
|
|
2024
|
+
presets: [
|
|
2025
|
+
{ name: "Light", values: { background: [0.93, 0.95, 0.96], link: [0.37, 0.72, 0.69], path: [0.96, 0.62, 0.04], ink: [0.2, 0.25, 0.33] } },
|
|
2026
|
+
{ name: "Neon", values: { background: [0.04, 0.02, 0.1], link: [0.13, 0.83, 0.93], path: [0.96, 0.45, 0.71], ink: [0.85, 0.85, 1] } }
|
|
2027
|
+
],
|
|
2028
|
+
motion: {
|
|
2029
|
+
svg: `<svg viewBox="0 0 400 240" font-family="system-ui, sans-serif"><!-- {{label: text default='Critical path' label='Hub label'}} {{background: color default=#0B1220 label='Background'}} {{link: color default=#4FD1C5 label='Links'}} {{path: color default=#F5A524 label='Critical path'}} {{ink: color default=#CBD5E1 label='Label'}} {{glow: float min=0 max=6 default=2.5 step=0.5 label='Glow'}} --><defs><filter id="glow" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="{{glow}}" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><rect width="400" height="240" fill="{{background}}"/>${LINKS.map(
|
|
2030
|
+
(l, i) => `<path id="l${i}" d="${link(l.from, l.to)}" fill="none" stroke="{{link}}" stroke-width="1.6" stroke-opacity="0.75" filter="url(#glow)"/>`
|
|
2031
|
+
).join("")}<path id="crit" d="${CRITICAL}" fill="none" stroke="{{path}}" stroke-width="3" stroke-linecap="round" filter="url(#glow)"/>${[0, 1].map((i) => `<circle id="ring${i}" cx="${HUB[0]}" cy="${HUB[1]}" r="10" fill="none" stroke="{{link}}" stroke-width="2" opacity="0"/>`).join("")}${Object.entries(NODES).map(([id, [x, y]]) => `<circle id="n${id}" cx="${x}" cy="${y}" r="6" fill="{{background}}" stroke="{{link}}" stroke-width="2.5" filter="url(#glow)" opacity="0"/>`).join("")}<g id="hub" opacity="0"><circle cx="${HUB[0]}" cy="${HUB[1]}" r="13" fill="{{link}}" fill-opacity="0.25" stroke="{{link}}" stroke-width="2" filter="url(#glow)"/><circle cx="${HUB[0]}" cy="${HUB[1]}" r="6" fill="{{link}}"/></g><text id="caption" x="${HUB[0]}" y="${HUB[1] + 34}" text-anchor="middle" font-size="12" font-weight="600" fill="{{ink}}" opacity="0">{{label}}</text>${[0, 1, 2].map((i) => `<circle id="pkt${i}" r="2.5" fill="{{link}}" filter="url(#glow)" opacity="0"/>`).join("")}<circle id="comet" r="5" fill="#FFFFFF" stroke="{{path}}" stroke-width="2" filter="url(#glow)" opacity="0"/></svg>`,
|
|
2032
|
+
animate: [
|
|
2033
|
+
...LINKS.map((l, i) => ({ id: `l${i}`, draw: true, delay: l.column * COLUMN_MS, ms: 500, ease: "out" })),
|
|
2034
|
+
...Object.keys(NODES).map((id) => ({ id: `n${id}`, attrs: { opacity: [0, 1] }, delay: Math.max(0, NODE_COLUMN[id] * COLUMN_MS - 120), ms: 250 })),
|
|
2035
|
+
{ id: "hub", attrs: { opacity: [0, 1] }, delay: 2 * COLUMN_MS - 120, ms: 300 },
|
|
2036
|
+
{ id: "caption", attrs: { opacity: [0, 1], y: [HUB[1] + 40, HUB[1] + 34] }, delay: 2 * COLUMN_MS, ms: 400, ease: "out" },
|
|
2037
|
+
// Data packets drift along a few links while the path waits.
|
|
2038
|
+
...[3, 5, 8].map((link2, i) => [
|
|
2039
|
+
{ id: `pkt${i}`, attrs: { opacity: [0, 1] }, delay: 1e3 + i * 150, ms: 150 },
|
|
2040
|
+
{ id: `pkt${i}`, along: `l${link2}`, delay: 1e3 + i * 150, ms: 1100, ease: "in-out" },
|
|
2041
|
+
{ id: `pkt${i}`, attrs: { opacity: [1, 0] }, delay: 2e3 + i * 150, ms: 200 }
|
|
2042
|
+
]).flat(),
|
|
2043
|
+
{ id: "crit", draw: true, delay: PATH_START, ms: PATH_MS, ease: "in-out" },
|
|
2044
|
+
{ id: "comet", attrs: { opacity: [0, 1] }, delay: PATH_START, ms: 150 },
|
|
2045
|
+
{ id: "comet", along: "crit", delay: PATH_START, ms: PATH_MS, ease: "in-out" },
|
|
2046
|
+
{ id: "comet", attrs: { opacity: [1, 0] }, delay: PATH_START + PATH_MS, ms: 300 },
|
|
2047
|
+
// Rings pulse out from the hub as the comet passes it, halfway along.
|
|
2048
|
+
...[0, 1].map((i) => ({
|
|
2049
|
+
id: `ring${i}`,
|
|
2050
|
+
attrs: { r: [10, 52], opacity: [0.9, 0], "stroke-width": [3, 0.5] },
|
|
2051
|
+
delay: PATH_START + PATH_MS / 2 + i * 350,
|
|
2052
|
+
ms: 1200,
|
|
2053
|
+
ease: "out"
|
|
2054
|
+
}))
|
|
2055
|
+
]
|
|
2056
|
+
}
|
|
2057
|
+
};
|
|
2058
|
+
var DRIP_TOP = 106;
|
|
2059
|
+
var DRIPS = [
|
|
2060
|
+
{ at: 0.067, width: 11, length: 34, delay: 0, ms: 1500 },
|
|
2061
|
+
{ at: 0.18, width: 8, length: 18, delay: 260, ms: 1100 },
|
|
2062
|
+
{ at: 0.3, width: 13, length: 50, delay: 120, ms: 1800 },
|
|
2063
|
+
{ at: 0.453, width: 9, length: 22, delay: 380, ms: 1200 },
|
|
2064
|
+
{ at: 0.587, width: 10, length: 28, delay: 200, ms: 1400 },
|
|
2065
|
+
{ at: 0.693, width: 12, length: 42, delay: 60, ms: 1700 },
|
|
2066
|
+
{ at: 0.793, width: 11, length: 32, delay: 300, ms: 1500 }
|
|
2067
|
+
];
|
|
2068
|
+
var dripText = {
|
|
2069
|
+
kind: "vector",
|
|
2070
|
+
id: "drip-text",
|
|
2071
|
+
name: "Drip text",
|
|
2072
|
+
description: "A chunky outlined title popping in, then oozing slime drips from its letters.",
|
|
2073
|
+
category: "icon",
|
|
2074
|
+
tags: ["title", "text", "drip", "slime", "y2k", "headline"],
|
|
2075
|
+
hold: 1200,
|
|
2076
|
+
presets: [
|
|
2077
|
+
{ name: "Slime", values: { fill: [0.55, 0.93, 0.25], outline: [0.06, 0.2, 0.04], shine: [0.85, 1, 0.7] } },
|
|
2078
|
+
{ name: "Molten gold", values: { fill: [1, 0.78, 0.2], outline: [0.35, 0.16, 0], shine: [1, 0.96, 0.75], drip: 1.5 } }
|
|
2079
|
+
],
|
|
2080
|
+
// The word is stretched to a width set by its length (textLength), so the drips land under its letters in any font.
|
|
2081
|
+
derived: (v) => {
|
|
2082
|
+
const letters = Math.max(1, Array.from(String(v.word).trim()).length);
|
|
2083
|
+
const width = Math.min(380, letters * 75);
|
|
2084
|
+
const left = 200 - width / 2;
|
|
2085
|
+
return {
|
|
2086
|
+
width,
|
|
2087
|
+
...Object.fromEntries(
|
|
2088
|
+
DRIPS.flatMap((d, i) => {
|
|
2089
|
+
const x = Math.round((left + d.at * width) * 10) / 10;
|
|
2090
|
+
const length = Math.round(d.length * Number(v.drip) * 10) / 10;
|
|
2091
|
+
return [
|
|
2092
|
+
[`x${i}`, x],
|
|
2093
|
+
[`left${i}`, x - d.width / 2],
|
|
2094
|
+
[`shine${i}`, x - d.width * 0.25],
|
|
2095
|
+
[`len${i}`, length],
|
|
2096
|
+
[`tip${i}`, DRIP_TOP + length]
|
|
2097
|
+
];
|
|
2098
|
+
})
|
|
2099
|
+
)
|
|
2100
|
+
};
|
|
2101
|
+
},
|
|
2102
|
+
motion: (() => {
|
|
2103
|
+
const word = `<text x="200" y="120" text-anchor="middle" textLength="{{width}}" lengthAdjust="spacingAndGlyphs" font-family="'Arial Black', Impact, system-ui, sans-serif" font-size="104" font-weight="900" stroke-linejoin="round"`;
|
|
2104
|
+
const drips = (layer, paint) => DRIPS.map(
|
|
2105
|
+
(d, i) => `<rect id="${layer}r${i}" x="{{left${i}}}" y="${DRIP_TOP}" width="${d.width}" height="0" rx="${d.width / 2}" ${paint}/><circle id="${layer}c${i}" cx="{{x${i}}}" cy="${DRIP_TOP}" r="${d.width * 0.72}" ${paint}/>`
|
|
2106
|
+
).join("");
|
|
2107
|
+
const outline = `fill="{{outline}}" stroke="{{outline}}" stroke-width="{{stroke}}"`;
|
|
2108
|
+
const pop = (from, to) => [`translate(200 100) scale(${from}) translate(-200 -100)`, `translate(200 100) scale(${to}) translate(-200 -100)`];
|
|
2109
|
+
return {
|
|
2110
|
+
svg: `<svg viewBox="0 0 400 200"><!-- {{word: text default=SLOP label='Word'}} {{fill: color default=#FF7EB6 label='Fill'}} {{outline: color default=#3A0A2C label='Outline'}} {{shine: color default=#FFD1E6 label='Shine'}} {{drip: float min=0.2 max=2 default=1 label='Drip length'}} {{stroke: float min=0 max=14 default=7 step=0.5 label='Outline width'}} --><g id="word" transform="${pop(0.6, 1)[0]}" opacity="0">${drips("o", outline)}${word} fill="none" stroke="{{outline}}" stroke-width="{{stroke}}">{{word}}</text>${drips("f", `fill="{{fill}}"`)}${word} fill="{{fill}}">{{word}}</text>${DRIPS.map(
|
|
2111
|
+
(d, i) => `<circle id="s${i}" cx="{{shine${i}}}" cy="${DRIP_TOP}" r="${d.width * 0.2}" fill="{{shine}}" opacity="0"/>`
|
|
2112
|
+
).join("")}</g></svg>`,
|
|
2113
|
+
animate: [
|
|
2114
|
+
{ id: "word", attrs: { transform: pop(0.6, 1.08), opacity: [0, 1] }, ms: 260, ease: "out" },
|
|
2115
|
+
{ id: "word", attrs: { transform: pop(1.08, 1) }, delay: 260, ms: 200, ease: "in-out" },
|
|
2116
|
+
...DRIPS.flatMap((d, i) => {
|
|
2117
|
+
const at = { delay: 400 + d.delay, ms: d.ms, ease: "in-out" };
|
|
2118
|
+
const tip = `{{tip${i}}}`;
|
|
2119
|
+
return [
|
|
2120
|
+
...["o", "f"].flatMap((layer) => [
|
|
2121
|
+
{ id: `${layer}r${i}`, attrs: { height: [0, `{{len${i}}}`] }, ...at },
|
|
2122
|
+
{ id: `${layer}c${i}`, attrs: { cy: [DRIP_TOP, tip] }, ...at }
|
|
2123
|
+
]),
|
|
2124
|
+
{ id: `s${i}`, attrs: { cy: [DRIP_TOP, tip], opacity: [0, 0.9] }, ...at }
|
|
2125
|
+
];
|
|
2126
|
+
})
|
|
2127
|
+
]
|
|
2128
|
+
};
|
|
2129
|
+
})()
|
|
2130
|
+
};
|
|
1788
2131
|
var vectorEffects = [
|
|
1789
2132
|
spinner,
|
|
1790
2133
|
bouncingDots,
|
|
@@ -1800,7 +2143,9 @@ var vectorEffects = [
|
|
|
1800
2143
|
heartbeat,
|
|
1801
2144
|
signature,
|
|
1802
2145
|
confetti,
|
|
1803
|
-
blobMorph
|
|
2146
|
+
blobMorph,
|
|
2147
|
+
dripText,
|
|
2148
|
+
flowNetwork
|
|
1804
2149
|
];
|
|
1805
2150
|
|
|
1806
2151
|
// src/catalog/index.ts
|
|
@@ -1823,10 +2168,12 @@ var SHADER_EFFECTS = [
|
|
|
1823
2168
|
moire,
|
|
1824
2169
|
kaleidoscope,
|
|
1825
2170
|
barberStripes,
|
|
2171
|
+
neonCircuit,
|
|
1826
2172
|
bokeh,
|
|
1827
2173
|
godRays,
|
|
1828
2174
|
sparkles,
|
|
1829
2175
|
neonRipple,
|
|
2176
|
+
lightRibbons,
|
|
1830
2177
|
starfield,
|
|
1831
2178
|
nebula,
|
|
1832
2179
|
blackHole,
|
|
@@ -1861,7 +2208,7 @@ function findEffects(query = {}) {
|
|
|
1861
2208
|
});
|
|
1862
2209
|
}
|
|
1863
2210
|
function effectControls(effect) {
|
|
1864
|
-
return effect.kind === "shader" ? parseControls(effect.source) : effect
|
|
2211
|
+
return effect.kind === "shader" ? parseControls(effect.source) : vectorEffectControls(effect);
|
|
1865
2212
|
}
|
|
1866
2213
|
function effectDefaults(effect, options = {}) {
|
|
1867
2214
|
const preset = options.preset ? effect.presets?.find((p) => p.name === options.preset)?.values : void 0;
|
|
@@ -1880,6 +2227,10 @@ export {
|
|
|
1880
2227
|
svgDataUrl,
|
|
1881
2228
|
SAMPLE_IMAGES,
|
|
1882
2229
|
EFFECT_CATEGORIES,
|
|
2230
|
+
VECTOR_CONTROL_TYPES,
|
|
2231
|
+
vectorControls,
|
|
2232
|
+
vectorEffectControls,
|
|
2233
|
+
vectorControlIssues,
|
|
1883
2234
|
vectorControlDef,
|
|
1884
2235
|
renderVectorEffect,
|
|
1885
2236
|
colorControl,
|
|
@@ -1891,4 +2242,4 @@ export {
|
|
|
1891
2242
|
effectControls,
|
|
1892
2243
|
effectDefaults
|
|
1893
2244
|
};
|
|
1894
|
-
//# sourceMappingURL=chunk-
|
|
2245
|
+
//# sourceMappingURL=chunk-IWKM5FJK.js.map
|