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.js CHANGED
@@ -1,6 +1,6 @@
1
- import { CompositionSequence } from './chunk-O4AJYUPK.js';
2
- import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, DEG, resolveAt, collectPropKeys } from './chunk-FNKSXBKB.js';
3
- export { registerSequenceType } from './chunk-FNKSXBKB.js';
1
+ import { CompositionSequence } from './chunk-WDGAVOMS.js';
2
+ import { ensureFilterLibrary, homeDistance, DEFAULT_FOV, flattenSvgPath, random, rand, DEG, resolveAt, collectPropKeys } from './chunk-JAMNDBXW.js';
3
+ export { noise, rand, random, registerSequenceType } from './chunk-JAMNDBXW.js';
4
4
  import { warnUnknownOptions, suggestName } from './chunk-DKGG7WD4.js';
5
5
  import * as PIXI from 'pixi.js';
6
6
  import { extensions, CullerPlugin, Application, Container, Rectangle, Culler, CanvasTextMetrics, TextStyle, Assets, ExtensionType, Filter, UniformGroup, GpuProgram, GlProgram, defaultFilterVert } from 'pixi.js';
@@ -1153,12 +1153,12 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1153
1153
  blending.add(w.to);
1154
1154
  }
1155
1155
  }
1156
- comp.layers().forEach(({ seq, display, threeD }, i) => {
1156
+ comp.layers().forEach(({ seq, display, threeD, carriers }, i) => {
1157
1157
  const t = seq.target;
1158
1158
  const name = seq.spec.name;
1159
1159
  const label = name ?? `${seq.spec.type}#${i}`;
1160
- const alpha = t?.alpha ?? 1;
1161
- const alive = threeD ? display.visible : t?.renderable ?? true;
1160
+ const alpha = (t?.alpha ?? 1) * carriers.reduce((a, c) => a * (c.target?.alpha ?? 1), 1);
1161
+ const alive = (threeD ? display.visible : t?.renderable ?? true) && carriers.every((c) => c.target?.renderable ?? true);
1162
1162
  const visible = parentVisible && alive && alpha > 0;
1163
1163
  let bounds = null;
1164
1164
  if (!insideThreeD) {
@@ -1173,9 +1173,16 @@ function inspectScene(root, frame, time, canvas, opts = {}) {
1173
1173
  if (area === 0) onCanvas = "none";
1174
1174
  }
1175
1175
  const keys = collectPropKeys(seq.spec);
1176
- const moving = parentMoving || (seq.spec.keyframes ?? []).length > 0 && (keys.has("x") || keys.has("y"));
1177
- const scale = t?.scale;
1178
- const zero = parentZero || !!scale && (scale.x === 0 || scale.y === 0);
1176
+ const carried = carriers.some((c) => {
1177
+ const k = collectPropKeys(c.spec);
1178
+ return (c.spec.keyframes ?? []).length > 0 && (k.has("x") || k.has("y") || k.has("rotation") || k.has("scale"));
1179
+ });
1180
+ const moving = parentMoving || carried || (seq.spec.keyframes ?? []).length > 0 && (keys.has("x") || keys.has("y"));
1181
+ const zeroScale = (o) => {
1182
+ const s = o?.scale;
1183
+ return !!s && (s.x === 0 || s.y === 0);
1184
+ };
1185
+ const zero = parentZero || zeroScale(t) || carriers.some((c) => zeroScale(c.target));
1179
1186
  const info = { path: prefix + label, name, type: seq.spec.type, threeD, visible, alpha, moving, bounds, onCanvas };
1180
1187
  layers.push(info);
1181
1188
  let shown = bounds ? clip(bounds, view) : null;
@@ -1308,7 +1315,8 @@ var COLORS = {
1308
1315
  audio: "#ff7aa8",
1309
1316
  composition: "#8ea4c8",
1310
1317
  three: "#ff9f68",
1311
- camera: "#b0b0b0"
1318
+ camera: "#b0b0b0",
1319
+ null: "#b0b0b0"
1312
1320
  };
1313
1321
  var esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1314
1322
  var num = (n) => String(Math.round(n * 100) / 100);
@@ -2136,6 +2144,269 @@ function orbit(opts) {
2136
2144
  return spec;
2137
2145
  }
2138
2146
 
2147
+ // src/presets/followPath.ts
2148
+ var KEYS = ["d", "at", "duration", "ease", "from", "to", "orient", "rotate", "frameRate"];
2149
+ var MAX_STEPS = 2e3;
2150
+ function followPath(opts) {
2151
+ warnUnknownOptions("followPath()", opts, KEYS);
2152
+ const { d, at = 0, duration, ease = "none", from = 0, to = 1, orient = false, rotate = 0, frameRate = 30 } = opts;
2153
+ 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"');
2154
+ if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`followPath(): duration must be a positive number of seconds, got ${duration}`);
2155
+ if (!(Number.isFinite(frameRate) && frameRate > 0)) throw new Error(`followPath(): frameRate must be a positive number (samples per second), got ${frameRate}`);
2156
+ for (const [n, v] of [["from", from], ["to", to]]) {
2157
+ 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}`);
2158
+ }
2159
+ const steps = Math.max(1, Math.round(duration * frameRate));
2160
+ if (steps > MAX_STEPS) throw new Error(`followPath(): ${steps} samples is too many (max ${MAX_STEPS}); lower frameRate or duration`);
2161
+ const segments = [];
2162
+ let total = 0;
2163
+ for (const line of flattenSvgPath(d)) {
2164
+ const p = line.closed ? [...line.pts, line.pts[0], line.pts[1]] : line.pts;
2165
+ for (let i = 2; i < p.length; i += 2) {
2166
+ const len = Math.hypot(p[i] - p[i - 2], p[i + 1] - p[i - 1]);
2167
+ if (len === 0) continue;
2168
+ segments.push({ x0: p[i - 2], y0: p[i - 1], x1: p[i], y1: p[i + 1], start: total, len });
2169
+ total += len;
2170
+ }
2171
+ }
2172
+ if (!(total > 0)) throw new Error(`followPath(): the path "${d}" has no length (it needs at least two different points)`);
2173
+ const find = (s) => {
2174
+ let lo = 0, hi = segments.length - 1;
2175
+ while (lo < hi) {
2176
+ const mid = lo + hi >> 1;
2177
+ if (segments[mid].start + segments[mid].len < s) lo = mid + 1;
2178
+ else hi = mid;
2179
+ }
2180
+ return segments[lo];
2181
+ };
2182
+ const at_ = (fraction) => {
2183
+ const s = Math.min(Math.max(fraction, 0), 1) * total;
2184
+ const g = find(s);
2185
+ const t = Math.min(Math.max((s - g.start) / g.len, 0), 1);
2186
+ 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 };
2187
+ };
2188
+ const shape = parseEase(ease);
2189
+ const round2 = (v) => Math.round(v * 1e6) / 1e6;
2190
+ let turn = 0;
2191
+ const sample = (k) => {
2192
+ const p = from + (to - from) * shape(k / steps);
2193
+ const pt = at_(p);
2194
+ const out2 = { x: round2(pt.x), y: round2(pt.y) };
2195
+ if (orient) {
2196
+ let h = pt.heading + rotate;
2197
+ if (k === 0) turn = h;
2198
+ else {
2199
+ let delta = h - turn;
2200
+ delta -= 360 * Math.round(delta / 360);
2201
+ turn += delta;
2202
+ }
2203
+ out2.rotation = round2(turn);
2204
+ }
2205
+ return out2;
2206
+ };
2207
+ const out = [{ at, set: sample(0) }];
2208
+ const step = duration / steps;
2209
+ for (let k = 1; k <= steps; k++) out.push({ at: round2(at + (k - 1) * step), to: sample(k), duration: round2(step), ease: "none" });
2210
+ return out;
2211
+ }
2212
+
2213
+ // src/presets/stagger.ts
2214
+ var STAGGER_KEYS = ["each", "amount", "from", "grid", "ease", "seed"];
2215
+ var FROM_NAMES = ["start", "end", "center", "edges", "random"];
2216
+ function stagger(items, options = {}) {
2217
+ const isCount = typeof items === "number";
2218
+ const n = isCount ? items : items.length;
2219
+ if (!Number.isInteger(n) || n < 0) throw new Error(`stagger(): count must be a whole number of items, got ${n}`);
2220
+ warnUnknownOptions("stagger()", options, STAGGER_KEYS);
2221
+ const { each, amount, from = "start", grid, ease = "none", seed = 0 } = options;
2222
+ if (each !== void 0 && amount !== void 0) throw new Error("stagger(): give `each` (seconds between neighbours) or `amount` (seconds in total), not both");
2223
+ if (each !== void 0 && !(Number.isFinite(each) && each >= 0)) throw new Error(`stagger(): each must be a number of seconds >= 0, got ${each}`);
2224
+ if (amount !== void 0 && !(Number.isFinite(amount) && amount >= 0)) throw new Error(`stagger(): amount must be a number of seconds >= 0, got ${amount}`);
2225
+ if (typeof from === "string" && !FROM_NAMES.includes(from)) {
2226
+ throw new Error(`stagger(): from "${from}" is not one of ${FROM_NAMES.map((f) => `"${f}"`).join(", ")} or an item index`);
2227
+ }
2228
+ 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`);
2229
+ if (grid && !(Array.isArray(grid) && grid.length === 2 && grid.every((g) => Number.isInteger(g) && g > 0) && grid[0] * grid[1] >= n)) {
2230
+ throw new Error(`stagger(): grid must be [columns, rows] and hold all ${n} items, got ${JSON.stringify(grid)}`);
2231
+ }
2232
+ const dist = distances(n, from, grid, seed);
2233
+ const max = dist.reduce((m, d) => Math.max(m, d), 0);
2234
+ const total = amount !== void 0 ? amount : (each ?? 0.1) * max;
2235
+ const shape = parseEase(ease);
2236
+ const delays = dist.map((d) => max === 0 ? 0 : Math.round(total * shape(d / max) * 1e6) / 1e6);
2237
+ if (isCount) return delays;
2238
+ return items.map((layer, i) => ({ ...layer, at: Math.round(((layer.at ?? 0) + delays[i]) * 1e6) / 1e6 }));
2239
+ }
2240
+ function distances(n, from, grid, seed) {
2241
+ if (n === 0) return [];
2242
+ if (from === "random") {
2243
+ const order = Array.from({ length: n }, (_, i) => i);
2244
+ const r = random(seed);
2245
+ for (let i = n - 1; i > 0; i--) {
2246
+ const j = Math.floor(r() * (i + 1));
2247
+ [order[i], order[j]] = [order[j], order[i]];
2248
+ }
2249
+ return order;
2250
+ }
2251
+ if (!grid) {
2252
+ const mid = (n - 1) / 2;
2253
+ return Array.from({ length: n }, (_, i) => {
2254
+ switch (from) {
2255
+ case "start":
2256
+ return i;
2257
+ case "end":
2258
+ return n - 1 - i;
2259
+ case "center":
2260
+ return Math.abs(i - mid);
2261
+ case "edges":
2262
+ return mid - Math.abs(i - mid);
2263
+ default:
2264
+ return Math.abs(i - from);
2265
+ }
2266
+ });
2267
+ }
2268
+ const [cols, rows] = grid;
2269
+ const at = (i) => ({ x: i % cols, y: Math.floor(i / cols) });
2270
+ 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);
2271
+ const d = Array.from({ length: n }, (_, i) => Math.hypot(at(i).x - origin.x, at(i).y - origin.y));
2272
+ if (from !== "edges") return d;
2273
+ const far = Math.max(...d);
2274
+ return d.map((v) => far - v);
2275
+ }
2276
+
2277
+ // src/presets/wiggle.ts
2278
+ var WIGGLE_KEYS = ["at", "duration", "freq", "seed", "ease", "props"];
2279
+ var MAX_STEPS2 = 2e3;
2280
+ function wiggle(opts) {
2281
+ warnUnknownOptions("wiggle()", opts, WIGGLE_KEYS);
2282
+ const { at = 0, duration, freq = 3, seed = 0, ease = "sine.inOut", props } = opts;
2283
+ if (!(Number.isFinite(duration) && duration > 0)) throw new Error(`wiggle(): duration must be a positive number of seconds, got ${duration}`);
2284
+ if (!(Number.isFinite(freq) && freq > 0)) throw new Error(`wiggle(): freq must be a positive number (steps per second), got ${freq}`);
2285
+ const names = Object.keys(props ?? {});
2286
+ if (names.length === 0) throw new Error("wiggle(): props is empty; name at least one property, e.g. props: { x: { around: 100, amp: 8 } }");
2287
+ for (const n of names) {
2288
+ const p = props[n];
2289
+ if (!Number.isFinite(p.amp)) throw new Error(`wiggle(): props.${n}.amp must be a number, got ${p.amp}`);
2290
+ if (p.around === void 0 || p.around === null || p.around === "") throw new Error(`wiggle(): props.${n}.around is missing`);
2291
+ }
2292
+ const steps = Math.ceil(duration * freq - 1e-9);
2293
+ if (steps > MAX_STEPS2) throw new Error(`wiggle(): ${steps} steps is too many (max ${MAX_STEPS2}); lower freq or duration`);
2294
+ const set = {};
2295
+ for (const n of names) set[n] = props[n].around;
2296
+ const out = [{ at, set }];
2297
+ const stepLen = duration / steps;
2298
+ for (let s = 0; s < steps; s++) {
2299
+ const to = {};
2300
+ names.forEach((n, pi) => {
2301
+ const p = props[n];
2302
+ if (s === steps - 1) {
2303
+ to[n] = p.around;
2304
+ return;
2305
+ }
2306
+ const off = (rand(seed * 7919 + pi * 104729 + s * 31 + 1) * 2 - 1) * p.amp;
2307
+ to[n] = typeof p.around === "number" ? p.around + off : `(${p.around}) ${off < 0 ? "-" : "+"} ${Math.abs(off)}`;
2308
+ });
2309
+ out.push({ at: at + s * stepLen, to, duration: stepLen, ease });
2310
+ }
2311
+ return out;
2312
+ }
2313
+
2314
+ // src/presets/animateText.ts
2315
+ var KEYS2 = ["by", "x", "y", "align", "name", "at", "duration", "in", "out", "stagger", "idle", "styleFor"];
2316
+ var PRESETS = {
2317
+ rise: { from: { y: 40, alpha: 0 }, duration: 0.5, ease: "back.out(2)" },
2318
+ drop: { from: { y: -50, alpha: 0 }, duration: 0.7, ease: "bounce.out" },
2319
+ fade: { from: { alpha: 0 }, duration: 0.5, ease: "power1.out" },
2320
+ pop: { from: { scale: 0, alpha: 0 }, duration: 0.45, ease: "back.out(2.4)" },
2321
+ zoom: { from: { scale: 2.5, alpha: 0 }, duration: 0.6, ease: "power3.out" },
2322
+ slide: { from: { x: -60, alpha: 0 }, duration: 0.5, ease: "power3.out" },
2323
+ spin: { from: { rotation: -90, scale: 0, alpha: 0 }, duration: 0.6, ease: "back.out(1.7)" }
2324
+ };
2325
+ var PRESET_NAMES = Object.keys(PRESETS);
2326
+ function animateText(text, style, options) {
2327
+ warnUnknownOptions("animateText()", options, KEYS2);
2328
+ const { by = "chars", x = 0, y = 0, align = "left", name = "text", at = 0, duration, idle, styleFor } = options;
2329
+ 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}`);
2330
+ const pieces = splitText(text, style, { by, x, y, align });
2331
+ if (pieces.length === 0) return [];
2332
+ const lineHeight = measureText("M", style).height;
2333
+ const inTween = options.in === false ? null : resolveTween(options.in ?? "rise", "in");
2334
+ const outTween = options.out === void 0 || options.out === false ? null : resolveTween(options.out, "out");
2335
+ const delays = stagger(pieces.length, options.stagger ?? { each: 0.04 });
2336
+ const wave = Math.max(...delays);
2337
+ const need = (inTween?.duration ?? 0) + (outTween?.duration ?? 0) + wave;
2338
+ if (duration < need - 1e-9) {
2339
+ 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`);
2340
+ }
2341
+ const end = at + duration;
2342
+ return pieces.map((p, i) => {
2343
+ const cx = p.x + p.width / 2, cy = p.y + lineHeight / 2;
2344
+ const rest = { x: cx, y: cy, alpha: 1, scale: 1, scaleX: 1, scaleY: 1, rotation: 0 };
2345
+ const start = at + delays[i];
2346
+ const len = end - start;
2347
+ const keyframes = [];
2348
+ const place = (key, v) => key === "x" || key === "y" ? rest[key] + v : v;
2349
+ if (inTween) {
2350
+ keyframes.push({
2351
+ at: 0,
2352
+ duration: inTween.duration,
2353
+ ease: inTween.ease,
2354
+ from: Object.fromEntries(Object.entries(inTween.from).map(([k, v]) => [k, place(k, v)])),
2355
+ to: Object.fromEntries(Object.keys(inTween.from).map((k) => [k, inTween.to[k] ?? rest[k]]))
2356
+ });
2357
+ }
2358
+ let idleEnd = len;
2359
+ if (outTween) {
2360
+ const begin = outTween.duration + (wave - delays[i]);
2361
+ idleEnd = len - begin;
2362
+ keyframes.push({
2363
+ at: -begin,
2364
+ duration: outTween.duration,
2365
+ ease: outTween.ease,
2366
+ from: Object.fromEntries(Object.keys(outTween.from).map((k) => [k, outTween.to[k] ?? rest[k]])),
2367
+ to: Object.fromEntries(Object.entries(outTween.from).map(([k, v]) => [k, place(k, v)]))
2368
+ });
2369
+ }
2370
+ if (idle) {
2371
+ const props = {};
2372
+ for (const k of ["x", "y", "rotation", "scale"]) if (idle[k] !== void 0) props[k] = { around: rest[k], amp: idle[k] };
2373
+ const freq = idle.freq ?? 1;
2374
+ const from = inTween?.duration ?? 0;
2375
+ if (Object.keys(props).length > 0 && idleEnd - from >= 1 / freq) {
2376
+ keyframes.push(...wiggle({ at: from, duration: idleEnd - from, freq, seed: (idle.seed ?? 0) * 1e3 + i, props }));
2377
+ }
2378
+ }
2379
+ return {
2380
+ type: "text",
2381
+ name: `${name}-${i}`,
2382
+ text: p.text,
2383
+ style: styleFor ? { ...style, ...styleFor(p) } : style,
2384
+ at: start,
2385
+ duration: len,
2386
+ initial: { x: cx, y: cy, anchorX: 0.5, anchorY: 0.5 },
2387
+ keyframes
2388
+ };
2389
+ });
2390
+ }
2391
+ function resolveTween(spec, which) {
2392
+ const t = typeof spec === "string" ? { preset: spec } : spec;
2393
+ if (t.preset !== void 0 && !(t.preset in PRESETS)) {
2394
+ const guess = suggestName(String(t.preset), PRESET_NAMES);
2395
+ throw new Error(`animateText(): ${which} "${t.preset}" is not a preset${guess ? `; did you mean "${guess}"?` : ""} (presets: ${PRESET_NAMES.map((n) => `"${n}"`).join(", ")})`);
2396
+ }
2397
+ const base = t.preset ? PRESETS[t.preset] : void 0;
2398
+ const from = { ...base?.from ?? {}, ...t.from ?? {} };
2399
+ const to = t.to ?? {};
2400
+ const known = /* @__PURE__ */ new Set(["x", "y", "alpha", "scale", "scaleX", "scaleY", "rotation"]);
2401
+ for (const k of Object.keys(from)) {
2402
+ 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 }`);
2403
+ }
2404
+ if (Object.keys(from).length === 0) throw new Error(`animateText(): ${which} needs a preset or a from`);
2405
+ let ease = t.ease ?? base?.ease ?? "power2.out";
2406
+ if (which === "out" && t.ease === void 0) ease = ease.replace(".out", ".in");
2407
+ return { from, to: { ...to }, duration: t.duration ?? base?.duration ?? 0.5, ease };
2408
+ }
2409
+
2139
2410
  // src/transforms/withFade.ts
2140
2411
  function withFade(spec, opts) {
2141
2412
  warnUnknownOptions("withFade()", opts, ["in", "out"]);
@@ -2156,6 +2427,6 @@ function withFade(spec, opts) {
2156
2427
  return spec;
2157
2428
  }
2158
2429
 
2159
- export { Movie, kenBurns, measureText, orbit, splitText, withFade };
2430
+ export { Movie, animateText, followPath, kenBurns, measureText, orbit, splitText, stagger, wiggle, withFade };
2160
2431
  //# sourceMappingURL=index.js.map
2161
2432
  //# sourceMappingURL=index.js.map