@pixerate/flair 0.16.0 → 0.17.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 +16 -0
- package/README.md +12 -6
- package/dist/catalog/index.d.ts +3 -3
- package/dist/catalog/index.js +6 -2
- package/dist/{chunk-UNLCGDCB.js → chunk-LKQM3GSQ.js} +3 -3
- package/dist/{chunk-KWNFZ5NC.js → chunk-LURJMWYM.js} +237 -185
- package/dist/{chunk-KWNFZ5NC.js.map → chunk-LURJMWYM.js.map} +1 -1
- package/dist/{chunk-DVVQRZJ5.js → chunk-MILF5MRS.js} +11 -2
- package/dist/chunk-MILF5MRS.js.map +1 -0
- package/dist/{chunk-YLJWC4N2.js → chunk-PMVJP5I4.js} +2 -2
- package/dist/{chunk-4RGHZWQC.js → chunk-RM3I6WZH.js} +2 -2
- package/dist/{chunk-4RGHZWQC.js.map → chunk-RM3I6WZH.js.map} +1 -1
- package/dist/{chunk-FIJOMGPG.js → chunk-UP6QQVRS.js} +2 -2
- package/dist/{chunk-WC3VADZC.js → chunk-VYOMXE6I.js} +46 -4
- package/dist/chunk-VYOMXE6I.js.map +1 -0
- package/dist/cli.js +4 -4
- package/dist/{controls-Syr6drzO.d.ts → controls-Ec6P0Vpy.d.ts} +9 -2
- package/dist/editor/index.d.ts +3 -3
- package/dist/editor/index.js +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +2 -2
- package/dist/react/index.d.ts +14 -3
- package/dist/react/index.js +18 -9
- package/dist/react/index.js.map +1 -1
- package/dist/recorder-HKFFHQES.js +10 -0
- package/dist/{renderer-SNYZu__R.d.ts → renderer-Bm0oQ7Bw.d.ts} +1 -1
- package/dist/svelte/index.d.ts +16 -5
- package/dist/svelte/index.js +16 -11
- package/dist/svelte/index.js.map +1 -1
- package/dist/{types-9J6NWaN3.d.ts → types-C7g8-lTX.d.ts} +9 -3
- package/dist/vector/index.d.ts +27 -1
- package/dist/vector/index.js +4 -2
- package/package.json +1 -1
- package/dist/chunk-DVVQRZJ5.js.map +0 -1
- package/dist/chunk-WC3VADZC.js.map +0 -1
- package/dist/recorder-KEX3U35D.js +0 -10
- /package/dist/{chunk-UNLCGDCB.js.map → chunk-LKQM3GSQ.js.map} +0 -0
- /package/dist/{chunk-YLJWC4N2.js.map → chunk-PMVJP5I4.js.map} +0 -0
- /package/dist/{chunk-FIJOMGPG.js.map → chunk-UP6QQVRS.js.map} +0 -0
- /package/dist/{recorder-KEX3U35D.js.map → recorder-HKFFHQES.js.map} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
hexToRgb,
|
|
13
13
|
parseControls,
|
|
14
14
|
rgbToHex
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-VYOMXE6I.js";
|
|
16
16
|
|
|
17
17
|
// src/catalog/glsl.ts
|
|
18
18
|
var HASH = `
|
|
@@ -1748,9 +1748,9 @@ var checkFlash = (at2, back) => [
|
|
|
1748
1748
|
{ id: "check", step: 1, attrs: { opacity: [1, 0] }, delay: back, ms: 160 },
|
|
1749
1749
|
{ id: "check", step: 1, draw: [0, 0], delay: back + 200, ms: 0 }
|
|
1750
1750
|
];
|
|
1751
|
-
var buttonLabels = (a, b,
|
|
1751
|
+
var buttonLabels = (a, b, room2, max) => {
|
|
1752
1752
|
const ems = [a, b].map((t) => Math.max(0.6, textWidthEm(String(t))));
|
|
1753
|
-
const size = Math.min(max,
|
|
1753
|
+
const size = Math.min(max, room2 / Math.max(...ems));
|
|
1754
1754
|
return { size: r1(size), base: r1(50 + size * 0.35), labelLen: r1(ems[0] * size), doneLen: r1(ems[1] * size) };
|
|
1755
1755
|
};
|
|
1756
1756
|
var labelTexts = (x) => `<text id="label" x="${x}" y="{{base}}" textLength="{{labelLen}}" lengthAdjust="spacingAndGlyphs" transform="translate(0 0)" font-family="${SANS}" font-size="{{size}}" font-weight="600" fill="{{ink}}">{{label}}</text><text id="done" x="${x}" y="{{base}}" textLength="{{doneLen}}" lengthAdjust="spacingAndGlyphs" transform="translate(0 10)" opacity="0" font-family="${SANS}" font-size="{{size}}" font-weight="600" fill="{{ink}}">{{doneText}}</text>`;
|
|
@@ -9265,6 +9265,138 @@ var imageTreatments = [
|
|
|
9265
9265
|
imageOilPaint
|
|
9266
9266
|
];
|
|
9267
9267
|
|
|
9268
|
+
// src/catalog/types.ts
|
|
9269
|
+
var EFFECT_CATEGORIES = [
|
|
9270
|
+
"gradient",
|
|
9271
|
+
"organic",
|
|
9272
|
+
"pattern",
|
|
9273
|
+
"light",
|
|
9274
|
+
"space",
|
|
9275
|
+
"retro",
|
|
9276
|
+
"image",
|
|
9277
|
+
"overlay",
|
|
9278
|
+
"text",
|
|
9279
|
+
"morph",
|
|
9280
|
+
"interaction",
|
|
9281
|
+
"pyrotechnics",
|
|
9282
|
+
"timewarp",
|
|
9283
|
+
"melt",
|
|
9284
|
+
"electric",
|
|
9285
|
+
"transition",
|
|
9286
|
+
"loader",
|
|
9287
|
+
"data",
|
|
9288
|
+
"icon"
|
|
9289
|
+
];
|
|
9290
|
+
var VECTOR_CONTROL_TYPES = ["float", "int", "bool", "color", "text"];
|
|
9291
|
+
var PLACEHOLDER = /\{\{\s*([A-Za-z_]\w*)\s*(?::\s*([A-Za-z0-9]*)([^}]*))?\}\}/g;
|
|
9292
|
+
function motionStrings(motion) {
|
|
9293
|
+
const out = [];
|
|
9294
|
+
const walk = (v) => {
|
|
9295
|
+
if (typeof v === "string") out.push(v);
|
|
9296
|
+
else if (Array.isArray(v)) v.forEach(walk);
|
|
9297
|
+
else if (v && typeof v === "object") Object.values(v).forEach(walk);
|
|
9298
|
+
};
|
|
9299
|
+
walk(motion);
|
|
9300
|
+
return out;
|
|
9301
|
+
}
|
|
9302
|
+
function vectorControls(motion) {
|
|
9303
|
+
const controls = [];
|
|
9304
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9305
|
+
for (const text of motionStrings(motion)) {
|
|
9306
|
+
for (const [, name, type, attributes] of text.matchAll(PLACEHOLDER)) {
|
|
9307
|
+
if (type === void 0 || seen.has(name) || !VECTOR_CONTROL_TYPES.includes(type)) continue;
|
|
9308
|
+
seen.add(name);
|
|
9309
|
+
const colour = type === "color";
|
|
9310
|
+
controls.push(controlFromDeclaration(name, colour ? "vec3" : type, `${attributes}${colour ? " color" : ""}`, controls.length + 1));
|
|
9311
|
+
}
|
|
9312
|
+
}
|
|
9313
|
+
return controls;
|
|
9314
|
+
}
|
|
9315
|
+
function vectorEffectControls(effect) {
|
|
9316
|
+
const declared = vectorControls(effect.motion);
|
|
9317
|
+
const names = new Set(declared.map((c) => c.name));
|
|
9318
|
+
const listed = (effect.controls ?? []).filter((c) => !names.has(c.name));
|
|
9319
|
+
return [...declared, ...listed.map((c, i) => vectorControlDef(c, declared.length + i + 1))];
|
|
9320
|
+
}
|
|
9321
|
+
function vectorControlIssues(motion) {
|
|
9322
|
+
const issues = [];
|
|
9323
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
9324
|
+
for (const text of motionStrings(motion)) {
|
|
9325
|
+
for (const [whole, name, type] of text.matchAll(PLACEHOLDER)) {
|
|
9326
|
+
if (type === void 0) continue;
|
|
9327
|
+
if (!VECTOR_CONTROL_TYPES.includes(type))
|
|
9328
|
+
issues.push(`${whole}: a control's type is one of ${VECTOR_CONTROL_TYPES.join(", ")}`);
|
|
9329
|
+
else if (declarations.has(name) && declarations.get(name) !== whole)
|
|
9330
|
+
issues.push(`"${name}" is declared twice; declare it once and write {{${name}}} everywhere else`);
|
|
9331
|
+
else declarations.set(name, whole);
|
|
9332
|
+
}
|
|
9333
|
+
}
|
|
9334
|
+
return issues;
|
|
9335
|
+
}
|
|
9336
|
+
function written(control, value) {
|
|
9337
|
+
if (control.color && Array.isArray(value)) return rgbToHex(value);
|
|
9338
|
+
if (Array.isArray(value)) return value.join(" ");
|
|
9339
|
+
return value;
|
|
9340
|
+
}
|
|
9341
|
+
var escapeMarkup = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
9342
|
+
function vectorControlDef(control, line = 0) {
|
|
9343
|
+
const min = control.min ?? 0;
|
|
9344
|
+
return {
|
|
9345
|
+
name: control.name,
|
|
9346
|
+
type: control.type,
|
|
9347
|
+
label: control.label ?? control.name,
|
|
9348
|
+
color: control.color ?? false,
|
|
9349
|
+
min,
|
|
9350
|
+
max: control.max ?? Math.max(min + 1, 1),
|
|
9351
|
+
step: control.step ?? (control.type === "int" ? 1 : 0.01),
|
|
9352
|
+
default: control.default,
|
|
9353
|
+
line
|
|
9354
|
+
};
|
|
9355
|
+
}
|
|
9356
|
+
var CURRENT_COLOR = "currentColor";
|
|
9357
|
+
function isCurrentColor(value) {
|
|
9358
|
+
return typeof value === "string" && value.trim().toLowerCase() === "currentcolor";
|
|
9359
|
+
}
|
|
9360
|
+
function renderVectorEffect(effect, values = {}) {
|
|
9361
|
+
const filled = {};
|
|
9362
|
+
const placeholders = /* @__PURE__ */ new Map();
|
|
9363
|
+
for (const control of vectorEffectControls(effect)) {
|
|
9364
|
+
const given = values[control.name];
|
|
9365
|
+
if (control.color && isCurrentColor(given)) {
|
|
9366
|
+
filled[control.name] = CURRENT_COLOR;
|
|
9367
|
+
placeholders.set(control.name, CURRENT_COLOR);
|
|
9368
|
+
continue;
|
|
9369
|
+
}
|
|
9370
|
+
const check = given === void 0 ? void 0 : checkControlValue(control, given);
|
|
9371
|
+
filled[control.name] = check?.ok ? check.value : control.default;
|
|
9372
|
+
placeholders.set(control.name, written(control, filled[control.name]));
|
|
9373
|
+
}
|
|
9374
|
+
for (const [name, value] of Object.entries(effect.derived?.(filled) ?? {})) placeholders.set(name, value);
|
|
9375
|
+
const fill = (text, markup) => {
|
|
9376
|
+
const whole = /^\{\{\s*([A-Za-z_]\w*)\s*(?::[^}]*)?\}\}$/.exec(text);
|
|
9377
|
+
if (!markup && whole && placeholders.has(whole[1])) return placeholders.get(whole[1]);
|
|
9378
|
+
return text.replace(PLACEHOLDER, (match, name) => {
|
|
9379
|
+
if (!placeholders.has(name)) return match;
|
|
9380
|
+
const value = String(placeholders.get(name));
|
|
9381
|
+
return markup ? escapeMarkup(value) : value;
|
|
9382
|
+
});
|
|
9383
|
+
};
|
|
9384
|
+
const walk = (v) => {
|
|
9385
|
+
if (typeof v === "string") return fill(v, false);
|
|
9386
|
+
if (Array.isArray(v)) return v.map(walk);
|
|
9387
|
+
if (v && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));
|
|
9388
|
+
return v;
|
|
9389
|
+
};
|
|
9390
|
+
const { svg, ...rest } = effect.motion;
|
|
9391
|
+
return { svg: fill(svg.replace(/<!--[\s\S]*?-->/g, ""), true), ...walk(rest) };
|
|
9392
|
+
}
|
|
9393
|
+
function colorControl(name, hex, label) {
|
|
9394
|
+
return { name, type: "vec3", color: true, default: hexToRgb(hex) ?? [1, 1, 1], label: label ?? name };
|
|
9395
|
+
}
|
|
9396
|
+
function numberControl(name, value, min, max, label, step) {
|
|
9397
|
+
return { name, type: "float", default: value, min, max, label: label ?? name, step };
|
|
9398
|
+
}
|
|
9399
|
+
|
|
9268
9400
|
// src/catalog/text.ts
|
|
9269
9401
|
var r14 = (n5) => Math.round(n5 * 10) / 10;
|
|
9270
9402
|
function fit2(text, width, max, ems = textWidthEm(text)) {
|
|
@@ -9274,6 +9406,10 @@ function fit2(text, width, max, ems = textWidthEm(text)) {
|
|
|
9274
9406
|
}
|
|
9275
9407
|
var lettersOf = (text) => Array.from(String(text));
|
|
9276
9408
|
var mixHex2 = (a, b, t) => {
|
|
9409
|
+
if (isCurrentColor(a) || isCurrentColor(b)) {
|
|
9410
|
+
const css = (c) => isCurrentColor(c) ? CURRENT_COLOR : rgbToHex(Array.isArray(c) ? c : [1, 1, 1]);
|
|
9411
|
+
return `color-mix(in srgb, ${css(a)}, ${css(b)} ${Math.round(t * 100)}%)`;
|
|
9412
|
+
}
|
|
9277
9413
|
const x = Array.isArray(a) ? a : [1, 1, 1];
|
|
9278
9414
|
const y = Array.isArray(b) ? b : [1, 1, 1];
|
|
9279
9415
|
return rgbToHex([0, 1, 2].map((i) => x[i] + (y[i] - x[i]) * t));
|
|
@@ -9284,6 +9420,16 @@ var MONO = `ui-monospace, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospa
|
|
|
9284
9420
|
var MONO_EM = 0.6;
|
|
9285
9421
|
var HEAVY = `'Arial Black', 'Helvetica Neue', Impact, system-ui, sans-serif`;
|
|
9286
9422
|
var SANS3 = `system-ui, 'Helvetica Neue', Arial, sans-serif`;
|
|
9423
|
+
var SCRIPT = `'Pacifico', 'Brush Script MT', 'Segoe Script', 'Snell Roundhand', cursive`;
|
|
9424
|
+
var CAP = 0.72;
|
|
9425
|
+
var typeface = (font, weight) => `{{font: text default="${font}" label='Font'}} {{weight: int min=100 max=900 default=${weight} step=100 label='Weight'}} {{inline: bool default=false label='Inline'}}`;
|
|
9426
|
+
function frame(v, left, width, base, size, overhang = [0.05, 0, DESCENT, 0]) {
|
|
9427
|
+
const spill = overhang.map((e) => Math.round(Math.max(0, e) * 100) / 100).join(" ");
|
|
9428
|
+
return v.inline ? { viewBox: `${r14(left)} ${r14(base - size * CAP)} ${r14(width)} ${r14(size * CAP)}`, overflow: "visible", backdrop: 0, overhang: spill } : { viewBox: "0 0 400 200", overflow: "hidden", backdrop: 1, overhang: spill };
|
|
9429
|
+
}
|
|
9430
|
+
var DESCENT = 0.25;
|
|
9431
|
+
var glowing = (overhang, by) => overhang.map((e) => e + by);
|
|
9432
|
+
var room = (v, width) => v.inline ? Infinity : width;
|
|
9287
9433
|
var BLINKS = 7;
|
|
9288
9434
|
var typewriter = {
|
|
9289
9435
|
kind: "vector",
|
|
@@ -9300,20 +9446,22 @@ var typewriter = {
|
|
|
9300
9446
|
// Monospace, so the caret lands after the last letter however long the text is.
|
|
9301
9447
|
derived: (v) => {
|
|
9302
9448
|
const count = Math.max(1, lettersOf(v.text).length);
|
|
9303
|
-
const size = r14(Math.min(44, 340 / ((count + 1) * MONO_EM)));
|
|
9449
|
+
const size = r14(Math.min(44, room(v, 340) / ((count + 1) * MONO_EM)));
|
|
9304
9450
|
const left = r14(200 - (count + 1) * MONO_EM * size / 2);
|
|
9305
9451
|
const typing = Math.round(count * Number(v.pace));
|
|
9452
|
+
const base = r14(100 + size * 0.35);
|
|
9306
9453
|
return {
|
|
9454
|
+
...frame(v, left, (count + 1) * MONO_EM * size, base, size, [0.08, 0, DESCENT, 0]),
|
|
9307
9455
|
size,
|
|
9308
9456
|
left,
|
|
9309
|
-
base
|
|
9457
|
+
base,
|
|
9310
9458
|
end: r14(left + count * MONO_EM * size),
|
|
9311
9459
|
typing,
|
|
9312
9460
|
...Object.fromEntries(Array.from({ length: BLINKS }, (_, k) => [`blink${k}`, typing + k * 500]))
|
|
9313
9461
|
};
|
|
9314
9462
|
},
|
|
9315
9463
|
motion: {
|
|
9316
|
-
svg: `<svg viewBox="
|
|
9464
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='Hello, world.' label='Text'}} {{paper: color default=#111827 label='Background'}} {{ink: color default=#F9FAFB label='Text colour'}} {{pace: float min=30 max=300 default=90 step=5 label='Ms per letter'}} ${typeface(MONO, 400)} --><rect width="400" height="200" fill="{{paper}}" opacity="{{backdrop}}"/><text id="typed" x="{{left}}" y="{{base}}" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" fill="{{ink}}" xml:space="preserve">{{text}}</text><text id="caret" x="{{end}}" y="{{base}}" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" fill="{{ink}}" opacity="0">\u258C</text></svg>`,
|
|
9317
9465
|
animate: [
|
|
9318
9466
|
{ id: "typed", write: "{{text}}", caret: "\u258C", ms: n4("typing"), ease: "linear" },
|
|
9319
9467
|
// Hidden while the writer shows its own caret, then blinking where it stopped.
|
|
@@ -9335,13 +9483,13 @@ var scrambleText = {
|
|
|
9335
9483
|
],
|
|
9336
9484
|
derived: (v) => {
|
|
9337
9485
|
const count = Math.max(1, lettersOf(v.text).length);
|
|
9338
|
-
const size = r14(Math.min(60, 360 / (count * MONO_EM)));
|
|
9486
|
+
const size = r14(Math.min(60, room(v, 360) / (count * MONO_EM)));
|
|
9339
9487
|
const width = r14(count * MONO_EM * size);
|
|
9340
9488
|
const base = r14(100 + size * 0.35);
|
|
9341
|
-
return { size, base, barX: r14(200 - width / 2), barY: r14(base + size * 0.3), barW: width, decode: Math.round(count * Number(v.pace)), glowWide: Number(v.glow) * 2 };
|
|
9489
|
+
return { size, base, barX: r14(200 - width / 2), barY: r14(base + size * 0.3), barW: width, decode: Math.round(count * Number(v.pace)), glowWide: Number(v.glow) * 2, ...frame(v, 200 - width / 2, width, base, size, glowing([0.08, 0, Math.max(DESCENT, 0.3 + 3 / size), 0], Number(v.glow) * 4 / size)) };
|
|
9342
9490
|
},
|
|
9343
9491
|
motion: {
|
|
9344
|
-
svg: `<svg viewBox="
|
|
9492
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='ACCESS GRANTED' label='Text'}} {{glyphs: text default='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#%&@$*+' label='Scramble glyphs'}} {{background: color default=#05070D label='Background'}} {{ink: color default=#E2E8F0 label='Text colour'}} {{accent: color default=#22D3EE label='Accent'}} {{pace: float min=20 max=250 default=70 step=5 label='Ms per letter'}} {{glow: float min=0 max=6 default=2 step=0.5 label='Glow'}} ${typeface(MONO, 700)} --><defs><filter id="glow" x="-20%" y="-50%" width="140%" height="200%"><feGaussianBlur stdDeviation="{{glowWide}}" result="blur"/><feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/><text id="word" x="200" y="{{base}}" text-anchor="middle" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" fill="{{ink}}" filter="url(#glow)" xml:space="preserve">{{text}}</text><rect id="bar" x="{{barX}}" y="{{barY}}" width="0" height="3" fill="{{accent}}"/></svg>`,
|
|
9345
9493
|
animate: [
|
|
9346
9494
|
// Pure noise for a moment (the scramble runs before the change starts), then it decodes.
|
|
9347
9495
|
{ id: "word", write: "{{text}}", scramble: "{{glyphs}}", delay: 500, ms: n4("decode"), ease: "linear" },
|
|
@@ -9367,10 +9515,11 @@ var kineticStagger = {
|
|
|
9367
9515
|
const letters = lettersOf(v.text).slice(0, SLOTS2);
|
|
9368
9516
|
const widths = letters.map((c) => textWidthEm(c));
|
|
9369
9517
|
const ems = Math.max(0.6, widths.reduce((a, b) => a + b, 0));
|
|
9370
|
-
const size = Math.min(92, 370 / ems);
|
|
9518
|
+
const size = Math.min(92, room(v, 370) / ems);
|
|
9371
9519
|
const base = 100 + size * 0.35;
|
|
9372
9520
|
const stagger = Number(v.stagger);
|
|
9373
9521
|
const out = {
|
|
9522
|
+
...frame(v, 200 - ems * size / 2, ems * size, base, size, [0.15, 0, Math.max(DESCENT, 0.2 + 6 / size), 0]),
|
|
9374
9523
|
size: r14(size),
|
|
9375
9524
|
base: r14(base),
|
|
9376
9525
|
rise: r14(base - size * 0.1),
|
|
@@ -9392,9 +9541,9 @@ var kineticStagger = {
|
|
|
9392
9541
|
return out;
|
|
9393
9542
|
},
|
|
9394
9543
|
motion: {
|
|
9395
|
-
svg: `<svg viewBox="
|
|
9544
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='Kinetic' label='Text'}} {{background: color default=#F8FAFC label='Background'}} {{ink: color default=#0F172A label='Text colour'}} {{accent: color default=#F43F5E label='Underline'}} {{stagger: float min=20 max=200 default=55 step=5 label='Ms between letters'}} ${typeface(HEAVY, 900)} --><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/>${Array.from(
|
|
9396
9545
|
{ length: SLOTS2 },
|
|
9397
|
-
(_, i) => `<text id="l${i}" text-anchor="middle" transform="translate({{x${i}}} {{drop}}) rotate(-28) scale(0.3)" opacity="0" font-family="
|
|
9546
|
+
(_, i) => `<text id="l${i}" text-anchor="middle" transform="translate({{x${i}}} {{drop}}) rotate(-28) scale(0.3)" opacity="0" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" fill="{{ink}}">{{c${i}}}</text>`
|
|
9398
9547
|
).join("")}<rect id="line" x="{{lineX}}" y="{{lineY}}" width="0" height="6" rx="3" fill="{{accent}}"/></svg>`,
|
|
9399
9548
|
animate: [
|
|
9400
9549
|
...Array.from({ length: SLOTS2 }, (_, i) => [
|
|
@@ -9428,8 +9577,10 @@ var neonSign = {
|
|
|
9428
9577
|
derived: (v) => {
|
|
9429
9578
|
const letters = lettersOf(v.text);
|
|
9430
9579
|
const k = Math.min(Math.max(0, Math.round(Number(v.faulty)) - 1), Math.max(0, letters.length - 1));
|
|
9431
|
-
const { size, length } = fit2(String(v.text), 340, 84);
|
|
9580
|
+
const { size, length, left } = fit2(String(v.text), room(v, 340), 84);
|
|
9432
9581
|
return {
|
|
9582
|
+
...frame(v, left, length, 100 + size * 0.3, size, glowing([0.1, 0, DESCENT, 0], Number(v.glow) * 6 / size)),
|
|
9583
|
+
hazeX: r14(Math.max(170, length / 2 + 30)),
|
|
9433
9584
|
head: letters.slice(0, k).join(""),
|
|
9434
9585
|
mid: letters[k] ?? "",
|
|
9435
9586
|
tail: letters.slice(k + 1).join(""),
|
|
@@ -9441,12 +9592,12 @@ var neonSign = {
|
|
|
9441
9592
|
};
|
|
9442
9593
|
},
|
|
9443
9594
|
motion: (() => {
|
|
9444
|
-
const word = `x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="
|
|
9595
|
+
const word = `x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}"`;
|
|
9445
9596
|
const runs = (id) => `<tspan>{{head}}</tspan><tspan id="${id}">{{mid}}</tspan><tspan>{{tail}}</tspan>`;
|
|
9446
9597
|
const startUp = [[0, 0], [150, 0.85], [210, 0], [330, 1], [390, 0.15], [450, 1], [620, 0.35], [680, 1]];
|
|
9447
9598
|
const buzz = [[0, 1], [1300, 0.15], [1350, 1], [1420, 0.2], [1520, 1], [2100, 0.1], [2160, 1], [2230, 0.25], [2280, 1]];
|
|
9448
9599
|
return {
|
|
9449
|
-
svg: `<svg viewBox="
|
|
9600
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='Open late' label='Text'}} {{tube: color default=#FF3DA5 label='Tube'}} {{wall: color default=#120612 label='Wall'}} {{glow: float min=0.5 max=8 default=3.5 step=0.5 label='Glow'}} {{faulty: int min=1 max=24 default=6 label='Buzzing letter'}} ${typeface(SCRIPT, 400)} --><defs><filter id="neon" x="-30%" y="-80%" width="160%" height="260%"><feGaussianBlur in="SourceGraphic" stdDeviation="{{glow}}" result="near"/><feGaussianBlur in="SourceGraphic" stdDeviation="{{far}}" result="far"/><feMerge><feMergeNode in="far"/><feMergeNode in="far"/><feMergeNode in="near"/><feMergeNode in="SourceGraphic"/></feMerge></filter><filter id="haze" x="-50%" y="-100%" width="200%" height="300%"><feGaussianBlur stdDeviation="30"/></filter></defs><rect width="400" height="200" fill="{{wall}}" opacity="{{backdrop}}"/><g id="sign" opacity="0"><ellipse cx="200" cy="100" rx="{{hazeX}}" ry="55" fill="{{tube}}" opacity="0.18" filter="url(#haze)"/><text ${word} fill="{{tube}}" stroke="{{tube}}" stroke-width="1.5" filter="url(#neon)">${runs("buzzGlow")}</text><text ${word} fill="{{core}}">${runs("buzzCore")}</text></g></svg>`,
|
|
9450
9601
|
animate: [
|
|
9451
9602
|
...holds("sign", "opacity", startUp),
|
|
9452
9603
|
// fill-opacity rather than opacity: browsers ignore opacity on a <tspan>.
|
|
@@ -9469,11 +9620,12 @@ var strokeDrawText = {
|
|
|
9469
9620
|
],
|
|
9470
9621
|
// <text> takes no pathLength, so `draw` cannot measure it: the dash grows instead, longer than any letter's outline.
|
|
9471
9622
|
derived: (v) => {
|
|
9472
|
-
const { size, length } = fit2(String(v.text), 360, 100);
|
|
9473
|
-
|
|
9623
|
+
const { size, length, left } = fit2(String(v.text), room(v, 360), 100);
|
|
9624
|
+
const edge = Number(v.width) / 2 / size;
|
|
9625
|
+
return { size, length, base: r14(100 + size * 0.35), dash: Math.round(size * 6), ...frame(v, left, length, 100 + size * 0.35, size, glowing([0.05, 0, DESCENT, 0], edge)) };
|
|
9474
9626
|
},
|
|
9475
9627
|
motion: {
|
|
9476
|
-
svg: `<svg viewBox="
|
|
9628
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='Draw' label='Text'}} {{background: color default=#FFFBF5 label='Background'}} {{outline: color default=#1E293B label='Outline'}} {{fill: color default=#F97316 label='Fill'}} {{width: float min=0.5 max=6 default=2 step=0.5 label='Outline width'}} {{fillOpacity: float min=0 max=1 default=1 label='Fill opacity'}} ${typeface(HEAVY, 900)} --><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/><text id="word" x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" fill="{{fill}}" fill-opacity="0" stroke="{{outline}}" stroke-width="{{width}}" stroke-linejoin="round" stroke-dasharray="0 {{dash}}">{{text}}</text></svg>`,
|
|
9477
9629
|
animate: [
|
|
9478
9630
|
{ id: "word", attrs: { "stroke-dasharray": ["0 {{dash}}", "{{dash}} 0"] }, ms: 2e3, ease: "in-out" },
|
|
9479
9631
|
{ id: "word", attrs: { "fill-opacity": [0, n4("fillOpacity")] }, delay: 1500, ms: 700, ease: "out" }
|
|
@@ -9493,8 +9645,9 @@ var splitReveal = {
|
|
|
9493
9645
|
{ name: "Gold on black", values: { background: [0.02, 0.02, 0.02], ink: [0.96, 0.84, 0.55], line: [0.96, 0.84, 0.55] } }
|
|
9494
9646
|
],
|
|
9495
9647
|
derived: (v) => {
|
|
9496
|
-
const { size, length, left } = fit2(String(v.text), 360, 88);
|
|
9648
|
+
const { size, length, left } = fit2(String(v.text), room(v, 360), 88);
|
|
9497
9649
|
return {
|
|
9650
|
+
...frame(v, left, length, 100 + size * 0.36, size, [0.05, 10 / size, Math.max(DESCENT, 34 / size), 10 / size]),
|
|
9498
9651
|
size,
|
|
9499
9652
|
length,
|
|
9500
9653
|
base: r14(100 + size * 0.36),
|
|
@@ -9505,9 +9658,9 @@ var splitReveal = {
|
|
|
9505
9658
|
};
|
|
9506
9659
|
},
|
|
9507
9660
|
motion: (() => {
|
|
9508
|
-
const word = `<text x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="
|
|
9661
|
+
const word = `<text x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" fill="{{ink}}">{{text}}</text>`;
|
|
9509
9662
|
return {
|
|
9510
|
-
svg: `<svg viewBox="
|
|
9663
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='Chapter One' label='Text'}} {{caption: text default='The beginning' label='Caption'}} {{background: color default=#0F172A label='Background'}} {{ink: color default=#F8FAFC label='Text colour'}} {{line: color default=#38BDF8 label='Line'}} ${typeface(SANS3, 800)} --><defs><clipPath id="above"><rect x="-5000" y="-5000" width="10400" height="5100"/></clipPath><clipPath id="below"><rect x="-5000" y="100" width="10400" height="5100"/></clipPath></defs><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/><g clip-path="url(#above)"><g id="top" transform="translate(0 {{shift}})">${word}</g></g><g clip-path="url(#below)"><g id="bottom" transform="translate(0 -{{shift}})">${word}</g></g><path id="line" d="M{{lineX1}} 100 H{{lineX2}}" stroke="{{line}}" stroke-width="2.5" stroke-linecap="round"/><text id="caption" x="200" y="{{captionY}}" text-anchor="middle" font-family="{{font}}" font-size="13" letter-spacing="3" fill="{{ink}}" opacity="0">{{caption}}</text></svg>`,
|
|
9511
9664
|
animate: [
|
|
9512
9665
|
{ id: "line", draw: true, ms: 600, ease: "in-out" },
|
|
9513
9666
|
{ id: "top", attrs: { transform: ["translate(0 {{shift}})", "translate(0 0)"] }, delay: 450, ms: 800, ease: "out" },
|
|
@@ -9533,12 +9686,13 @@ var highlighter = {
|
|
|
9533
9686
|
],
|
|
9534
9687
|
// The swipe runs a little past the phrase at both ends, across its lower half, with a ragged second pass.
|
|
9535
9688
|
derived: (v) => {
|
|
9536
|
-
const { size, length, left } = fit2(String(v.text), 330, 56);
|
|
9689
|
+
const { size, length, left } = fit2(String(v.text), room(v, 330), 56);
|
|
9537
9690
|
const base = 100 + size * 0.35;
|
|
9538
9691
|
const x0 = left - size * 0.25;
|
|
9539
9692
|
const x1 = 400 - left + size * 0.2;
|
|
9540
9693
|
const y = base - size * 0.3;
|
|
9541
9694
|
return {
|
|
9695
|
+
...frame(v, left, length, base, size, [0.05, 0.2, DESCENT, 0.25]),
|
|
9542
9696
|
size,
|
|
9543
9697
|
length,
|
|
9544
9698
|
base: r14(base),
|
|
@@ -9549,7 +9703,7 @@ var highlighter = {
|
|
|
9549
9703
|
};
|
|
9550
9704
|
},
|
|
9551
9705
|
motion: {
|
|
9552
|
-
svg: `<svg viewBox="
|
|
9706
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='Ship it faster' label='Phrase'}} {{background: color default=#FFFFFF label='Background'}} {{ink: color default=#111827 label='Text colour'}} {{marker: color default=#FDE047 label='Marker'}} ${typeface(SANS3, 700)} --><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/><g transform="rotate(-1.2 200 100)" opacity="0.9"><path id="swipe" d="{{swipe}}" fill="none" stroke="{{marker}}" stroke-width="{{thick}}"/><path id="ragged" d="{{ragged}}" fill="none" stroke="{{marker}}" stroke-width="{{thin}}" stroke-linecap="round"/></g><text id="phrase" x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" fill="{{ink}}" opacity="0">{{text}}</text></svg>`,
|
|
9553
9707
|
animate: [
|
|
9554
9708
|
{ id: "phrase", attrs: { opacity: [0, 1] }, ms: 300, ease: "out" },
|
|
9555
9709
|
{ id: "swipe", draw: true, delay: 350, ms: 650, ease: "in-out" },
|
|
@@ -9578,12 +9732,14 @@ var wordCycle = {
|
|
|
9578
9732
|
const lead = textWidthEm(String(v.prefix));
|
|
9579
9733
|
const ems = words.map((w) => Math.max(0.6, textWidthEm(w)));
|
|
9580
9734
|
const total = lead + 0.3 + Math.max(...ems);
|
|
9581
|
-
const size = Math.min(52, 370 / total);
|
|
9735
|
+
const size = Math.min(52, room(v, 370) / total);
|
|
9582
9736
|
const left = 200 - total * size / 2;
|
|
9583
9737
|
const base = 100 + size * 0.35;
|
|
9584
9738
|
const step = size * 1.3;
|
|
9585
9739
|
const wx = left + (lead + 0.3) * size;
|
|
9586
9740
|
return {
|
|
9741
|
+
...frame(v, left, total * size, base, size, [0.08, 0, Math.max(DESCENT, 0.18 + 4 / size), 0]),
|
|
9742
|
+
bold: Math.min(900, Number(v.weight) + 100),
|
|
9587
9743
|
size: r14(size),
|
|
9588
9744
|
base: r14(base),
|
|
9589
9745
|
px: r14(left),
|
|
@@ -9605,9 +9761,9 @@ var wordCycle = {
|
|
|
9605
9761
|
};
|
|
9606
9762
|
},
|
|
9607
9763
|
motion: {
|
|
9608
|
-
svg: `<svg viewBox="
|
|
9764
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{prefix: text default='We build' label='Lead-in'}} {{word1: text default=apps label='Word 1'}} {{word2: text default=brands label='Word 2'}} {{word3: text default=websites label='Word 3'}} {{word4: text default=games label='Word 4'}} {{background: color default=#FFFFFF label='Background'}} {{ink: color default=#0F172A label='Text colour'}} {{accent: color default=#6366F1 label='Accent'}} ${typeface(SANS3, 700)} --><defs><clipPath id="window"><rect x="{{clipX}}" y="{{clipY}}" width="{{clipW}}" height="{{clipH}}"/></clipPath></defs><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/><text x="{{px}}" y="{{base}}" textLength="{{plen}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" fill="{{ink}}">{{prefix}}</text><g clip-path="url(#window)"><g id="reel" transform="translate(0 -0)">${Array.from(
|
|
9609
9765
|
{ length: CYCLE_WORDS + 1 },
|
|
9610
|
-
(_, k) => `<text x="{{wx}}" y="{{y${k}}}" textLength="{{len${k}}}" lengthAdjust="spacingAndGlyphs" font-family="
|
|
9766
|
+
(_, k) => `<text x="{{wx}}" y="{{y${k}}}" textLength="{{len${k}}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{bold}}" fill="{{accent}}">{{w${k % CYCLE_WORDS}}}</text>`
|
|
9611
9767
|
).join("")}</g></g><rect id="under" x="{{wx}}" y="{{underY}}" width="{{len0}}" height="4" rx="2" fill="{{accent}}"/></svg>`,
|
|
9612
9768
|
animate: Array.from({ length: CYCLE_WORDS }, (_, i) => {
|
|
9613
9769
|
const at2 = { delay: (i + 1) * SHOW_MS + i * ROLL_MS, ms: ROLL_MS, ease: "in-out" };
|
|
@@ -9618,7 +9774,7 @@ var wordCycle = {
|
|
|
9618
9774
|
}).flat()
|
|
9619
9775
|
}
|
|
9620
9776
|
};
|
|
9621
|
-
var WAVE = `M-
|
|
9777
|
+
var WAVE = `M-2000 0${" q25 -8 50 0 t50 0".repeat(46)} V320 H-2000Z`;
|
|
9622
9778
|
var liquidFill = {
|
|
9623
9779
|
kind: "vector",
|
|
9624
9780
|
id: "liquid-fill",
|
|
@@ -9634,14 +9790,14 @@ var liquidFill = {
|
|
|
9634
9790
|
],
|
|
9635
9791
|
// The surface starts under the letters and ends above their caps.
|
|
9636
9792
|
derived: (v) => {
|
|
9637
|
-
const { size, length } = fit2(String(v.text), 360, 110);
|
|
9793
|
+
const { size, length, left } = fit2(String(v.text), room(v, 360), 110);
|
|
9638
9794
|
const base = 100 + size * 0.36;
|
|
9639
|
-
return { size, length, base: r14(base), low: r14(base + 12), high: r14(base - size * 0.82 - 12) };
|
|
9795
|
+
return { ...frame(v, left, length, base, size), size, length, base: r14(base), low: r14(base + 12), high: r14(base - size * 0.82 - 12) };
|
|
9640
9796
|
},
|
|
9641
9797
|
motion: (() => {
|
|
9642
|
-
const word = `x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="
|
|
9798
|
+
const word = `x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}"`;
|
|
9643
9799
|
return {
|
|
9644
|
-
svg: `<svg viewBox="
|
|
9800
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='FILL' label='Text'}} {{background: color default=#FFFFFF label='Background'}} {{empty: color default=#E0F2FE label='Empty'}} {{liquid: color default=#0284C7 label='Liquid'}} {{liquid2: color default=#38BDF8 label='Liquid sweep'}} ${typeface(HEAVY, 900)} --><defs><clipPath id="letters"><text ${word}>{{text}}</text></clipPath><linearGradient id="liq" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="400" y2="0" spreadMethod="repeat"><stop offset="0" stop-color="{{liquid}}"/><stop offset="0.5" stop-color="{{liquid2}}"/><stop offset="1" stop-color="{{liquid}}"/></linearGradient></defs><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/><text ${word} fill="{{empty}}" stroke="{{liquid}}" stroke-width="1.5" stroke-opacity="0.5">{{text}}</text><g clip-path="url(#letters)"><g id="level" transform="translate(0 {{low}})"><path id="back" d="${WAVE}" fill="{{liquid}}" opacity="0.45" transform="translate(-50 -5)"/><path id="wave" d="${WAVE}" fill="url(#liq)" transform="translate(0 0)"/></g></g></svg>`,
|
|
9645
9801
|
animate: [
|
|
9646
9802
|
{ id: "level", attrs: { transform: ["translate(0 {{low}})", "translate(0 {{high}})"] }, ms: 2800, ease: "out" },
|
|
9647
9803
|
{ id: "wave", attrs: { transform: ["translate(0 0)", "translate(-200 0)"] }, ms: 3e3, ease: "linear" },
|
|
@@ -9683,13 +9839,15 @@ var glitchText = {
|
|
|
9683
9839
|
{ name: "Vapor", values: { left: [1, 0.3, 0.85], right: [0.4, 0.6, 1], background: [0.08, 0.02, 0.12], amount: 1.4 } }
|
|
9684
9840
|
],
|
|
9685
9841
|
derived: (v) => {
|
|
9686
|
-
const { size, length } = fit2(String(v.text), 340, 100);
|
|
9842
|
+
const { size, length, left } = fit2(String(v.text), room(v, 340), 100);
|
|
9843
|
+
const a = Number(v.amount);
|
|
9687
9844
|
const base = 100 + size * 0.36;
|
|
9688
9845
|
const top = base - size * 0.78;
|
|
9689
|
-
const cuts = [
|
|
9846
|
+
const cuts = [-1e3, ...BANDS.map((f) => r14(top + f * (base - top))), 1200];
|
|
9690
9847
|
const unit = size * Number(v.amount);
|
|
9691
9848
|
const shift = ([x, y]) => `translate(${r14(x * unit)} ${r14(y * unit)})`;
|
|
9692
9849
|
return {
|
|
9850
|
+
...frame(v, left, length, base, size, [0.05 + 0.04 * a, 0.25 * a, DESCENT + 0.04 * a, 0.25 * a]),
|
|
9693
9851
|
size,
|
|
9694
9852
|
length,
|
|
9695
9853
|
base: r14(base),
|
|
@@ -9704,10 +9862,10 @@ var glitchText = {
|
|
|
9704
9862
|
};
|
|
9705
9863
|
},
|
|
9706
9864
|
motion: (() => {
|
|
9707
|
-
const word = (paint) => `<text x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="
|
|
9865
|
+
const word = (paint) => `<text x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" ${paint}>{{text}}</text>`;
|
|
9708
9866
|
const jumps = (id, value) => GLITCH.map((key, k) => ({ id, attrs: { transform: [value(k), value(k)] }, delay: key.at, ms: 0 }));
|
|
9709
9867
|
return {
|
|
9710
|
-
svg: `<svg viewBox="
|
|
9868
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='GLITCH' label='Text'}} {{background: color default=#07070B label='Background'}} {{ink: color default=#F5F5F5 label='Text colour'}} {{left: color default=#FF0040 label='Red copy'}} {{right: color default=#00F0FF label='Cyan copy'}} {{amount: float min=0 max=3 default=1 step=0.1 label='Intensity'}} ${typeface(HEAVY, 900)} --><defs>${[0, 1, 2, 3].map((b) => `<clipPath id="slice${b}"><rect x="-5000" y="{{cut${b}}}" width="10400" height="{{band${b}}}"/></clipPath>`).join("")}</defs><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/><g id="red" style="mix-blend-mode:screen">${word(`fill="{{left}}"`)}</g><g id="cyan" style="mix-blend-mode:screen">${word(`fill="{{right}}"`)}</g>${[0, 1, 2, 3].map((b) => `<g clip-path="url(#slice${b})"><g id="s${b}">${word(`fill="{{ink}}"`)}</g></g>`).join("")}</svg>`,
|
|
9711
9869
|
animate: [
|
|
9712
9870
|
...jumps("red", (k) => `{{red${k}}}`),
|
|
9713
9871
|
...jumps("cyan", (k) => `{{cyan${k}}}`),
|
|
@@ -9733,13 +9891,15 @@ var extrudedText = {
|
|
|
9733
9891
|
],
|
|
9734
9892
|
// Each layer sits a step further back along the extrude direction, shaded from the side colour to the shadow.
|
|
9735
9893
|
derived: (v) => {
|
|
9736
|
-
const { size, length } = fit2(String(v.text), 320, 96);
|
|
9894
|
+
const { size, length, left } = fit2(String(v.text), room(v, 320), 96);
|
|
9895
|
+
const d = 0.36 * Number(v.depth);
|
|
9737
9896
|
const step = size * 0.03 * Number(v.depth);
|
|
9738
9897
|
const at2 = (deg, i) => {
|
|
9739
9898
|
const a = deg * Math.PI / 180;
|
|
9740
9899
|
return `translate(${r14(Math.cos(a) * step * i)} ${r14(Math.sin(a) * step * i)})`;
|
|
9741
9900
|
};
|
|
9742
9901
|
return {
|
|
9902
|
+
...frame(v, left, length, 92 + size * 0.35, size, [0.05, 0.77 * d, DESCENT + d, 0.77 * d]),
|
|
9743
9903
|
size,
|
|
9744
9904
|
length,
|
|
9745
9905
|
base: r14(92 + size * 0.35),
|
|
@@ -9756,10 +9916,10 @@ var extrudedText = {
|
|
|
9756
9916
|
};
|
|
9757
9917
|
},
|
|
9758
9918
|
motion: (() => {
|
|
9759
|
-
const word = (paint) => `x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="
|
|
9919
|
+
const word = (paint) => `x="200" y="{{base}}" text-anchor="middle" textLength="{{length}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="{{size}}" font-weight="{{weight}}" stroke-linejoin="round" ${paint}`;
|
|
9760
9920
|
const layers = Array.from({ length: LAYERS }, (_, k) => LAYERS - k);
|
|
9761
9921
|
return {
|
|
9762
|
-
svg: `<svg viewBox="
|
|
9922
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{text: text default='RETRO' label='Text'}} {{background: color default=#FFE8C2 label='Background'}} {{face: color default=#FFD23F label='Face'}} {{outline: color default=#1A1423 label='Outline'}} {{side: color default=#EE4266 label='Extrude'}} {{shade: color default=#3B1F2B label='Shadow'}} {{depth: float min=0.2 max=2 default=1 step=0.1 label='Depth'}} ${typeface(HEAVY, 900)} --><rect width="400" height="200" fill="{{background}}" opacity="{{backdrop}}"/>${layers.map((i) => `<text id="e${i}" transform="translate(0 0)" ${word(`fill="{{fill${i}}}" stroke="{{fill${i}}}" stroke-width="2"`)}>{{text}}</text>`).join("")}<text ${word(`fill="{{face}}" stroke="{{outline}}" stroke-width="2.5" paint-order="stroke"`)}>{{text}}</text></svg>`,
|
|
9763
9923
|
animate: layers.flatMap((i) => [
|
|
9764
9924
|
{ id: `e${i}`, attrs: { transform: ["translate(0 0)", `{{a${i}}}`] }, ms: 700, ease: "out" },
|
|
9765
9925
|
{ id: `e${i}`, attrs: { transform: [`{{a${i}}}`, `{{b${i}}}`] }, delay: 1200, ms: 1500, ease: "in-out" },
|
|
@@ -9782,129 +9942,6 @@ var textEffects = [
|
|
|
9782
9942
|
extrudedText
|
|
9783
9943
|
];
|
|
9784
9944
|
|
|
9785
|
-
// src/catalog/types.ts
|
|
9786
|
-
var EFFECT_CATEGORIES = [
|
|
9787
|
-
"gradient",
|
|
9788
|
-
"organic",
|
|
9789
|
-
"pattern",
|
|
9790
|
-
"light",
|
|
9791
|
-
"space",
|
|
9792
|
-
"retro",
|
|
9793
|
-
"image",
|
|
9794
|
-
"overlay",
|
|
9795
|
-
"text",
|
|
9796
|
-
"morph",
|
|
9797
|
-
"interaction",
|
|
9798
|
-
"pyrotechnics",
|
|
9799
|
-
"timewarp",
|
|
9800
|
-
"melt",
|
|
9801
|
-
"electric",
|
|
9802
|
-
"transition",
|
|
9803
|
-
"loader",
|
|
9804
|
-
"data",
|
|
9805
|
-
"icon"
|
|
9806
|
-
];
|
|
9807
|
-
var VECTOR_CONTROL_TYPES = ["float", "int", "bool", "color", "text"];
|
|
9808
|
-
var PLACEHOLDER = /\{\{\s*([A-Za-z_]\w*)\s*(?::\s*([A-Za-z0-9]*)([^}]*))?\}\}/g;
|
|
9809
|
-
function motionStrings(motion) {
|
|
9810
|
-
const out = [];
|
|
9811
|
-
const walk = (v) => {
|
|
9812
|
-
if (typeof v === "string") out.push(v);
|
|
9813
|
-
else if (Array.isArray(v)) v.forEach(walk);
|
|
9814
|
-
else if (v && typeof v === "object") Object.values(v).forEach(walk);
|
|
9815
|
-
};
|
|
9816
|
-
walk(motion);
|
|
9817
|
-
return out;
|
|
9818
|
-
}
|
|
9819
|
-
function vectorControls(motion) {
|
|
9820
|
-
const controls = [];
|
|
9821
|
-
const seen = /* @__PURE__ */ new Set();
|
|
9822
|
-
for (const text of motionStrings(motion)) {
|
|
9823
|
-
for (const [, name, type, attributes] of text.matchAll(PLACEHOLDER)) {
|
|
9824
|
-
if (type === void 0 || seen.has(name) || !VECTOR_CONTROL_TYPES.includes(type)) continue;
|
|
9825
|
-
seen.add(name);
|
|
9826
|
-
const colour = type === "color";
|
|
9827
|
-
controls.push(controlFromDeclaration(name, colour ? "vec3" : type, `${attributes}${colour ? " color" : ""}`, controls.length + 1));
|
|
9828
|
-
}
|
|
9829
|
-
}
|
|
9830
|
-
return controls;
|
|
9831
|
-
}
|
|
9832
|
-
function vectorEffectControls(effect) {
|
|
9833
|
-
const declared = vectorControls(effect.motion);
|
|
9834
|
-
const names = new Set(declared.map((c) => c.name));
|
|
9835
|
-
const listed = (effect.controls ?? []).filter((c) => !names.has(c.name));
|
|
9836
|
-
return [...declared, ...listed.map((c, i) => vectorControlDef(c, declared.length + i + 1))];
|
|
9837
|
-
}
|
|
9838
|
-
function vectorControlIssues(motion) {
|
|
9839
|
-
const issues = [];
|
|
9840
|
-
const declarations = /* @__PURE__ */ new Map();
|
|
9841
|
-
for (const text of motionStrings(motion)) {
|
|
9842
|
-
for (const [whole, name, type] of text.matchAll(PLACEHOLDER)) {
|
|
9843
|
-
if (type === void 0) continue;
|
|
9844
|
-
if (!VECTOR_CONTROL_TYPES.includes(type))
|
|
9845
|
-
issues.push(`${whole}: a control's type is one of ${VECTOR_CONTROL_TYPES.join(", ")}`);
|
|
9846
|
-
else if (declarations.has(name) && declarations.get(name) !== whole)
|
|
9847
|
-
issues.push(`"${name}" is declared twice; declare it once and write {{${name}}} everywhere else`);
|
|
9848
|
-
else declarations.set(name, whole);
|
|
9849
|
-
}
|
|
9850
|
-
}
|
|
9851
|
-
return issues;
|
|
9852
|
-
}
|
|
9853
|
-
function written(control, value) {
|
|
9854
|
-
if (control.color && Array.isArray(value)) return rgbToHex(value);
|
|
9855
|
-
if (Array.isArray(value)) return value.join(" ");
|
|
9856
|
-
return value;
|
|
9857
|
-
}
|
|
9858
|
-
var escapeMarkup = (text) => text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
9859
|
-
function vectorControlDef(control, line = 0) {
|
|
9860
|
-
const min = control.min ?? 0;
|
|
9861
|
-
return {
|
|
9862
|
-
name: control.name,
|
|
9863
|
-
type: control.type,
|
|
9864
|
-
label: control.label ?? control.name,
|
|
9865
|
-
color: control.color ?? false,
|
|
9866
|
-
min,
|
|
9867
|
-
max: control.max ?? Math.max(min + 1, 1),
|
|
9868
|
-
step: control.step ?? (control.type === "int" ? 1 : 0.01),
|
|
9869
|
-
default: control.default,
|
|
9870
|
-
line
|
|
9871
|
-
};
|
|
9872
|
-
}
|
|
9873
|
-
function renderVectorEffect(effect, values = {}) {
|
|
9874
|
-
const filled = {};
|
|
9875
|
-
const placeholders = /* @__PURE__ */ new Map();
|
|
9876
|
-
for (const control of vectorEffectControls(effect)) {
|
|
9877
|
-
const given = values[control.name];
|
|
9878
|
-
const check = given === void 0 ? void 0 : checkControlValue(control, given);
|
|
9879
|
-
filled[control.name] = check?.ok ? check.value : control.default;
|
|
9880
|
-
placeholders.set(control.name, written(control, filled[control.name]));
|
|
9881
|
-
}
|
|
9882
|
-
for (const [name, value] of Object.entries(effect.derived?.(filled) ?? {})) placeholders.set(name, value);
|
|
9883
|
-
const fill = (text, markup) => {
|
|
9884
|
-
const whole = /^\{\{\s*([A-Za-z_]\w*)\s*(?::[^}]*)?\}\}$/.exec(text);
|
|
9885
|
-
if (!markup && whole && placeholders.has(whole[1])) return placeholders.get(whole[1]);
|
|
9886
|
-
return text.replace(PLACEHOLDER, (match, name) => {
|
|
9887
|
-
if (!placeholders.has(name)) return match;
|
|
9888
|
-
const value = String(placeholders.get(name));
|
|
9889
|
-
return markup ? escapeMarkup(value) : value;
|
|
9890
|
-
});
|
|
9891
|
-
};
|
|
9892
|
-
const walk = (v) => {
|
|
9893
|
-
if (typeof v === "string") return fill(v, false);
|
|
9894
|
-
if (Array.isArray(v)) return v.map(walk);
|
|
9895
|
-
if (v && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk(x)]));
|
|
9896
|
-
return v;
|
|
9897
|
-
};
|
|
9898
|
-
const { svg, ...rest } = effect.motion;
|
|
9899
|
-
return { svg: fill(svg.replace(/<!--[\s\S]*?-->/g, ""), true), ...walk(rest) };
|
|
9900
|
-
}
|
|
9901
|
-
function colorControl(name, hex, label) {
|
|
9902
|
-
return { name, type: "vec3", color: true, default: hexToRgb(hex) ?? [1, 1, 1], label: label ?? name };
|
|
9903
|
-
}
|
|
9904
|
-
function numberControl(name, value, min, max, label, step) {
|
|
9905
|
-
return { name, type: "float", default: value, min, max, label: label ?? name, step };
|
|
9906
|
-
}
|
|
9907
|
-
|
|
9908
9945
|
// src/catalog/vector.ts
|
|
9909
9946
|
var spinner = {
|
|
9910
9947
|
kind: "vector",
|
|
@@ -10317,6 +10354,7 @@ var flowNetwork = {
|
|
|
10317
10354
|
}
|
|
10318
10355
|
};
|
|
10319
10356
|
var DRIP_BASELINE = 120;
|
|
10357
|
+
var DRIP_SIZE = 104;
|
|
10320
10358
|
var DRIP_CAP = 75;
|
|
10321
10359
|
var DRIP_TOP = 106;
|
|
10322
10360
|
var DRIPS2 = [
|
|
@@ -10328,6 +10366,9 @@ var DRIPS2 = [
|
|
|
10328
10366
|
{ at: 0.693, width: 12, length: 42, delay: 60, ms: 1700 },
|
|
10329
10367
|
{ at: 0.793, width: 11, length: 32, delay: 300, ms: 1500 }
|
|
10330
10368
|
];
|
|
10369
|
+
var DRIP_RUN = 300;
|
|
10370
|
+
var DRIP_RUNS = 8;
|
|
10371
|
+
var DRIP_SLOTS = Array.from({ length: DRIP_RUNS }, (_, run) => DRIPS2.map((d, k) => ({ ...d, i: run * DRIPS2.length + k, run, delay: d.delay + run * 90 }))).flat();
|
|
10331
10372
|
var dripText = {
|
|
10332
10373
|
kind: "vector",
|
|
10333
10374
|
id: "drip-text",
|
|
@@ -10340,55 +10381,63 @@ var dripText = {
|
|
|
10340
10381
|
{ name: "Slime", values: { fill: [0.55, 0.93, 0.25], outline: [0.06, 0.2, 0.04], shine: [0.85, 1, 0.7] } },
|
|
10341
10382
|
{ 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 } }
|
|
10342
10383
|
],
|
|
10343
|
-
// The word is
|
|
10384
|
+
// The word is drawn at its own width (textLength from its estimated width), so the drips land under its letters in any font.
|
|
10344
10385
|
derived: (v) => {
|
|
10345
|
-
const
|
|
10346
|
-
const width = Math.min(380, letters * 75);
|
|
10386
|
+
const width = Math.round(Math.max(0.6, textWidthEm(String(v.word).trim())) * DRIP_SIZE);
|
|
10347
10387
|
const left = 200 - width / 2;
|
|
10388
|
+
const runs = Math.min(DRIP_RUNS, Math.max(1, Math.round(width / DRIP_RUN)));
|
|
10389
|
+
const run = width / runs;
|
|
10390
|
+
const r15 = (n5) => Math.round(n5 * 10) / 10;
|
|
10391
|
+
const edge = Number(v.stroke) / 2 / DRIP_SIZE;
|
|
10392
|
+
const tip = (DRIP_TOP + Math.max(...DRIPS2.map((d) => d.length)) * Number(v.drip) + 13 * 0.72 - DRIP_BASELINE) / DRIP_SIZE;
|
|
10393
|
+
const overhang = [edge, edge, Math.max(0.05, tip) + edge, edge].map((e) => Math.round(e * 100) / 100).join(" ");
|
|
10348
10394
|
return {
|
|
10349
10395
|
width,
|
|
10396
|
+
fit: v.inline ? 1 : Math.round(Math.min(1, 380 / width) * 1e3) / 1e3,
|
|
10350
10397
|
// Inline, the picture frames just the word's capitals, from cap height to baseline, so it sits on
|
|
10351
10398
|
// the line like a letter would; the pop, outline and drips spill out over the text around it.
|
|
10352
10399
|
viewBox: v.inline ? `${left} ${DRIP_BASELINE - DRIP_CAP} ${width} ${DRIP_CAP}` : "0 0 400 200",
|
|
10353
10400
|
overflow: v.inline ? "visible" : "hidden",
|
|
10401
|
+
overhang,
|
|
10354
10402
|
...Object.fromEntries(
|
|
10355
|
-
|
|
10356
|
-
const x =
|
|
10357
|
-
const length =
|
|
10403
|
+
DRIP_SLOTS.flatMap((d) => {
|
|
10404
|
+
const x = r15(left + (d.run + d.at) * run);
|
|
10405
|
+
const length = r15(d.length * Number(v.drip));
|
|
10358
10406
|
return [
|
|
10359
|
-
[`
|
|
10360
|
-
[`
|
|
10361
|
-
[`
|
|
10362
|
-
[`
|
|
10363
|
-
[`
|
|
10407
|
+
[`show${d.i}`, d.run < runs ? "inline" : "none"],
|
|
10408
|
+
[`x${d.i}`, x],
|
|
10409
|
+
[`left${d.i}`, r15(x - d.width / 2)],
|
|
10410
|
+
[`shine${d.i}`, r15(x - d.width * 0.25)],
|
|
10411
|
+
[`len${d.i}`, length],
|
|
10412
|
+
[`tip${d.i}`, r15(DRIP_TOP + length)]
|
|
10364
10413
|
];
|
|
10365
10414
|
})
|
|
10366
10415
|
)
|
|
10367
10416
|
};
|
|
10368
10417
|
},
|
|
10369
10418
|
motion: (() => {
|
|
10370
|
-
const word = `<text x="200" y="${DRIP_BASELINE}" text-anchor="middle" textLength="{{width}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="
|
|
10371
|
-
const drips = (layer, paint) =>
|
|
10372
|
-
(d
|
|
10419
|
+
const word = `<text x="200" y="${DRIP_BASELINE}" text-anchor="middle" textLength="{{width}}" lengthAdjust="spacingAndGlyphs" font-family="{{font}}" font-size="${DRIP_SIZE}" font-weight="{{weight}}" stroke-linejoin="round"`;
|
|
10420
|
+
const drips = (layer, paint) => DRIP_SLOTS.map(
|
|
10421
|
+
(d) => `<g display="{{show${d.i}}}"><rect id="${layer}r${d.i}" x="{{left${d.i}}}" y="${DRIP_TOP}" width="${d.width}" height="0" rx="${d.width / 2}" ${paint}/><circle id="${layer}c${d.i}" cx="{{x${d.i}}}" cy="${DRIP_TOP}" r="${d.width * 0.72}" ${paint}/></g>`
|
|
10373
10422
|
).join("");
|
|
10374
10423
|
const outline = `fill="{{outline}}" stroke="{{outline}}" stroke-width="{{stroke}}"`;
|
|
10375
10424
|
const pop = (from, to) => [`translate(200 100) scale(${from}) translate(-200 -100)`, `translate(200 100) scale(${to}) translate(-200 -100)`];
|
|
10376
10425
|
return {
|
|
10377
|
-
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}"><!-- {{word: text default=SLOP label='Word'}} {{font: text default='Arial Black, Impact, system-ui, sans-serif' label='Font'}} {{weight: int min=100 max=900 default=900 step=100 label='Weight'}} {{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'}} {{inline: bool default=false label='Inline'}} --><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>${
|
|
10378
|
-
(d
|
|
10379
|
-
).join("")}</g></svg>`,
|
|
10426
|
+
svg: `<svg viewBox="{{viewBox}}" overflow="{{overflow}}" data-overhang="{{overhang}}"><!-- {{word: text default=SLOP label='Word'}} {{font: text default='Arial Black, Impact, system-ui, sans-serif' label='Font'}} {{weight: int min=100 max=900 default=900 step=100 label='Weight'}} {{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'}} {{inline: bool default=false label='Inline'}} --><g transform="translate(200 100) scale({{fit}}) translate(-200 -100)"><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>${DRIP_SLOTS.map(
|
|
10427
|
+
(d) => `<circle id="s${d.i}" display="{{show${d.i}}}" cx="{{shine${d.i}}}" cy="${DRIP_TOP}" r="${d.width * 0.2}" fill="{{shine}}" opacity="0"/>`
|
|
10428
|
+
).join("")}</g></g></svg>`,
|
|
10380
10429
|
animate: [
|
|
10381
10430
|
{ id: "word", attrs: { transform: pop(0.6, 1.08), opacity: [0, 1] }, ms: 260, ease: "out" },
|
|
10382
10431
|
{ id: "word", attrs: { transform: pop(1.08, 1) }, delay: 260, ms: 200, ease: "in-out" },
|
|
10383
|
-
...
|
|
10432
|
+
...DRIP_SLOTS.flatMap((d) => {
|
|
10384
10433
|
const at2 = { delay: 400 + d.delay, ms: d.ms, ease: "in-out" };
|
|
10385
|
-
const tip = `{{tip${i}}}`;
|
|
10434
|
+
const tip = `{{tip${d.i}}}`;
|
|
10386
10435
|
return [
|
|
10387
10436
|
...["o", "f"].flatMap((layer) => [
|
|
10388
|
-
{ id: `${layer}r${i}`, attrs: { height: [0, `{{len${i}}}`] }, ...at2 },
|
|
10389
|
-
{ id: `${layer}c${i}`, attrs: { cy: [DRIP_TOP, tip] }, ...at2 }
|
|
10437
|
+
{ id: `${layer}r${d.i}`, attrs: { height: [0, `{{len${d.i}}}`] }, ...at2 },
|
|
10438
|
+
{ id: `${layer}c${d.i}`, attrs: { cy: [DRIP_TOP, tip] }, ...at2 }
|
|
10390
10439
|
]),
|
|
10391
|
-
{ id: `s${i}`, attrs: { cy: [DRIP_TOP, tip], opacity: [0, 0.9] }, ...at2 }
|
|
10440
|
+
{ id: `s${d.i}`, attrs: { cy: [DRIP_TOP, tip], opacity: [0, 0.9] }, ...at2 }
|
|
10392
10441
|
];
|
|
10393
10442
|
})
|
|
10394
10443
|
]
|
|
@@ -10518,7 +10567,8 @@ function effectControls(effect) {
|
|
|
10518
10567
|
function effectDefaults(effect, options = {}) {
|
|
10519
10568
|
const preset = options.preset ? effect.presets?.find((p) => p.name === options.preset)?.values : void 0;
|
|
10520
10569
|
const merged = { ...preset, ...options.values };
|
|
10521
|
-
|
|
10570
|
+
const keep = (c) => effect.kind === "vector" && c.color && isCurrentColor(merged[c.name]);
|
|
10571
|
+
return Object.fromEntries(effectControls(effect).map((c) => [c.name, keep(c) ? CURRENT_COLOR : controlValue(c, merged)]));
|
|
10522
10572
|
}
|
|
10523
10573
|
|
|
10524
10574
|
export {
|
|
@@ -10538,6 +10588,8 @@ export {
|
|
|
10538
10588
|
vectorEffectControls,
|
|
10539
10589
|
vectorControlIssues,
|
|
10540
10590
|
vectorControlDef,
|
|
10591
|
+
CURRENT_COLOR,
|
|
10592
|
+
isCurrentColor,
|
|
10541
10593
|
renderVectorEffect,
|
|
10542
10594
|
colorControl,
|
|
10543
10595
|
numberControl,
|
|
@@ -10548,4 +10600,4 @@ export {
|
|
|
10548
10600
|
effectControls,
|
|
10549
10601
|
effectDefaults
|
|
10550
10602
|
};
|
|
10551
|
-
//# sourceMappingURL=chunk-
|
|
10603
|
+
//# sourceMappingURL=chunk-LURJMWYM.js.map
|