pixi-effects 0.9.0 → 0.10.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +6 -6
  3. package/ai/SKILL.md +2 -1
  4. package/ai/reference/cheatsheet.md +13 -5
  5. package/ai/reference/pitfalls.md +2 -2
  6. package/ai/reference/recipes.md +123 -2
  7. package/ai/template.html +3 -3
  8. package/dist/{Base-CSIulyIq.d.cts → Base-CwUxTfja.d.cts} +1 -1
  9. package/dist/{Base-DVgwyIUj.d.ts → Base-CyvSLoCP.d.ts} +1 -1
  10. package/dist/Composition-EKVTUYCI.cjs +14 -0
  11. package/dist/{Composition-CM62XJKT.cjs.map → Composition-EKVTUYCI.cjs.map} +1 -1
  12. package/dist/Composition-NJCOMGD7.js +5 -0
  13. package/dist/{Composition-RJDSQYTV.js.map → Composition-NJCOMGD7.js.map} +1 -1
  14. package/dist/Controller.cjs +34 -0
  15. package/dist/Controller.cjs.map +1 -1
  16. package/dist/Controller.d.cts +12 -2
  17. package/dist/Controller.d.ts +12 -2
  18. package/dist/Controller.js +34 -0
  19. package/dist/Controller.js.map +1 -1
  20. package/dist/{Movie-QMlFyHkf.d.ts → Movie-puD9oZPg.d.ts} +1 -1
  21. package/dist/{Movie-qGHp15c_.d.cts → Movie-w3GliuP3.d.cts} +1 -1
  22. package/dist/{chunk-PDBDDGG5.cjs → chunk-4WJUSDJJ.cjs} +103 -30
  23. package/dist/chunk-4WJUSDJJ.cjs.map +1 -0
  24. package/dist/{chunk-FNKSXBKB.js → chunk-JAMNDBXW.js} +193 -14
  25. package/dist/chunk-JAMNDBXW.js.map +1 -0
  26. package/dist/{chunk-AGMNSJJH.cjs → chunk-S3ZI4MKS.cjs} +196 -12
  27. package/dist/chunk-S3ZI4MKS.cjs.map +1 -0
  28. package/dist/{chunk-O4AJYUPK.js → chunk-WDGAVOMS.js} +85 -12
  29. package/dist/chunk-WDGAVOMS.js.map +1 -0
  30. package/dist/index.cjs +309 -22
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +231 -5
  33. package/dist/index.d.ts +231 -5
  34. package/dist/index.js +282 -11
  35. package/dist/index.js.map +1 -1
  36. package/dist/three.cjs +5 -5
  37. package/dist/three.d.cts +2 -2
  38. package/dist/three.d.ts +2 -2
  39. package/dist/three.js +1 -1
  40. package/dist/{types-oJijbn-K.d.ts → types-BXu28nJj.d.cts} +26 -2
  41. package/dist/{types-oJijbn-K.d.cts → types-BXu28nJj.d.ts} +26 -2
  42. package/docs/api.md +1 -0
  43. package/docs/dsl.md +108 -2
  44. package/llms-full.txt +249 -12
  45. package/package.json +1 -1
  46. package/dist/Composition-CM62XJKT.cjs +0 -14
  47. package/dist/Composition-RJDSQYTV.js +0 -5
  48. package/dist/chunk-AGMNSJJH.cjs.map +0 -1
  49. package/dist/chunk-FNKSXBKB.js.map +0 -1
  50. package/dist/chunk-O4AJYUPK.js.map +0 -1
  51. package/dist/chunk-PDBDDGG5.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkPDBDDGG5_cjs = require('./chunk-PDBDDGG5.cjs');
4
- var chunkAGMNSJJH_cjs = require('./chunk-AGMNSJJH.cjs');
3
+ var chunk4WJUSDJJ_cjs = require('./chunk-4WJUSDJJ.cjs');
4
+ var chunkS3ZI4MKS_cjs = require('./chunk-S3ZI4MKS.cjs');
5
5
  var chunkUQBGW5DO_cjs = require('./chunk-UQBGW5DO.cjs');
6
6
  var PIXI = require('pixi.js');
7
7
  var gsap = require('gsap');
@@ -843,16 +843,16 @@ function expandTransitions(spec) {
843
843
  if (toUses > 1) {
844
844
  throw new Error(`pixi-effects: ${tag} sequence "${t.to}" is used as \`to\` in more than one transition`);
845
845
  }
846
- const tStart = chunkAGMNSJJH_cjs.resolveAt(t.at, parentDuration);
846
+ const tStart = chunkS3ZI4MKS_cjs.resolveAt(t.at, parentDuration);
847
847
  const tEnd = tStart + t.duration;
848
848
  const fromSeq = fromEntry.seq;
849
- const fromStart = chunkAGMNSJJH_cjs.resolveAt(fromSeq.at, parentDuration);
849
+ const fromStart = chunkS3ZI4MKS_cjs.resolveAt(fromSeq.at, parentDuration);
850
850
  const fromEnd = fromStart + (fromSeq.duration ?? parentDuration);
851
851
  if (tStart < fromStart || tEnd > fromEnd) {
852
852
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`from\` "${t.from}" (lives [${fromStart}, ${fromEnd}], so it ends at ${fromEnd})`);
853
853
  }
854
854
  const toSeq = toEntry.seq;
855
- const toStart = chunkAGMNSJJH_cjs.resolveAt(toSeq.at, parentDuration);
855
+ const toStart = chunkS3ZI4MKS_cjs.resolveAt(toSeq.at, parentDuration);
856
856
  const toEnd = toStart + (toSeq.duration ?? parentDuration);
857
857
  if (tStart < toStart || tEnd > toEnd) {
858
858
  throw new Error(`pixi-effects: ${tag} window [${tStart}, ${tEnd}] is not covered by \`to\` "${t.to}" (lives [${toStart}, ${toEnd}], so it starts at ${toStart})`);
@@ -1173,12 +1173,12 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1173
1173
  blending.add(w.to);
1174
1174
  }
1175
1175
  }
1176
- comp.layers().forEach(({ seq, display, threeD }, i) => {
1176
+ comp.layers().forEach(({ seq, display, threeD, carriers }, i) => {
1177
1177
  const t = seq.target;
1178
1178
  const name = seq.spec.name;
1179
1179
  const label = name ?? `${seq.spec.type}#${i}`;
1180
- const alpha = t?.alpha ?? 1;
1181
- const alive = threeD ? display.visible : t?.renderable ?? true;
1180
+ const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1181
+ const alive = (threeD ? display.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1182
1182
  const visible = parentVisible && alive && alpha > 0;
1183
1183
  let bounds = null;
1184
1184
  if (!insideThreeD) {
@@ -1192,10 +1192,17 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1192
1192
  onCanvas = inter === 0 && area > 0 ? "none" : inter >= area - 1e-6 ? "full" : "partial";
1193
1193
  if (area === 0) onCanvas = "none";
1194
1194
  }
1195
- const keys = chunkAGMNSJJH_cjs.collectPropKeys(seq.spec);
1196
- const moving = parentMoving || (seq.spec.keyframes ?? []).length > 0 && (keys.has("x") || keys.has("y"));
1197
- const scale = t?.scale;
1198
- const zero = parentZero || !!scale && (scale.x === 0 || scale.y === 0);
1195
+ const keys = chunkS3ZI4MKS_cjs.collectPropKeys(seq.spec);
1196
+ const carried = carriers.some((c) => {
1197
+ const k = chunkS3ZI4MKS_cjs.collectPropKeys(c.spec);
1198
+ return (c.spec.keyframes ?? []).length > 0 && (k.has("x") || k.has("y") || k.has("rotation") || k.has("scale"));
1199
+ });
1200
+ const moving = parentMoving || carried || (seq.spec.keyframes ?? []).length > 0 && (keys.has("x") || keys.has("y"));
1201
+ const zeroScale = (o) => {
1202
+ const s = o?.scale;
1203
+ return !!s && (s.x === 0 || s.y === 0);
1204
+ };
1205
+ const zero = parentZero || zeroScale(t) || carriers.some((c) => zeroScale(c.target));
1199
1206
  const info = { path: prefix + label, name, type: seq.spec.type, threeD, visible, alpha, moving, bounds, onCanvas };
1200
1207
  layers.push(info);
1201
1208
  let shown = bounds ? clip(bounds, view) : null;
@@ -1208,7 +1215,7 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1208
1215
  const group = parentScene ?? (name !== void 0 && blending.has(name) ? name : void 0);
1209
1216
  if (group) scene.set(info, group);
1210
1217
  if (zero || seq.showsNothingYet) scaledToNothing.add(info);
1211
- if (seq instanceof chunkPDBDDGG5_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
1218
+ if (seq instanceof chunk4WJUSDJJ_cjs.CompositionSequence) walk2(seq, prefix + label + "/", insideThreeD || threeD, visible, group, moving, zero);
1212
1219
  });
1213
1220
  };
1214
1221
  walk2(root, "", false, true);
@@ -1268,10 +1275,10 @@ function walk(comp, parentStart, parentEnd, depth, prefix, rows, transitions) {
1268
1275
  const start = parentStart + (seq.at ?? 0);
1269
1276
  const end = Math.min(parentEnd, seq.duration !== void 0 ? start + seq.duration : parentEnd);
1270
1277
  const span = end - start;
1271
- const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkAGMNSJJH_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1278
+ const keys = (seq.spec.keyframes ?? []).map((kf) => round(start + chunkS3ZI4MKS_cjs.resolveAt(kf.at, span))).sort((a, b) => a - b).slice(0, MAX_KEYS);
1272
1279
  const row = { path: prefix + name, name, type: seq.spec.type, depth, start: round(start), end: round(end), keys, detail: detailOf(seq) };
1273
1280
  const inner = [];
1274
- const isComp = seq instanceof chunkPDBDDGG5_cjs.CompositionSequence;
1281
+ const isComp = seq instanceof chunk4WJUSDJJ_cjs.CompositionSequence;
1275
1282
  if (isComp) walk(seq, start, end, depth + 1, prefix + name + "/", inner, transitions);
1276
1283
  entries.push({ row, inner, groupable: !isComp });
1277
1284
  });
@@ -1328,7 +1335,8 @@ var COLORS = {
1328
1335
  audio: "#ff7aa8",
1329
1336
  composition: "#8ea4c8",
1330
1337
  three: "#ff9f68",
1331
- camera: "#b0b0b0"
1338
+ camera: "#b0b0b0",
1339
+ null: "#b0b0b0"
1332
1340
  };
1333
1341
  var esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1334
1342
  var num = (n) => String(Math.round(n * 100) / 100);
@@ -1610,7 +1618,7 @@ var Movie = class {
1610
1618
  height: options.composition.height ?? this.height,
1611
1619
  duration: options.composition.duration ?? this.duration
1612
1620
  } : void 0;
1613
- await chunkAGMNSJJH_cjs.ensureFilterLibrary(seededComposition);
1621
+ await chunkS3ZI4MKS_cjs.ensureFilterLibrary(seededComposition);
1614
1622
  const userComposition = seededComposition ? expandTransitions(seededComposition) : void 0;
1615
1623
  const rootSeqSpec = {
1616
1624
  type: "composition",
@@ -1620,7 +1628,7 @@ var Movie = class {
1620
1628
  ...userComposition
1621
1629
  };
1622
1630
  if (userComposition) carryTransitionWindows(userComposition, rootSeqSpec);
1623
- const composition = new chunkPDBDDGG5_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
1631
+ const composition = new chunk4WJUSDJJ_cjs.CompositionSequence(rootSeqSpec, null, rootShape);
1624
1632
  await composition.build();
1625
1633
  this._rootSequence = composition;
1626
1634
  if (composition.target) root.addChild(composition.target);
@@ -2119,17 +2127,17 @@ function orbit(opts) {
2119
2127
  throw new Error("pixi-effects: orbit() cannot combine `radius` with `dollyZoom` \u2014 the radius follows the animated fov");
2120
2128
  }
2121
2129
  const [cx, cy] = opts.center ?? [width / 2, height / 2];
2122
- const fixedR = opts.radius ?? chunkAGMNSJJH_cjs.homeDistance(height, fov ?? chunkAGMNSJJH_cjs.DEFAULT_FOV);
2130
+ const fixedR = opts.radius ?? chunkS3ZI4MKS_cjs.homeDistance(height, fov ?? chunkS3ZI4MKS_cjs.DEFAULT_FOV);
2123
2131
  const start = opts.start ?? -degrees / 2;
2124
2132
  const steps = Math.max(1, Math.round(duration * stepsPerSecond));
2125
2133
  const easeFn = parseEase(ease);
2126
2134
  const dollyEase = dolly ? parseEase(dolly.ease ?? ease) : null;
2127
2135
  const pos = (i) => {
2128
2136
  const p = i / steps;
2129
- const a = (start + degrees * easeFn(p)) * chunkAGMNSJJH_cjs.DEG;
2137
+ const a = (start + degrees * easeFn(p)) * chunkS3ZI4MKS_cjs.DEG;
2130
2138
  if (!dolly) return { x: cx + fixedR * Math.sin(a), z: fixedR * Math.cos(a) };
2131
2139
  const f = dolly.from + (dolly.to - dolly.from) * dollyEase(p);
2132
- const R = chunkAGMNSJJH_cjs.homeDistance(height, f);
2140
+ const R = chunkS3ZI4MKS_cjs.homeDistance(height, f);
2133
2141
  return { x: cx + R * Math.sin(a), z: R * Math.cos(a), fov: f };
2134
2142
  };
2135
2143
  const keyframes = [];
@@ -2156,6 +2164,269 @@ function orbit(opts) {
2156
2164
  return spec;
2157
2165
  }
2158
2166
 
2167
+ // src/presets/followPath.ts
2168
+ var KEYS = ["d", "at", "duration", "ease", "from", "to", "orient", "rotate", "frameRate"];
2169
+ var MAX_STEPS = 2e3;
2170
+ function followPath(opts) {
2171
+ chunkUQBGW5DO_cjs.warnUnknownOptions("followPath()", opts, KEYS);
2172
+ const { d, at = 0, duration, ease = "none", from = 0, to = 1, orient = false, rotate = 0, frameRate = 30 } = opts;
2173
+ if (typeof d !== "string" || d.trim() === "") throw new Error('followPath(): d must be SVG path data, e.g. "M 100 600 C 300 100 900 100 1180 600"');
2174
+ if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`followPath(): duration must be a positive number of seconds, got ${duration}`);
2175
+ if (!(Number.isFinite(frameRate) && frameRate > 0)) throw new Error(`followPath(): frameRate must be a positive number (samples per second), got ${frameRate}`);
2176
+ for (const [n, v] of [["from", from], ["to", to]]) {
2177
+ if (!(Number.isFinite(v) && v >= 0 && v <= 1)) throw new Error(`followPath(): ${n} must be a fraction of the path between 0 and 1, got ${v}`);
2178
+ }
2179
+ const steps = Math.max(1, Math.round(duration * frameRate));
2180
+ if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
2181
+ const segments = [];
2182
+ let total = 0;
2183
+ for (const line of chunkS3ZI4MKS_cjs.flattenSvgPath(d)) {
2184
+ const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
2185
+ for (let i = 2; i < p.length; i += 2) {
2186
+ const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
2187
+ if (len === 0) continue;
2188
+ segments.push({ x0: p[i - 2], y0: p[i - 1], x1: p[i], y1: p[i + 1], start: total, len });
2189
+ total += len;
2190
+ }
2191
+ }
2192
+ if (!(total > 0)) throw new Error(`followPath(): the path "${d}" has no length (it needs at least two different points)`);
2193
+ const find = (s) => {
2194
+ let lo = 0, hi = segments.length - 1;
2195
+ while (lo < hi) {
2196
+ const mid = lo + hi >> 1;
2197
+ if (segments[mid].start + segments[mid].len < s) lo = mid + 1;
2198
+ else hi = mid;
2199
+ }
2200
+ return segments[lo];
2201
+ };
2202
+ const at_ = (fraction) => {
2203
+ const s = Math.min(Math.max(fraction, 0), 1) * total;
2204
+ const g = find(s);
2205
+ const t = Math.min(Math.max((s - g.start) / g.len, 0), 1);
2206
+ return { x: g.x0 + (g.x1 - g.x0) * t, y: g.y0 + (g.y1 - g.y0) * t, heading: Math.atan2(g.y1 - g.y0, g.x1 - g.x0) * 180 / Math.PI };
2207
+ };
2208
+ const shape = parseEase(ease);
2209
+ const round2 = (v) => Math.round(v * 1e6) / 1e6;
2210
+ let turn = 0;
2211
+ const sample = (k) => {
2212
+ const p = from + (to - from) * shape(k / steps);
2213
+ const pt = at_(p);
2214
+ const out2 = { x: round2(pt.x), y: round2(pt.y) };
2215
+ if (orient) {
2216
+ let h = pt.heading + rotate;
2217
+ if (k === 0) turn = h;
2218
+ else {
2219
+ let delta = h - turn;
2220
+ delta -= 360 * Math.round(delta / 360);
2221
+ turn += delta;
2222
+ }
2223
+ out2.rotation = round2(turn);
2224
+ }
2225
+ return out2;
2226
+ };
2227
+ const out = [{ at, set: sample(0) }];
2228
+ const step = duration / steps;
2229
+ for (let k = 1; k <= steps; k++) out.push({ at: round2(at + (k - 1) * step), to: sample(k), duration: round2(step), ease: "none" });
2230
+ return out;
2231
+ }
2232
+
2233
+ // src/presets/stagger.ts
2234
+ var STAGGER_KEYS = ["each", "amount", "from", "grid", "ease", "seed"];
2235
+ var FROM_NAMES = ["start", "end", "center", "edges", "random"];
2236
+ function stagger(items, options = {}) {
2237
+ const isCount = typeof items === "number";
2238
+ const n = isCount ? items : items.length;
2239
+ if (!Number.isInteger(n) || n < 0) throw new Error(`stagger(): count must be a whole number of items, got ${n}`);
2240
+ chunkUQBGW5DO_cjs.warnUnknownOptions("stagger()", options, STAGGER_KEYS);
2241
+ const { each, amount, from = "start", grid, ease = "none", seed = 0 } = options;
2242
+ if (each !== void 0 && amount !== void 0) throw new Error("stagger(): give `each` (seconds between neighbours) or `amount` (seconds in total), not both");
2243
+ if (each !== void 0 && !(Number.isFinite(each) && each >= 0)) throw new Error(`stagger(): each must be a number of seconds >= 0, got ${each}`);
2244
+ if (amount !== void 0 && !(Number.isFinite(amount) && amount >= 0)) throw new Error(`stagger(): amount must be a number of seconds >= 0, got ${amount}`);
2245
+ if (typeof from === "string" && !FROM_NAMES.includes(from)) {
2246
+ throw new Error(`stagger(): from "${from}" is not one of ${FROM_NAMES.map((f) => `"${f}"`).join(", ")} or an item index`);
2247
+ }
2248
+ if (typeof from === "number" && !(Number.isInteger(from) && from >= 0 && from < Math.max(n, 1))) throw new Error(`stagger(): from ${from} is not an index of the ${n} items`);
2249
+ if (grid && !(Array.isArray(grid) && grid.length === 2 && grid.every((g) => Number.isInteger(g) && g > 0) && grid[0] * grid[1] >= n)) {
2250
+ throw new Error(`stagger(): grid must be [columns, rows] and hold all ${n} items, got ${JSON.stringify(grid)}`);
2251
+ }
2252
+ const dist = distances(n, from, grid, seed);
2253
+ const max = dist.reduce((m, d) => Math.max(m, d), 0);
2254
+ const total = amount !== void 0 ? amount : (each ?? 0.1) * max;
2255
+ const shape = parseEase(ease);
2256
+ const delays = dist.map((d) => max === 0 ? 0 : Math.round(total * shape(d / max) * 1e6) / 1e6);
2257
+ if (isCount) return delays;
2258
+ return items.map((layer, i) => ({ ...layer, at: Math.round(((layer.at ?? 0) + delays[i]) * 1e6) / 1e6 }));
2259
+ }
2260
+ function distances(n, from, grid, seed) {
2261
+ if (n === 0) return [];
2262
+ if (from === "random") {
2263
+ const order = Array.from({ length: n }, (_, i) => i);
2264
+ const r = chunkS3ZI4MKS_cjs.random(seed);
2265
+ for (let i = n - 1; i > 0; i--) {
2266
+ const j = Math.floor(r() * (i + 1));
2267
+ [order[i], order[j]] = [order[j], order[i]];
2268
+ }
2269
+ return order;
2270
+ }
2271
+ if (!grid) {
2272
+ const mid = (n - 1) / 2;
2273
+ return Array.from({ length: n }, (_, i) => {
2274
+ switch (from) {
2275
+ case "start":
2276
+ return i;
2277
+ case "end":
2278
+ return n - 1 - i;
2279
+ case "center":
2280
+ return Math.abs(i - mid);
2281
+ case "edges":
2282
+ return mid - Math.abs(i - mid);
2283
+ default:
2284
+ return Math.abs(i - from);
2285
+ }
2286
+ });
2287
+ }
2288
+ const [cols, rows] = grid;
2289
+ const at = (i) => ({ x: i % cols, y: Math.floor(i / cols) });
2290
+ const origin = from === "start" ? { x: 0, y: 0 } : from === "end" ? { x: cols - 1, y: rows - 1 } : from === "center" || from === "edges" ? { x: (cols - 1) / 2, y: (rows - 1) / 2 } : at(from);
2291
+ const d = Array.from({ length: n }, (_, i) => Math.hypot(at(i).x - origin.x, at(i).y - origin.y));
2292
+ if (from !== "edges") return d;
2293
+ const far = Math.max(...d);
2294
+ return d.map((v) => far - v);
2295
+ }
2296
+
2297
+ // src/presets/wiggle.ts
2298
+ var WIGGLE_KEYS = ["at", "duration", "freq", "seed", "ease", "props"];
2299
+ var MAX_STEPS2 = 2e3;
2300
+ function wiggle(opts) {
2301
+ chunkUQBGW5DO_cjs.warnUnknownOptions("wiggle()", opts, WIGGLE_KEYS);
2302
+ const { at = 0, duration, freq = 3, seed = 0, ease = "sine.inOut", props } = opts;
2303
+ if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`wiggle(): duration must be a positive number of seconds, got ${duration}`);
2304
+ if (!(Number.isFinite(freq) && freq > 0)) throw new Error(`wiggle(): freq must be a positive number (steps per second), got ${freq}`);
2305
+ const names = Object.keys(props ?? {});
2306
+ if (names.length === 0) throw new Error("wiggle(): props is empty; name at least one property, e.g. props: { x: { around: 100, amp: 8 } }");
2307
+ for (const n of names) {
2308
+ const p = props[n];
2309
+ if (!Number.isFinite(p.amp)) throw new Error(`wiggle(): props.${n}.amp must be a number, got ${p.amp}`);
2310
+ if (p.around === void 0 || p.around === null || p.around === "") throw new Error(`wiggle(): props.${n}.around is missing`);
2311
+ }
2312
+ const steps = Math.ceil(duration * freq - 1e-9);
2313
+ if (steps > MAX_STEPS2) throw new Error(`wiggle(): ${steps} steps is too many (max ${MAX_STEPS2}); lower freq or duration`);
2314
+ const set = {};
2315
+ for (const n of names) set[n] = props[n].around;
2316
+ const out = [{ at, set }];
2317
+ const stepLen = duration / steps;
2318
+ for (let s = 0; s < steps; s++) {
2319
+ const to = {};
2320
+ names.forEach((n, pi) => {
2321
+ const p = props[n];
2322
+ if (s === steps - 1) {
2323
+ to[n] = p.around;
2324
+ return;
2325
+ }
2326
+ const off = (chunkS3ZI4MKS_cjs.rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
2327
+ to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
2328
+ });
2329
+ out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
2330
+ }
2331
+ return out;
2332
+ }
2333
+
2334
+ // src/presets/animateText.ts
2335
+ var KEYS2 = ["by", "x", "y", "align", "name", "at", "duration", "in", "out", "stagger", "idle", "styleFor"];
2336
+ var PRESETS = {
2337
+ rise: { from: { y: 40, alpha: 0 }, duration: 0.5, ease: "back.out(2)" },
2338
+ drop: { from: { y: -50, alpha: 0 }, duration: 0.7, ease: "bounce.out" },
2339
+ fade: { from: { alpha: 0 }, duration: 0.5, ease: "power1.out" },
2340
+ pop: { from: { scale: 0, alpha: 0 }, duration: 0.45, ease: "back.out(2.4)" },
2341
+ zoom: { from: { scale: 2.5, alpha: 0 }, duration: 0.6, ease: "power3.out" },
2342
+ slide: { from: { x: -60, alpha: 0 }, duration: 0.5, ease: "power3.out" },
2343
+ spin: { from: { rotation: -90, scale: 0, alpha: 0 }, duration: 0.6, ease: "back.out(1.7)" }
2344
+ };
2345
+ var PRESET_NAMES = Object.keys(PRESETS);
2346
+ function animateText(text, style, options) {
2347
+ chunkUQBGW5DO_cjs.warnUnknownOptions("animateText()", options, KEYS2);
2348
+ const { by = "chars", x = 0, y = 0, align = "left", name = "text", at = 0, duration, idle, styleFor } = options;
2349
+ if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`animateText(): duration must be a positive number of seconds (how long until the last piece is gone), got ${duration}`);
2350
+ const pieces = splitText(text, style, { by, x, y, align });
2351
+ if (pieces.length === 0) return [];
2352
+ const lineHeight = measureText("M", style).height;
2353
+ const inTween = options.in === false ? null : resolveTween(options.in ?? "rise", "in");
2354
+ const outTween = options.out === void 0 || options.out === false ? null : resolveTween(options.out, "out");
2355
+ const delays = stagger(pieces.length, options.stagger ?? { each: 0.04 });
2356
+ const wave = Math.max(...delays);
2357
+ const need = (inTween?.duration ?? 0) + (outTween?.duration ?? 0) + wave;
2358
+ if (duration < need - 1e-9) {
2359
+ throw new Error(`animateText(): duration ${duration} is too short for this wave: the entrance${outTween ? ", the exit" : ""} and the stagger need at least ${+need.toFixed(3)} s`);
2360
+ }
2361
+ const end = at + duration;
2362
+ return pieces.map((p, i) => {
2363
+ const cx = p.x + p.width / 2, cy = p.y + lineHeight / 2;
2364
+ const rest = { x: cx, y: cy, alpha: 1, scale: 1, scaleX: 1, scaleY: 1, rotation: 0 };
2365
+ const start = at + delays[i];
2366
+ const len = end - start;
2367
+ const keyframes = [];
2368
+ const place = (key, v) => key === "x" || key === "y" ? rest[key] + v : v;
2369
+ if (inTween) {
2370
+ keyframes.push({
2371
+ at: 0,
2372
+ duration: inTween.duration,
2373
+ ease: inTween.ease,
2374
+ from: Object.fromEntries(Object.entries(inTween.from).map(([k, v]) => [k, place(k, v)])),
2375
+ to: Object.fromEntries(Object.keys(inTween.from).map((k) => [k, inTween.to[k] ?? rest[k]]))
2376
+ });
2377
+ }
2378
+ let idleEnd = len;
2379
+ if (outTween) {
2380
+ const begin = outTween.duration + (wave - delays[i]);
2381
+ idleEnd = len - begin;
2382
+ keyframes.push({
2383
+ at: -begin,
2384
+ duration: outTween.duration,
2385
+ ease: outTween.ease,
2386
+ from: Object.fromEntries(Object.keys(outTween.from).map((k) => [k, outTween.to[k] ?? rest[k]])),
2387
+ to: Object.fromEntries(Object.entries(outTween.from).map(([k, v]) => [k, place(k, v)]))
2388
+ });
2389
+ }
2390
+ if (idle) {
2391
+ const props = {};
2392
+ for (const k of ["x", "y", "rotation", "scale"]) if (idle[k] !== void 0) props[k] = { around: rest[k], amp: idle[k] };
2393
+ const freq = idle.freq ?? 1;
2394
+ const from = inTween?.duration ?? 0;
2395
+ if (Object.keys(props).length > 0 && idleEnd - from >= 1 / freq) {
2396
+ keyframes.push(...wiggle({ at: from, duration: idleEnd - from, freq, seed: (idle.seed ?? 0) * 1e3 + i, props }));
2397
+ }
2398
+ }
2399
+ return {
2400
+ type: "text",
2401
+ name: `${name}-${i}`,
2402
+ text: p.text,
2403
+ style: styleFor ? { ...style, ...styleFor(p) } : style,
2404
+ at: start,
2405
+ duration: len,
2406
+ initial: { x: cx, y: cy, anchorX: 0.5, anchorY: 0.5 },
2407
+ keyframes
2408
+ };
2409
+ });
2410
+ }
2411
+ function resolveTween(spec, which) {
2412
+ const t = typeof spec === "string" ? { preset: spec } : spec;
2413
+ if (t.preset !== void 0 && !(t.preset in PRESETS)) {
2414
+ const guess = chunkUQBGW5DO_cjs.suggestName(String(t.preset), PRESET_NAMES);
2415
+ throw new Error(`animateText(): ${which} "${t.preset}" is not a preset${guess ? `; did you mean "${guess}"?` : ""} (presets: ${PRESET_NAMES.map((n) => `"${n}"`).join(", ")})`);
2416
+ }
2417
+ const base = t.preset ? PRESETS[t.preset] : void 0;
2418
+ const from = { ...base?.from ?? {}, ...t.from ?? {} };
2419
+ const to = t.to ?? {};
2420
+ const known = /* @__PURE__ */ new Set(["x", "y", "alpha", "scale", "scaleX", "scaleY", "rotation"]);
2421
+ for (const k of Object.keys(from)) {
2422
+ if (!known.has(k) && to[k] === void 0) throw new Error(`animateText(): ${which}.from has "${k}", which has no resting value; give ${which}.to: { ${k}: \u2026 }`);
2423
+ }
2424
+ if (Object.keys(from).length === 0) throw new Error(`animateText(): ${which} needs a preset or a from`);
2425
+ let ease = t.ease ?? base?.ease ?? "power2.out";
2426
+ if (which === "out" && t.ease === void 0) ease = ease.replace(".out", ".in");
2427
+ return { from, to: { ...to }, duration: t.duration ?? base?.duration ?? 0.5, ease };
2428
+ }
2429
+
2159
2430
  // src/transforms/withFade.ts
2160
2431
  function withFade(spec, opts) {
2161
2432
  chunkUQBGW5DO_cjs.warnUnknownOptions("withFade()", opts, ["in", "out"]);
@@ -2176,15 +2447,31 @@ function withFade(spec, opts) {
2176
2447
  return spec;
2177
2448
  }
2178
2449
 
2450
+ Object.defineProperty(exports, "noise", {
2451
+ enumerable: true,
2452
+ get: function () { return chunkS3ZI4MKS_cjs.noise; }
2453
+ });
2454
+ Object.defineProperty(exports, "rand", {
2455
+ enumerable: true,
2456
+ get: function () { return chunkS3ZI4MKS_cjs.rand; }
2457
+ });
2458
+ Object.defineProperty(exports, "random", {
2459
+ enumerable: true,
2460
+ get: function () { return chunkS3ZI4MKS_cjs.random; }
2461
+ });
2179
2462
  Object.defineProperty(exports, "registerSequenceType", {
2180
2463
  enumerable: true,
2181
- get: function () { return chunkAGMNSJJH_cjs.registerSequenceType; }
2464
+ get: function () { return chunkS3ZI4MKS_cjs.registerSequenceType; }
2182
2465
  });
2183
2466
  exports.Movie = Movie;
2467
+ exports.animateText = animateText;
2468
+ exports.followPath = followPath;
2184
2469
  exports.kenBurns = kenBurns;
2185
2470
  exports.measureText = measureText;
2186
2471
  exports.orbit = orbit;
2187
2472
  exports.splitText = splitText;
2473
+ exports.stagger = stagger;
2474
+ exports.wiggle = wiggle;
2188
2475
  exports.withFade = withFade;
2189
2476
  //# sourceMappingURL=index.cjs.map
2190
2477
  //# sourceMappingURL=index.cjs.map