pixi-effects 0.18.1 → 0.20.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 +49 -0
- package/README.md +6 -6
- package/ai/CHAT.md +1 -1
- package/ai/SKILL.md +2 -1
- package/ai/chat-template.html +4 -4
- package/ai/reference/cheatsheet.md +10 -9
- package/ai/reference/pitfalls.md +9 -0
- package/ai/reference/recipes.md +117 -0
- package/ai/template.html +5 -5
- package/ai/tools/check.mjs +29 -6
- package/dist/{Base-BQUodYZK.d.ts → Base-CzfLGoDr.d.ts} +9 -1
- package/dist/{Base-OHqp8qvX.d.cts → Base-wYJQ2g5Q.d.cts} +9 -1
- package/dist/Composition-6PA4C4TV.js +5 -0
- package/dist/{Composition-MLBHYYFF.js.map → Composition-6PA4C4TV.js.map} +1 -1
- package/dist/Composition-LL2XXU5N.cjs +14 -0
- package/dist/{Composition-6JMXWNVV.cjs.map → Composition-LL2XXU5N.cjs.map} +1 -1
- package/dist/Controller.cjs +2 -2
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/Controller.js +1 -1
- package/dist/{Movie-DlHQ9C02.d.ts → Movie-CBxGH5ms.d.ts} +13 -2
- package/dist/{Movie-K2YuVAdg.d.cts → Movie-CzzJw8A-.d.cts} +13 -2
- package/dist/Presenter.cjs +2 -2
- package/dist/Presenter.d.cts +2 -2
- package/dist/Presenter.d.ts +2 -2
- package/dist/Presenter.js +1 -1
- package/dist/{chunk-SG3Y66FN.js → chunk-DQHXVNOI.js} +1029 -64
- package/dist/chunk-DQHXVNOI.js.map +1 -0
- package/dist/{chunk-JW67MDHQ.cjs → chunk-FFYA3UBU.cjs} +144 -36
- package/dist/chunk-FFYA3UBU.cjs.map +1 -0
- package/dist/{chunk-DKGG7WD4.js → chunk-MNM56B3X.js} +6 -3
- package/dist/chunk-MNM56B3X.js.map +1 -0
- package/dist/{chunk-ANO5R2E6.cjs → chunk-NO26HWR7.cjs} +1036 -65
- package/dist/chunk-NO26HWR7.cjs.map +1 -0
- package/dist/{chunk-UXDR3ZDU.js → chunk-TSPMKV2F.js} +121 -13
- package/dist/chunk-TSPMKV2F.js.map +1 -0
- package/dist/{chunk-UQBGW5DO.cjs → chunk-WTRKDIKH.cjs} +6 -3
- package/dist/chunk-WTRKDIKH.cjs.map +1 -0
- package/dist/index.cjs +246 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +187 -37
- package/dist/index.js.map +1 -1
- package/dist/loader.css +1 -1
- package/dist/{music-4HDFULNR.js → music-CFEC7HKL.js} +3 -3
- package/dist/{music-4HDFULNR.js.map → music-CFEC7HKL.js.map} +1 -1
- package/dist/{music-3OF5C6X5.cjs → music-UAN7DXK4.cjs} +7 -7
- package/dist/{music-3OF5C6X5.cjs.map → music-UAN7DXK4.cjs.map} +1 -1
- package/dist/{sfx-MZJZXYY3.cjs → sfx-EX4PUBMV.cjs} +8 -8
- package/dist/{sfx-MZJZXYY3.cjs.map → sfx-EX4PUBMV.cjs.map} +1 -1
- package/dist/{sfx-S37DDC66.js → sfx-U3GM2MED.js} +3 -3
- package/dist/{sfx-S37DDC66.js.map → sfx-U3GM2MED.js.map} +1 -1
- package/dist/three.cjs +6 -6
- package/dist/three.d.cts +2 -2
- package/dist/three.d.ts +2 -2
- package/dist/three.js +2 -2
- package/dist/{types-SS51lP6O.d.ts → types-YmUaViL1.d.cts} +40 -6
- package/dist/{types-SS51lP6O.d.cts → types-YmUaViL1.d.ts} +40 -6
- package/docs/dsl.md +58 -5
- package/llms-full.txt +196 -15
- package/package.json +3 -1
- package/dist/Composition-6JMXWNVV.cjs +0 -14
- package/dist/Composition-MLBHYYFF.js +0 -5
- package/dist/chunk-ANO5R2E6.cjs.map +0 -1
- package/dist/chunk-DKGG7WD4.js.map +0 -1
- package/dist/chunk-JW67MDHQ.cjs.map +0 -1
- package/dist/chunk-SG3Y66FN.js.map +0 -1
- package/dist/chunk-UQBGW5DO.cjs.map +0 -1
- package/dist/chunk-UXDR3ZDU.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { suggestName } from './chunk-
|
|
1
|
+
import { suggestName, warnUnknownOptions } from './chunk-MNM56B3X.js';
|
|
2
2
|
import * as PIXI from 'pixi.js';
|
|
3
|
-
import { Rectangle, Container, GraphicsPath, Graphics, Assets, Texture, Sprite, Text, Filter, GlProgram, defaultFilterVert, GpuProgram, UniformGroup,
|
|
3
|
+
import { Rectangle, Container, GraphicsPath, Graphics, Assets, Texture, Sprite, FillGradient, Text, Filter, GlProgram, defaultFilterVert, GpuProgram, UniformGroup, Color } from 'pixi.js';
|
|
4
4
|
import { gsap } from 'gsap';
|
|
5
5
|
|
|
6
6
|
// src/expr/random.ts
|
|
@@ -37,6 +37,113 @@ function noise(x, seed = 0) {
|
|
|
37
37
|
const u = f * f * f * (f * (f * 6 - 15) + 10);
|
|
38
38
|
return a + (b - a) * u;
|
|
39
39
|
}
|
|
40
|
+
var SETTLE_TOL = 5e-3;
|
|
41
|
+
var DEFAULTS = { mass: 1, stiffness: 100, damping: 10 };
|
|
42
|
+
function springResponse(t, { mass: m, stiffness: k, damping: c }) {
|
|
43
|
+
if (t <= 0) return 0;
|
|
44
|
+
const w0 = Math.sqrt(k / m);
|
|
45
|
+
const zeta = c / (2 * Math.sqrt(k * m));
|
|
46
|
+
if (Math.abs(zeta - 1) < 1e-6) return 1 - Math.exp(-w0 * t) * (1 + w0 * t);
|
|
47
|
+
if (zeta < 1) {
|
|
48
|
+
const wd = w0 * Math.sqrt(1 - zeta * zeta);
|
|
49
|
+
return 1 - Math.exp(-zeta * w0 * t) * (Math.cos(wd * t) + zeta * w0 / wd * Math.sin(wd * t));
|
|
50
|
+
}
|
|
51
|
+
const s = w0 * Math.sqrt(zeta * zeta - 1);
|
|
52
|
+
const r1 = -zeta * w0 + s, r2 = -zeta * w0 - s;
|
|
53
|
+
return 1 - (r2 * Math.exp(r1 * t) - r1 * Math.exp(r2 * t)) / (r2 - r1);
|
|
54
|
+
}
|
|
55
|
+
function complete(p) {
|
|
56
|
+
return { mass: p.mass ?? DEFAULTS.mass, stiffness: p.stiffness ?? DEFAULTS.stiffness, damping: p.damping ?? DEFAULTS.damping };
|
|
57
|
+
}
|
|
58
|
+
function springDuration(params = {}, tol = SETTLE_TOL) {
|
|
59
|
+
const p = complete(params);
|
|
60
|
+
const w0 = Math.sqrt(p.stiffness / p.mass);
|
|
61
|
+
const zeta = p.damping / (2 * Math.sqrt(p.stiffness * p.mass));
|
|
62
|
+
const bad = (t) => Math.abs(1 - springResponse(t, p)) > tol;
|
|
63
|
+
let lo, hi;
|
|
64
|
+
if (zeta < 1 - 1e-6) {
|
|
65
|
+
const wd = w0 * Math.sqrt(1 - zeta * zeta);
|
|
66
|
+
const decay = Math.PI * zeta / Math.sqrt(1 - zeta * zeta);
|
|
67
|
+
const n = Math.max(1, Math.ceil(Math.log(1 / tol) / decay));
|
|
68
|
+
lo = (n - 1) * Math.PI / wd;
|
|
69
|
+
hi = n * Math.PI / wd;
|
|
70
|
+
} else {
|
|
71
|
+
lo = 0;
|
|
72
|
+
hi = 1 / w0;
|
|
73
|
+
while (bad(hi)) {
|
|
74
|
+
lo = hi;
|
|
75
|
+
hi *= 2;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (let i = 0; i < 60; i++) {
|
|
79
|
+
const mid = (lo + hi) / 2;
|
|
80
|
+
if (bad(mid)) lo = mid;
|
|
81
|
+
else hi = mid;
|
|
82
|
+
}
|
|
83
|
+
return hi;
|
|
84
|
+
}
|
|
85
|
+
function springEase(params = {}) {
|
|
86
|
+
const sp = complete(params);
|
|
87
|
+
const T = springDuration(sp);
|
|
88
|
+
const end = springResponse(T, sp);
|
|
89
|
+
return (p) => p <= 0 ? 0 : p >= 1 ? 1 : springResponse(p * T, sp) / end;
|
|
90
|
+
}
|
|
91
|
+
var SPRING_PRESETS = {
|
|
92
|
+
gentle: { mass: 1, stiffness: 100, damping: 15 },
|
|
93
|
+
// zeta .75, one soft overshoot
|
|
94
|
+
snappy: { mass: 1, stiffness: 300, damping: 28 },
|
|
95
|
+
// zeta .81, quick, barely rings
|
|
96
|
+
bouncy: { mass: 1, stiffness: 170, damping: 10 },
|
|
97
|
+
// zeta .38
|
|
98
|
+
wobbly: { mass: 1, stiffness: 120, damping: 6 },
|
|
99
|
+
// zeta .27, rings several times
|
|
100
|
+
slow: { mass: 1, stiffness: 60, damping: 18 }
|
|
101
|
+
// zeta 1.16, no overshoot
|
|
102
|
+
};
|
|
103
|
+
var SPRING_RE = /^spring(?:\.(\w+)|\(([^)]*)\))?$/;
|
|
104
|
+
function springProblem(name) {
|
|
105
|
+
const m = SPRING_RE.exec(name.trim());
|
|
106
|
+
if (!m) return 'expected "spring(mass, stiffness, damping)" or "spring.<preset>"';
|
|
107
|
+
if (m[1] && !SPRING_PRESETS[m[1]]) return `no preset "${m[1]}" (presets: ${Object.keys(SPRING_PRESETS).join(", ")})`;
|
|
108
|
+
if (m[2] !== void 0 && m[2].trim() !== "") {
|
|
109
|
+
const nums = m[2].split(",").map((s) => Number(s.trim()));
|
|
110
|
+
if (nums.length > 3 || nums.some((n) => !Number.isFinite(n) || n <= 0)) return "mass, stiffness and damping must be 1 to 3 numbers greater than 0";
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
function parseSpring(name) {
|
|
115
|
+
if (typeof name !== "string" || !/^\s*spring/.test(name) || springProblem(name)) return null;
|
|
116
|
+
const m = SPRING_RE.exec(name.trim());
|
|
117
|
+
if (m[1]) return SPRING_PRESETS[m[1]];
|
|
118
|
+
const [mass, stiffness, damping] = (m[2] ?? "").split(",").map((s) => s.trim()).filter(Boolean).map(Number);
|
|
119
|
+
return complete({ mass, stiffness, damping });
|
|
120
|
+
}
|
|
121
|
+
var registered = false;
|
|
122
|
+
function registerSpringEases() {
|
|
123
|
+
if (registered) return;
|
|
124
|
+
registered = true;
|
|
125
|
+
const base = springEase();
|
|
126
|
+
const ease = (p) => base(p);
|
|
127
|
+
ease.config = (mass, stiffness, damping) => {
|
|
128
|
+
const bad = [mass, stiffness, damping].some((v) => v !== void 0 && !(typeof v === "number" && v > 0 && Number.isFinite(v)));
|
|
129
|
+
return springEase(bad ? {} : { mass, stiffness, damping });
|
|
130
|
+
};
|
|
131
|
+
gsap.registerEase("spring", ease);
|
|
132
|
+
for (const [name, p] of Object.entries(SPRING_PRESETS)) gsap.registerEase(`spring.${name}`, springEase(p));
|
|
133
|
+
}
|
|
134
|
+
var autoWarned = /* @__PURE__ */ new Set();
|
|
135
|
+
function kfDuration(kf) {
|
|
136
|
+
if (kf.duration !== "auto") return kf.duration ?? 0;
|
|
137
|
+
const sp = parseSpring(kf.ease);
|
|
138
|
+
if (sp) return Math.round(springDuration(sp) * 1e3) / 1e3;
|
|
139
|
+
const name = String(kf.ease ?? "none");
|
|
140
|
+
if (!/^\s*spring/.test(name) && !autoWarned.has(name)) {
|
|
141
|
+
autoWarned.add(name);
|
|
142
|
+
console.warn(`pixi-effects: keyframe duration 'auto' needs a spring ease (ease: 'spring(1, 170, 12)' or 'spring.bouncy'), got ease ${JSON.stringify(name)} \u2014 using 0.5 s.`);
|
|
143
|
+
}
|
|
144
|
+
return 0.5;
|
|
145
|
+
}
|
|
146
|
+
registerSpringEases();
|
|
40
147
|
|
|
41
148
|
// src/expr/Parser.ts
|
|
42
149
|
var FUNCS = {
|
|
@@ -289,11 +396,77 @@ function looksLikeColorString(s) {
|
|
|
289
396
|
if (s.charCodeAt(0) === 35) return true;
|
|
290
397
|
return /^(rgb|hsl|oklab|oklch|lab|lch)\b/i.test(s);
|
|
291
398
|
}
|
|
399
|
+
function cubicBezierEase(x1, y1, x2, y2) {
|
|
400
|
+
const cx = 3 * x1, bx = 3 * (x2 - x1) - cx, ax = 1 - cx - bx;
|
|
401
|
+
const cy = 3 * y1, by = 3 * (y2 - y1) - cy, ay = 1 - cy - by;
|
|
402
|
+
const X = (t) => ((ax * t + bx) * t + cx) * t;
|
|
403
|
+
const Y = (t) => ((ay * t + by) * t + cy) * t;
|
|
404
|
+
const dX = (t) => (3 * ax * t + 2 * bx) * t + cx;
|
|
405
|
+
return (p) => {
|
|
406
|
+
if (p <= 0) return 0;
|
|
407
|
+
if (p >= 1) return 1;
|
|
408
|
+
let t = p;
|
|
409
|
+
for (let i = 0; i < 8; i++) {
|
|
410
|
+
const err = X(t) - p;
|
|
411
|
+
if (Math.abs(err) < 1e-9) return Y(t);
|
|
412
|
+
const d = dX(t);
|
|
413
|
+
if (Math.abs(d) < 1e-6) break;
|
|
414
|
+
t -= err / d;
|
|
415
|
+
}
|
|
416
|
+
let lo = 0, hi = 1;
|
|
417
|
+
t = p;
|
|
418
|
+
for (let i = 0; i < 60; i++) {
|
|
419
|
+
const x = X(t);
|
|
420
|
+
if (Math.abs(x - p) < 1e-10) break;
|
|
421
|
+
if (x < p) lo = t;
|
|
422
|
+
else hi = t;
|
|
423
|
+
t = (lo + hi) / 2;
|
|
424
|
+
}
|
|
425
|
+
return Y(t);
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
var NUM = String.raw`\s*(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s*`;
|
|
429
|
+
var RE = new RegExp(`^\\s*cubic-bezier\\(${NUM},${NUM},${NUM},${NUM}\\)\\s*$`, "i");
|
|
430
|
+
function cubicBezierProblem(name) {
|
|
431
|
+
const m = RE.exec(name);
|
|
432
|
+
if (!m) return 'expected "cubic-bezier(x1, y1, x2, y2)" with four numbers, like CSS';
|
|
433
|
+
const x1 = Number(m[1]), x2 = Number(m[3]);
|
|
434
|
+
if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) return "x1 and x2 must be between 0 and 1 (y1 and y2 may overshoot)";
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
var registered2 = false;
|
|
438
|
+
function registerCubicBezierEase() {
|
|
439
|
+
if (registered2) return;
|
|
440
|
+
registered2 = true;
|
|
441
|
+
const base = ((p) => p);
|
|
442
|
+
base.config = (x1, y1, x2, y2) => {
|
|
443
|
+
const bad = [x1, y1, x2, y2].some((v) => typeof v !== "number" || !Number.isFinite(v)) || x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1;
|
|
444
|
+
return bad ? (p) => p : cubicBezierEase(x1, y1, x2, y2);
|
|
445
|
+
};
|
|
446
|
+
gsap.registerEase("cubic-bezier", base);
|
|
447
|
+
}
|
|
448
|
+
registerCubicBezierEase();
|
|
449
|
+
|
|
450
|
+
// src/core/ease.ts
|
|
292
451
|
var FAMILIES = ["power1", "power2", "power3", "power4", "back", "elastic", "bounce", "circ", "expo", "sine"];
|
|
293
452
|
var EASE_NAMES = ["none", ...FAMILIES.flatMap((f) => [`${f}.in`, `${f}.out`, `${f}.inOut`])];
|
|
294
453
|
var warned = /* @__PURE__ */ new Set();
|
|
295
454
|
function checkEase(ease, where) {
|
|
296
455
|
if (typeof ease !== "string") return;
|
|
456
|
+
if (/^\s*spring/.test(ease)) {
|
|
457
|
+
const problem = springProblem(ease);
|
|
458
|
+
if (!problem || warned.has(ease)) return;
|
|
459
|
+
warned.add(ease);
|
|
460
|
+
console.warn(`pixi-effects: ${where}: unknown ease "${ease}" \u2014 ${problem}. GSAP would run it as its default ease, power1.out.`);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (/^\s*cubic-bezier/i.test(ease)) {
|
|
464
|
+
const problem = cubicBezierProblem(ease);
|
|
465
|
+
if (!problem || warned.has(ease)) return;
|
|
466
|
+
warned.add(ease);
|
|
467
|
+
console.warn(`pixi-effects: ${where}: unknown ease "${ease}" \u2014 ${problem}. It runs as 'none' (a straight line).`);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
297
470
|
const found = gsap.parseEase(ease);
|
|
298
471
|
if (typeof found === "function") return;
|
|
299
472
|
if (warned.has(ease)) return;
|
|
@@ -334,7 +507,7 @@ function resolveAt(at, duration) {
|
|
|
334
507
|
}
|
|
335
508
|
function normalizeKeyframe(kf, parentDuration) {
|
|
336
509
|
const at = resolveAt(kf.at, parentDuration);
|
|
337
|
-
const duration = kf
|
|
510
|
+
const duration = kfDuration(kf);
|
|
338
511
|
const ease = kf.ease ?? "none";
|
|
339
512
|
checkEase(ease, "a keyframe");
|
|
340
513
|
const hasSet = !!kf.set;
|
|
@@ -764,8 +937,217 @@ var ChromaKeyFilter = class extends Filter {
|
|
|
764
937
|
u[2] = rgb[2];
|
|
765
938
|
}
|
|
766
939
|
};
|
|
940
|
+
var HASH_GL = `
|
|
941
|
+
uint hash(uint x) { x ^= x >> 16; x *= 0x7feb352du; x ^= x >> 15; x *= 0x846ca68bu; x ^= x >> 16; return x; }
|
|
942
|
+
// one gaussian-ish value (sum of the four bytes of a hash), mean 0, std 1
|
|
943
|
+
float gauss(ivec2 p, uint k) {
|
|
944
|
+
uint h = hash(uint(p.x) + hash(uint(p.y) + hash(k)));
|
|
945
|
+
float s = float(h & 255u) + float((h >> 8) & 255u) + float((h >> 16) & 255u) + float(h >> 24);
|
|
946
|
+
return (s / 255.0 - 2.0) * 1.7320508;
|
|
947
|
+
}
|
|
948
|
+
// the grain value at a pixel: cells of size pixels, smoothly blended
|
|
949
|
+
float cell(vec2 pos, float size, uint k) {
|
|
950
|
+
vec2 q = pos / size - 0.5;
|
|
951
|
+
vec2 i = floor(q);
|
|
952
|
+
vec2 f = fract(q);
|
|
953
|
+
ivec2 p = ivec2(i);
|
|
954
|
+
float a = gauss(p, k), b = gauss(p + ivec2(1, 0), k), c = gauss(p + ivec2(0, 1), k), d = gauss(p + ivec2(1, 1), k);
|
|
955
|
+
float v = mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
956
|
+
vec2 wx = vec2(1.0 - f.x, f.x), wy = vec2(1.0 - f.y, f.y);
|
|
957
|
+
return v * inversesqrt(dot(wx, wx) * dot(wy, wy)); // blending lowers the spread: keep std exactly 1 at every pixel
|
|
958
|
+
}`;
|
|
959
|
+
var GL_VERTEX = `#version 300 es
|
|
960
|
+
in vec2 aPosition;
|
|
961
|
+
out vec2 vTextureCoord;
|
|
962
|
+
uniform vec4 uInputSize;
|
|
963
|
+
uniform vec4 uOutputFrame;
|
|
964
|
+
uniform vec4 uOutputTexture;
|
|
965
|
+
void main(void) {
|
|
966
|
+
vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
|
|
967
|
+
position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
|
|
968
|
+
position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
|
|
969
|
+
gl_Position = vec4(position, 0.0, 1.0);
|
|
970
|
+
vTextureCoord = aPosition * (uOutputFrame.zw * uInputSize.zw);
|
|
971
|
+
}`;
|
|
972
|
+
var GL_FRAGMENT2 = `#version 300 es
|
|
973
|
+
precision highp float;
|
|
974
|
+
precision highp int;
|
|
975
|
+
in vec2 vTextureCoord;
|
|
976
|
+
uniform sampler2D uTexture;
|
|
977
|
+
uniform vec4 uInputSize;
|
|
978
|
+
uniform float uAmount;
|
|
979
|
+
uniform float uSize;
|
|
980
|
+
uniform float uSeed;
|
|
981
|
+
uniform float uFrame;
|
|
982
|
+
uniform float uColor;
|
|
983
|
+
out vec4 finalColor;
|
|
984
|
+
${HASH_GL}
|
|
985
|
+
void main(void) {
|
|
986
|
+
vec4 raw = texture(uTexture, vTextureCoord);
|
|
987
|
+
vec3 rgb = raw.a > 0.0 ? raw.rgb / raw.a : vec3(0.0);
|
|
988
|
+
float L = dot(rgb, vec3(0.2126, 0.7152, 0.0722));
|
|
989
|
+
float w = clamp(4.0 * L * (1.0 - L), 0.0, 1.0);
|
|
990
|
+
vec2 pos = vTextureCoord * uInputSize.xy;
|
|
991
|
+
uint k = hash(uint(uSeed) * 0x9E3779B1u + uint(uFrame));
|
|
992
|
+
float n0 = cell(pos, uSize, k);
|
|
993
|
+
vec3 n = vec3(n0);
|
|
994
|
+
if (uColor > 0.0) {
|
|
995
|
+
vec3 nc = vec3(cell(pos, uSize, k + 1u), cell(pos, uSize, k + 2u), cell(pos, uSize, k + 3u));
|
|
996
|
+
n = mix(n, nc, uColor);
|
|
997
|
+
}
|
|
998
|
+
rgb = clamp(rgb + n * (uAmount * w), 0.0, 1.0);
|
|
999
|
+
finalColor = vec4(rgb * raw.a, raw.a);
|
|
1000
|
+
}`;
|
|
1001
|
+
var WGSL_SOURCE2 = `
|
|
1002
|
+
struct GlobalFilterUniforms {
|
|
1003
|
+
uInputSize: vec4<f32>,
|
|
1004
|
+
uInputPixel: vec4<f32>,
|
|
1005
|
+
uInputClamp: vec4<f32>,
|
|
1006
|
+
uOutputFrame: vec4<f32>,
|
|
1007
|
+
uGlobalFrame: vec4<f32>,
|
|
1008
|
+
uOutputTexture: vec4<f32>,
|
|
1009
|
+
};
|
|
1010
|
+
struct GrainUniforms {
|
|
1011
|
+
uAmount: f32,
|
|
1012
|
+
uSize: f32,
|
|
1013
|
+
uSeed: f32,
|
|
1014
|
+
uFrame: f32,
|
|
1015
|
+
uColor: f32,
|
|
1016
|
+
};
|
|
1017
|
+
@group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
|
|
1018
|
+
@group(0) @binding(1) var uTexture: texture_2d<f32>;
|
|
1019
|
+
@group(0) @binding(2) var uSampler : sampler;
|
|
1020
|
+
@group(1) @binding(0) var<uniform> grainUniforms : GrainUniforms;
|
|
1021
|
+
|
|
1022
|
+
struct VSOutput {
|
|
1023
|
+
@builtin(position) position: vec4<f32>,
|
|
1024
|
+
@location(0) uv : vec2<f32>,
|
|
1025
|
+
};
|
|
1026
|
+
fn filterVertexPosition(aPosition : vec2<f32>) -> vec4<f32> {
|
|
1027
|
+
var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
|
|
1028
|
+
position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
|
|
1029
|
+
position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
|
|
1030
|
+
return vec4<f32>(position, 0.0, 1.0);
|
|
1031
|
+
}
|
|
1032
|
+
fn filterTextureCoord(aPosition : vec2<f32>) -> vec2<f32> {
|
|
1033
|
+
return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);
|
|
1034
|
+
}
|
|
1035
|
+
@vertex
|
|
1036
|
+
fn mainVertex(
|
|
1037
|
+
@location(0) aPosition : vec2<f32>,
|
|
1038
|
+
) -> VSOutput {
|
|
1039
|
+
return VSOutput(
|
|
1040
|
+
filterVertexPosition(aPosition),
|
|
1041
|
+
filterTextureCoord(aPosition),
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
fn hash(xIn: u32) -> u32 {
|
|
1046
|
+
var x = xIn;
|
|
1047
|
+
x = x ^ (x >> 16u); x = x * 0x7feb352du; x = x ^ (x >> 15u); x = x * 0x846ca68bu; x = x ^ (x >> 16u);
|
|
1048
|
+
return x;
|
|
1049
|
+
}
|
|
1050
|
+
fn gauss(p: vec2<i32>, k: u32) -> f32 {
|
|
1051
|
+
let h = hash(u32(p.x) + hash(u32(p.y) + hash(k)));
|
|
1052
|
+
let s = f32(h & 255u) + f32((h >> 8u) & 255u) + f32((h >> 16u) & 255u) + f32(h >> 24u);
|
|
1053
|
+
return (s / 255.0 - 2.0) * 1.7320508;
|
|
1054
|
+
}
|
|
1055
|
+
fn cell(pos: vec2<f32>, size: f32, k: u32) -> f32 {
|
|
1056
|
+
let q = pos / size - vec2<f32>(0.5);
|
|
1057
|
+
let i = floor(q);
|
|
1058
|
+
let f = fract(q);
|
|
1059
|
+
let p = vec2<i32>(i);
|
|
1060
|
+
let a = gauss(p, k);
|
|
1061
|
+
let b = gauss(p + vec2<i32>(1, 0), k);
|
|
1062
|
+
let c = gauss(p + vec2<i32>(0, 1), k);
|
|
1063
|
+
let d = gauss(p + vec2<i32>(1, 1), k);
|
|
1064
|
+
let v = mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
1065
|
+
let wx = vec2<f32>(1.0 - f.x, f.x);
|
|
1066
|
+
let wy = vec2<f32>(1.0 - f.y, f.y);
|
|
1067
|
+
return v * inverseSqrt(dot(wx, wx) * dot(wy, wy));
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
@fragment
|
|
1071
|
+
fn mainFragment(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {
|
|
1072
|
+
let raw = textureSample(uTexture, uSampler, uv);
|
|
1073
|
+
var rgb = vec3<f32>(0.0);
|
|
1074
|
+
if (raw.a > 0.0) { rgb = raw.rgb / raw.a; }
|
|
1075
|
+
let L = dot(rgb, vec3<f32>(0.2126, 0.7152, 0.0722));
|
|
1076
|
+
let w = clamp(4.0 * L * (1.0 - L), 0.0, 1.0);
|
|
1077
|
+
let pos = uv * gfu.uInputSize.xy;
|
|
1078
|
+
let k = hash(u32(grainUniforms.uSeed) * 0x9E3779B1u + u32(grainUniforms.uFrame));
|
|
1079
|
+
let n0 = cell(pos, grainUniforms.uSize, k);
|
|
1080
|
+
var n = vec3<f32>(n0);
|
|
1081
|
+
if (grainUniforms.uColor > 0.0) {
|
|
1082
|
+
let nc = vec3<f32>(cell(pos, grainUniforms.uSize, k + 1u), cell(pos, grainUniforms.uSize, k + 2u), cell(pos, grainUniforms.uSize, k + 3u));
|
|
1083
|
+
n = mix(n, nc, grainUniforms.uColor);
|
|
1084
|
+
}
|
|
1085
|
+
rgb = clamp(rgb + n * (grainUniforms.uAmount * w), vec3<f32>(0.0), vec3<f32>(1.0));
|
|
1086
|
+
return vec4<f32>(rgb * raw.a, raw.a);
|
|
1087
|
+
}`;
|
|
1088
|
+
var GRAIN_KEYS = ["amount", "size", "seed", "fps", "color"];
|
|
1089
|
+
var GrainFilter = class extends Filter {
|
|
1090
|
+
_fps;
|
|
1091
|
+
constructor(options = {}) {
|
|
1092
|
+
warnUnknownOptions("grain filter", options, GRAIN_KEYS);
|
|
1093
|
+
const { amount = 0.08, size = 1.5, seed = 0, fps = 24, color = 0 } = options;
|
|
1094
|
+
super({
|
|
1095
|
+
glProgram: GlProgram.from({ vertex: GL_VERTEX, fragment: GL_FRAGMENT2, name: "grain-filter" }),
|
|
1096
|
+
gpuProgram: GpuProgram.from({ vertex: { source: WGSL_SOURCE2, entryPoint: "mainVertex" }, fragment: { source: WGSL_SOURCE2, entryPoint: "mainFragment" } }),
|
|
1097
|
+
resources: {
|
|
1098
|
+
grainUniforms: new UniformGroup({
|
|
1099
|
+
uAmount: { value: amount, type: "f32" },
|
|
1100
|
+
uSize: { value: Math.max(size, 0.25), type: "f32" },
|
|
1101
|
+
uSeed: { value: Math.floor(seed), type: "f32" },
|
|
1102
|
+
uFrame: { value: 0, type: "f32" },
|
|
1103
|
+
uColor: { value: color, type: "f32" }
|
|
1104
|
+
})
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
this._fps = fps;
|
|
1108
|
+
}
|
|
1109
|
+
get u() {
|
|
1110
|
+
return this.resources.grainUniforms.uniforms;
|
|
1111
|
+
}
|
|
1112
|
+
get amount() {
|
|
1113
|
+
return this.u.uAmount;
|
|
1114
|
+
}
|
|
1115
|
+
set amount(v) {
|
|
1116
|
+
this.u.uAmount = v;
|
|
1117
|
+
}
|
|
1118
|
+
get size() {
|
|
1119
|
+
return this.u.uSize;
|
|
1120
|
+
}
|
|
1121
|
+
set size(v) {
|
|
1122
|
+
this.u.uSize = Math.max(v, 0.25);
|
|
1123
|
+
}
|
|
1124
|
+
get seed() {
|
|
1125
|
+
return this.u.uSeed;
|
|
1126
|
+
}
|
|
1127
|
+
set seed(v) {
|
|
1128
|
+
this.u.uSeed = Math.floor(v);
|
|
1129
|
+
}
|
|
1130
|
+
get color() {
|
|
1131
|
+
return this.u.uColor;
|
|
1132
|
+
}
|
|
1133
|
+
set color(v) {
|
|
1134
|
+
this.u.uColor = v;
|
|
1135
|
+
}
|
|
1136
|
+
get fps() {
|
|
1137
|
+
return this._fps;
|
|
1138
|
+
}
|
|
1139
|
+
set fps(v) {
|
|
1140
|
+
this._fps = v;
|
|
1141
|
+
}
|
|
1142
|
+
/** The movie calls this with the time (s) of the frame it is about to draw: the grain is new every 1/fps s. */
|
|
1143
|
+
setTime(t) {
|
|
1144
|
+
this.u.uFrame = this._fps > 0 ? Math.floor(t * this._fps + 1e-3) : 0;
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
// src/filters/named.ts
|
|
767
1149
|
var CORE = { blur: "BlurFilter", noise: "NoiseFilter", alpha: "AlphaFilter" };
|
|
768
|
-
var CORE_TYPES = [...Object.keys(CORE), "colorMatrix"];
|
|
1150
|
+
var CORE_TYPES = [...Object.keys(CORE), "colorMatrix", "grain"];
|
|
769
1151
|
var LIBRARY_TYPES = [
|
|
770
1152
|
"adjustment",
|
|
771
1153
|
"advancedBloom",
|
|
@@ -882,6 +1264,7 @@ function colorGradientParams(params) {
|
|
|
882
1264
|
}
|
|
883
1265
|
function createNamedFilter(type, params) {
|
|
884
1266
|
if (lower(type) === "colormatrix") return colorMatrix(params);
|
|
1267
|
+
if (lower(type) === "grain") return new GrainFilter(params);
|
|
885
1268
|
const core = Object.entries(CORE).find(([k]) => lower(k) === lower(type));
|
|
886
1269
|
if (core) return new PIXI[core[1]](params);
|
|
887
1270
|
if (!isLibraryType(type)) throw unknownType(type);
|
|
@@ -973,13 +1356,21 @@ var Sequence = class {
|
|
|
973
1356
|
pathRouters() {
|
|
974
1357
|
return {};
|
|
975
1358
|
}
|
|
1359
|
+
/**
|
|
1360
|
+
* The `initial` and `keyframes` that go onto the display object. A layer whose `time` runs a clock (video, composition) takes `time`
|
|
1361
|
+
* out of them: it is not a property of the display object.
|
|
1362
|
+
*/
|
|
1363
|
+
displayProps() {
|
|
1364
|
+
return { initial: this.spec.initial, keyframes: this.spec.keyframes };
|
|
1365
|
+
}
|
|
976
1366
|
bindTimeline(timeline, offset = 0) {
|
|
977
1367
|
if (!this.target) return;
|
|
978
1368
|
const scope = this.scope();
|
|
979
1369
|
const routers = this.pathRouters();
|
|
980
1370
|
const startTime = offset + this.at;
|
|
981
|
-
|
|
982
|
-
|
|
1371
|
+
const { initial, keyframes } = this.displayProps();
|
|
1372
|
+
applyInitial(this.target, initial, scope, [], routers);
|
|
1373
|
+
applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], startTime, routers);
|
|
983
1374
|
const endTime = startTime + this.duration;
|
|
984
1375
|
this.absoluteStart = startTime;
|
|
985
1376
|
if (this.keepHidden) return;
|
|
@@ -1030,6 +1421,141 @@ function collectPropKeys(spec) {
|
|
|
1030
1421
|
return out;
|
|
1031
1422
|
}
|
|
1032
1423
|
|
|
1424
|
+
// src/core/layerKeys.ts
|
|
1425
|
+
var COMMON = ["type", "name", "parent", "at", "duration", "initial", "keyframes", "filters", "filterArea", "mask", "maskInverted", "blendMode", "threeD", "hideBehindCamera"];
|
|
1426
|
+
var VIDEO = [...COMMON, "asset", "loop", "audio", "volume", "speed"];
|
|
1427
|
+
var IMAGE = [...COMMON, "asset", "colorSpace"];
|
|
1428
|
+
var TEXT = [...COMMON, "text", "format", "style", "colorSpace", "fillGradient"];
|
|
1429
|
+
var AUDIO = [...COMMON, "volume", "asset", "loop", "sfx", "music", "speed"];
|
|
1430
|
+
var COMPOSITION = [...COMMON, "width", "height", "sequences", "transitions", "speed"];
|
|
1431
|
+
var CAMERA = [...COMMON];
|
|
1432
|
+
var NULL = [...COMMON];
|
|
1433
|
+
var SHAPE = [...COMMON, "shape", "fillGradient", "colorSpace", "strokeCap", "strokeJoin"];
|
|
1434
|
+
var TRIM = ["trimStart", "trimEnd", "trimEach"];
|
|
1435
|
+
var RECT = [...SHAPE, ...TRIM, "width", "height", "cornerRadius", "anchorX", "anchorY"];
|
|
1436
|
+
var CIRCLE = [...SHAPE, ...TRIM, "radius", "anchorX", "anchorY"];
|
|
1437
|
+
var ELLIPSE = [...SHAPE, ...TRIM, "radiusX", "radiusY", "anchorX", "anchorY"];
|
|
1438
|
+
var ARC = [...SHAPE, "radius", "innerRadius", "startAngle", "endAngle"];
|
|
1439
|
+
var LINE = [...SHAPE, ...TRIM, "from", "to"];
|
|
1440
|
+
var POLYGON = [...SHAPE, ...TRIM, "points", "open"];
|
|
1441
|
+
var PATH = [...SHAPE, ...TRIM, "d", "morphTo", "morph", "morphPoints"];
|
|
1442
|
+
var SHAPES = { rect: RECT, circle: CIRCLE, ellipse: ELLIPSE, arc: ARC, line: LINE, polygon: POLYGON, path: PATH };
|
|
1443
|
+
var BY_TYPE = { video: VIDEO, image: IMAGE, text: TEXT, audio: AUDIO, composition: COMPOSITION, camera: CAMERA, null: NULL };
|
|
1444
|
+
function kindName(spec) {
|
|
1445
|
+
return spec.type === "shape" && spec.shape ? `${spec.shape} shape` : `${spec.type} layer`;
|
|
1446
|
+
}
|
|
1447
|
+
function layerKeys(spec) {
|
|
1448
|
+
if (spec.type === "shape") return spec.shape && SHAPES[spec.shape] || null;
|
|
1449
|
+
return BY_TYPE[spec.type] ?? null;
|
|
1450
|
+
}
|
|
1451
|
+
function kindsWithKey(key) {
|
|
1452
|
+
const out = [];
|
|
1453
|
+
for (const [shape, keys] of Object.entries(SHAPES)) if (keys.includes(key)) out.push(`${shape} shape`);
|
|
1454
|
+
for (const [type, keys] of Object.entries(BY_TYPE)) if (keys.includes(key)) out.push(`${type} layer`);
|
|
1455
|
+
return out;
|
|
1456
|
+
}
|
|
1457
|
+
var PROP_KEYS = /* @__PURE__ */ new Set([
|
|
1458
|
+
// transform and look (GSAP + PixiPlugin names)
|
|
1459
|
+
"x",
|
|
1460
|
+
"y",
|
|
1461
|
+
"z",
|
|
1462
|
+
"alpha",
|
|
1463
|
+
"rotation",
|
|
1464
|
+
"rotationX",
|
|
1465
|
+
"rotationY",
|
|
1466
|
+
"width",
|
|
1467
|
+
"height",
|
|
1468
|
+
"visible",
|
|
1469
|
+
"autoAlpha",
|
|
1470
|
+
"scale",
|
|
1471
|
+
"scaleX",
|
|
1472
|
+
"scaleY",
|
|
1473
|
+
"anchor",
|
|
1474
|
+
"anchorX",
|
|
1475
|
+
"anchorY",
|
|
1476
|
+
"pivot",
|
|
1477
|
+
"pivotX",
|
|
1478
|
+
"pivotY",
|
|
1479
|
+
"skew",
|
|
1480
|
+
"skewX",
|
|
1481
|
+
"skewY",
|
|
1482
|
+
"position",
|
|
1483
|
+
"positionX",
|
|
1484
|
+
"positionY",
|
|
1485
|
+
"tint",
|
|
1486
|
+
"colorize",
|
|
1487
|
+
"colorizeAmount",
|
|
1488
|
+
"blur",
|
|
1489
|
+
"blurX",
|
|
1490
|
+
"blurY",
|
|
1491
|
+
"blurPadding",
|
|
1492
|
+
// shapes
|
|
1493
|
+
"fillColor",
|
|
1494
|
+
"fillAlpha",
|
|
1495
|
+
"strokeColor",
|
|
1496
|
+
"strokeAlpha",
|
|
1497
|
+
"strokeWidth",
|
|
1498
|
+
"lineColor",
|
|
1499
|
+
"lineAlpha",
|
|
1500
|
+
"cornerRadius",
|
|
1501
|
+
"radius",
|
|
1502
|
+
"radiusX",
|
|
1503
|
+
"radiusY",
|
|
1504
|
+
"innerRadius",
|
|
1505
|
+
"startAngle",
|
|
1506
|
+
"endAngle",
|
|
1507
|
+
"trimStart",
|
|
1508
|
+
"trimEnd",
|
|
1509
|
+
"trimEach",
|
|
1510
|
+
"morph",
|
|
1511
|
+
"fillGradient",
|
|
1512
|
+
"strokeCap",
|
|
1513
|
+
"strokeJoin",
|
|
1514
|
+
// text, audio, video
|
|
1515
|
+
"fill",
|
|
1516
|
+
"value",
|
|
1517
|
+
"visibleChars",
|
|
1518
|
+
"text",
|
|
1519
|
+
"volume",
|
|
1520
|
+
"time",
|
|
1521
|
+
// camera
|
|
1522
|
+
"lookAtX",
|
|
1523
|
+
"lookAtY",
|
|
1524
|
+
"lookAtZ",
|
|
1525
|
+
"fov",
|
|
1526
|
+
"offsetX",
|
|
1527
|
+
"offsetY",
|
|
1528
|
+
"offsetZ",
|
|
1529
|
+
"lookOffsetX",
|
|
1530
|
+
"lookOffsetY",
|
|
1531
|
+
"lookOffsetZ"
|
|
1532
|
+
]);
|
|
1533
|
+
var STYLE_KEYS = [
|
|
1534
|
+
"fontSize",
|
|
1535
|
+
"fontFamily",
|
|
1536
|
+
"fontWeight",
|
|
1537
|
+
"fontStyle",
|
|
1538
|
+
"fontVariant",
|
|
1539
|
+
"fill",
|
|
1540
|
+
"stroke",
|
|
1541
|
+
"dropShadow",
|
|
1542
|
+
"letterSpacing",
|
|
1543
|
+
"lineHeight",
|
|
1544
|
+
"leading",
|
|
1545
|
+
"align",
|
|
1546
|
+
"wordWrap",
|
|
1547
|
+
"wordWrapWidth",
|
|
1548
|
+
"breakWords",
|
|
1549
|
+
"whiteSpace",
|
|
1550
|
+
"padding",
|
|
1551
|
+
"textBaseline",
|
|
1552
|
+
"trim",
|
|
1553
|
+
"lineJoin",
|
|
1554
|
+
"miterLimit",
|
|
1555
|
+
"filters",
|
|
1556
|
+
"tagStyles"
|
|
1557
|
+
];
|
|
1558
|
+
|
|
1033
1559
|
// src/core/lint.ts
|
|
1034
1560
|
var defaultWarn = (m) => console.warn(m);
|
|
1035
1561
|
function describeLayer(spec) {
|
|
@@ -1068,6 +1594,191 @@ function lintText(spec, warn = defaultWarn) {
|
|
|
1068
1594
|
warn(`pixi-effects: ${describeLayer(spec)}: \`value\` is set or animated but the text has no {value} placeholder, so nothing shows it. Use text: '{value}' (or e.g. '{value} users').`);
|
|
1069
1595
|
}
|
|
1070
1596
|
}
|
|
1597
|
+
var REMAPPABLE = /* @__PURE__ */ new Set(["video", "audio", "composition"]);
|
|
1598
|
+
var REMAP_NAMES = /* @__PURE__ */ new Set(["time", "timeRemap", "reverse"]);
|
|
1599
|
+
var TIME_BAGS = ["set", "to", "from"];
|
|
1600
|
+
function lintRemap(spec, warn) {
|
|
1601
|
+
const s = spec;
|
|
1602
|
+
const who = describeLayer(spec);
|
|
1603
|
+
const kfs = s.keyframes ?? [];
|
|
1604
|
+
const bags = [s.initial, ...kfs.flatMap((kf) => TIME_BAGS.map((b) => kf[b]))];
|
|
1605
|
+
const timeInInitial = !!s.initial && s.initial.time !== void 0;
|
|
1606
|
+
const timeInKeyframes = kfs.some((kf) => TIME_BAGS.some((b) => kf[b] && kf[b].time !== void 0));
|
|
1607
|
+
const timed = timeInInitial || timeInKeyframes;
|
|
1608
|
+
if (bags.some((bag) => bag && bag.speed !== void 0)) {
|
|
1609
|
+
warn(`pixi-effects: ${who}: speed is a fixed setting, not an animatable property: write it on the layer (speed: 2); to change the speed over time, animate time (keyframes: [{ at: 0, from: { time: 0 }, to: { time: 4 }, duration: 2, ease: 'power2.in' }])`);
|
|
1610
|
+
}
|
|
1611
|
+
if (!REMAPPABLE.has(spec.type)) {
|
|
1612
|
+
if (timed) warn(`pixi-effects: ${who}: "time" only works on video, audio and composition layers; put this layer in a composition and remap that (it is ignored here)`);
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
if (s.reverse !== void 0) warn(`pixi-effects: ${who}: "reverse" is not a key: write speed: -1 to play backward`);
|
|
1616
|
+
if (s.timeRemap !== void 0) warn(`pixi-effects: ${who}: "timeRemap" is not a key: animate time in keyframes (from / to / set), or write speed`);
|
|
1617
|
+
if (s.time !== void 0) warn(`pixi-effects: ${who}: "time" belongs in initial or keyframes (initial: { time: 2 }), not on the layer`);
|
|
1618
|
+
if (spec.type === "audio" && (s.sfx !== void 0 || s.music !== void 0) && (s.speed !== void 0 || timed)) {
|
|
1619
|
+
warn(`pixi-effects: ${who}: speed and time do not apply to a synthesised sound (sfx, music); change an sfx's pitch with pitch (semitones), or put it in a composition and remap that`);
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
if (s.speed !== void 0 && (typeof s.speed !== "number" || !Number.isFinite(s.speed) || s.speed === 0)) {
|
|
1623
|
+
warn(`pixi-effects: ${who}: speed ${JSON.stringify(s.speed)} cannot be used: write a number other than 0 (negative plays backward); to hold a moment, key time to the same value twice`);
|
|
1624
|
+
}
|
|
1625
|
+
if (s.speed !== void 0 && timeInKeyframes) {
|
|
1626
|
+
warn(`pixi-effects: ${who}: speed and keyframed time are both set; time wins and speed is ignored. Use one of them`);
|
|
1627
|
+
}
|
|
1628
|
+
if (timeInInitial && typeof s.initial.time !== "number") {
|
|
1629
|
+
warn(`pixi-effects: ${who}: initial.time must be a number of seconds, got ${JSON.stringify(s.initial.time)}`);
|
|
1630
|
+
}
|
|
1631
|
+
kfs.forEach((kf, i) => {
|
|
1632
|
+
for (const b of TIME_BAGS) {
|
|
1633
|
+
const v = kf[b]?.time;
|
|
1634
|
+
if (v !== void 0 && typeof v !== "number" && typeof v !== "string") warn(`pixi-effects: ${who}: keyframes[${i}].${b}.time must be a number of seconds, got ${JSON.stringify(v)}`);
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
function lintKeys(spec, warn = defaultWarn) {
|
|
1639
|
+
const who = describeLayer(spec);
|
|
1640
|
+
const valid = layerKeys(spec);
|
|
1641
|
+
if (valid) {
|
|
1642
|
+
for (const key of Object.keys(spec)) {
|
|
1643
|
+
if (valid.includes(key) || spec[key] === void 0) continue;
|
|
1644
|
+
if (REMAPPABLE.has(spec.type) && REMAP_NAMES.has(key)) continue;
|
|
1645
|
+
const guess = suggestName(key, valid.filter((k) => k !== "type"));
|
|
1646
|
+
const owners = kindsWithKey(key);
|
|
1647
|
+
const belongs = owners.length ? ` (it belongs to ${owners.slice(0, 3).join(", ")})` : "";
|
|
1648
|
+
const hint = guess ? ` \u2014 did you mean "${guess}"?${belongs}` : belongs;
|
|
1649
|
+
warn(`pixi-effects: ${who}: "${key}" is not a ${kindName(spec)} key${hint}. Valid keys: ${valid.join(", ")}`);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
lintRemap(spec, warn);
|
|
1653
|
+
const said = /* @__PURE__ */ new Set();
|
|
1654
|
+
const bags = [spec.initial, ...(spec.keyframes ?? []).flatMap((kf) => [kf.set, kf.to, kf.from])];
|
|
1655
|
+
for (const bag of bags) {
|
|
1656
|
+
for (const key of Object.keys(bag ?? {})) {
|
|
1657
|
+
if (bag[key] === void 0) continue;
|
|
1658
|
+
if (PROP_KEYS.has(key) || key.includes(".") || /^gradient/i.test(key) || said.has(key) || key === "speed") continue;
|
|
1659
|
+
said.add(key);
|
|
1660
|
+
const guess = suggestName(key, [...PROP_KEYS]);
|
|
1661
|
+
warn(`pixi-effects: ${who}: "${key}" is not an animatable property${guess ? ` \u2014 did you mean "${guess}"?` : ""} (in initial, set, to or from). See the cheatsheet for the properties of this kind of layer`);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (spec.type === "text" && spec.style && typeof spec.style === "object") {
|
|
1665
|
+
for (const key of Object.keys(spec.style)) {
|
|
1666
|
+
if (STYLE_KEYS.includes(key) || spec.style[key] === void 0) continue;
|
|
1667
|
+
const guess = suggestName(key, STYLE_KEYS);
|
|
1668
|
+
warn(`pixi-effects: ${who}: style.${key} is not a text style key${guess ? ` \u2014 did you mean "${guess}"?` : ""} (it is ignored). Keys: ${STYLE_KEYS.join(", ")}`);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
var BAGS = ["set", "to", "from"];
|
|
1673
|
+
function timeRemapOf(spec, prop = "time") {
|
|
1674
|
+
const hasInitial = !!spec.initial && "time" in spec.initial;
|
|
1675
|
+
const hasKf = (spec.keyframes ?? []).some((kf) => BAGS.some((b) => kf[b] && "time" in kf[b]));
|
|
1676
|
+
const speed = typeof spec.speed === "number" && Number.isFinite(spec.speed) && spec.speed !== 0 ? spec.speed : void 0;
|
|
1677
|
+
if (!hasInitial && !hasKf && speed === void 0) return null;
|
|
1678
|
+
const timeKfs = [];
|
|
1679
|
+
const restKfs = [];
|
|
1680
|
+
for (const kf of spec.keyframes ?? []) {
|
|
1681
|
+
const t = { ...kf }, r = { ...kf };
|
|
1682
|
+
let touched = false, left = false;
|
|
1683
|
+
for (const b of BAGS) {
|
|
1684
|
+
const bag = kf[b];
|
|
1685
|
+
if (!bag) {
|
|
1686
|
+
delete t[b];
|
|
1687
|
+
delete r[b];
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
const { time, ...others } = bag;
|
|
1691
|
+
if ("time" in bag) {
|
|
1692
|
+
touched = true;
|
|
1693
|
+
t[b] = { [prop]: time };
|
|
1694
|
+
} else delete t[b];
|
|
1695
|
+
if (Object.keys(others).length) {
|
|
1696
|
+
r[b] = others;
|
|
1697
|
+
left = true;
|
|
1698
|
+
} else delete r[b];
|
|
1699
|
+
}
|
|
1700
|
+
if (touched) timeKfs.push(t);
|
|
1701
|
+
if (!touched || left) restKfs.push(r);
|
|
1702
|
+
}
|
|
1703
|
+
let restInitial = spec.initial;
|
|
1704
|
+
let start;
|
|
1705
|
+
if (hasInitial) {
|
|
1706
|
+
const { time, ...others } = spec.initial;
|
|
1707
|
+
start = typeof time === "number" && Number.isFinite(time) ? time : void 0;
|
|
1708
|
+
restInitial = others;
|
|
1709
|
+
}
|
|
1710
|
+
return { timeKfs, restKfs: restKfs.length ? restKfs : void 0, restInitial, start, speed };
|
|
1711
|
+
}
|
|
1712
|
+
function resolveFromEnd(remap, length, prop) {
|
|
1713
|
+
const fix = (v) => typeof v === "number" && v < 0 ? Math.max(0, length + v) : v;
|
|
1714
|
+
const timeKfs = remap.timeKfs.map((kf) => {
|
|
1715
|
+
const out = { ...kf };
|
|
1716
|
+
for (const b of BAGS) {
|
|
1717
|
+
const bag = kf[b];
|
|
1718
|
+
if (bag && prop in bag) out[b] = { ...bag, [prop]: fix(bag[prop]) };
|
|
1719
|
+
}
|
|
1720
|
+
return out;
|
|
1721
|
+
});
|
|
1722
|
+
return { ...remap, timeKfs, start: remap.start === void 0 ? void 0 : fix(remap.start) };
|
|
1723
|
+
}
|
|
1724
|
+
function remapOf(spec, prop, length) {
|
|
1725
|
+
const r = timeRemapOf(spec, prop);
|
|
1726
|
+
return r && length !== null ? resolveFromEnd(r, length, prop) : r;
|
|
1727
|
+
}
|
|
1728
|
+
function contentLength(remap, duration) {
|
|
1729
|
+
let m = duration;
|
|
1730
|
+
if (remap.speed !== void 0) m = Math.max(m, Math.abs(remap.speed) * duration);
|
|
1731
|
+
if (remap.start !== void 0) m = Math.max(m, remap.start + (remap.speed ?? 1) * duration);
|
|
1732
|
+
for (const kf of remap.timeKfs) {
|
|
1733
|
+
for (const b of BAGS) {
|
|
1734
|
+
const bag = kf[b];
|
|
1735
|
+
const v = bag && Object.values(bag)[0];
|
|
1736
|
+
if (typeof v === "number") m = Math.max(m, v);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
return m;
|
|
1740
|
+
}
|
|
1741
|
+
function bindClock(timeline, clock, prop, remap, duration, at, scope) {
|
|
1742
|
+
if (remap.timeKfs.length) {
|
|
1743
|
+
if (remap.start !== void 0) timeline.set(clock, { [prop]: remap.start }, at);
|
|
1744
|
+
applyKeyframes(timeline, clock, remap.timeKfs, duration, scope, [], at);
|
|
1745
|
+
return;
|
|
1746
|
+
}
|
|
1747
|
+
const s = remap.speed ?? 1;
|
|
1748
|
+
const from = remap.start ?? (s < 0 ? -s * duration : 0);
|
|
1749
|
+
timeline.fromTo(clock, { [prop]: from }, { [prop]: from + s * duration, duration, ease: "none" }, at);
|
|
1750
|
+
}
|
|
1751
|
+
function clockTable(remap, prop, duration, at, scope, to) {
|
|
1752
|
+
const probe = gsap.timeline({ paused: true });
|
|
1753
|
+
const clock = { [prop]: remap.start ?? 0 };
|
|
1754
|
+
bindClock(probe, clock, prop, remap, duration, at, scope);
|
|
1755
|
+
probe.time(probe.duration());
|
|
1756
|
+
probe.time(0);
|
|
1757
|
+
const h = 1 / 1e3;
|
|
1758
|
+
const n = Math.max(2, Math.ceil(to / h) + 2);
|
|
1759
|
+
const tab = new Float64Array(n);
|
|
1760
|
+
let min = Infinity, max = -Infinity;
|
|
1761
|
+
for (let k = 0; k < n; k++) {
|
|
1762
|
+
probe.time(k * h);
|
|
1763
|
+
const v = clock[prop];
|
|
1764
|
+
tab[k] = v;
|
|
1765
|
+
const t = k * h;
|
|
1766
|
+
if (t >= at && t <= at + duration) {
|
|
1767
|
+
if (v < min) min = v;
|
|
1768
|
+
if (v > max) max = v;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
probe.kill();
|
|
1772
|
+
return {
|
|
1773
|
+
at(t) {
|
|
1774
|
+
const x = Math.min(Math.max(t / h, 0), n - 1.000001);
|
|
1775
|
+
const k = Math.floor(x), f = x - k;
|
|
1776
|
+
return tab[k] * (1 - f) + tab[k + 1] * f;
|
|
1777
|
+
},
|
|
1778
|
+
min: Number.isFinite(min) ? min : 0,
|
|
1779
|
+
max: Number.isFinite(max) ? max : 0
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1071
1782
|
|
|
1072
1783
|
// src/space/math.ts
|
|
1073
1784
|
var DEG = Math.PI / 180;
|
|
@@ -1393,6 +2104,7 @@ function shortestHueDelta(from, to) {
|
|
|
1393
2104
|
}
|
|
1394
2105
|
|
|
1395
2106
|
// src/expr/colorTween.ts
|
|
2107
|
+
var css = (c) => typeof c === "number" ? "#" + c.toString(16).padStart(6, "0") : c;
|
|
1396
2108
|
function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, at, colorSpace, onUpdate, loop = {}) {
|
|
1397
2109
|
let interp = null;
|
|
1398
2110
|
const proxy = { p: 0 };
|
|
@@ -1407,7 +2119,7 @@ function tweenColor(timeline, target, key, fromValue, toValue, duration, ease, a
|
|
|
1407
2119
|
onStart: () => {
|
|
1408
2120
|
const start = fromValue !== void 0 ? fromValue : target[key];
|
|
1409
2121
|
if (start === void 0) return;
|
|
1410
|
-
interp = colorSpace === "rgb" ? gsap.utils.interpolate(start, toValue) : buildColorInterp(start, toValue, colorSpace);
|
|
2122
|
+
interp = colorSpace === "rgb" ? gsap.utils.interpolate(css(start), css(toValue)) : buildColorInterp(start, toValue, colorSpace);
|
|
1411
2123
|
},
|
|
1412
2124
|
onUpdate: () => {
|
|
1413
2125
|
if (interp) {
|
|
@@ -1451,7 +2163,7 @@ var ImageSequence = class extends Sequence {
|
|
|
1451
2163
|
if (initTint !== void 0) this.target.tint = initTint;
|
|
1452
2164
|
for (const kf of this.spec.keyframes ?? []) {
|
|
1453
2165
|
const at = startTime + resolveAt(kf.at, this.duration);
|
|
1454
|
-
const duration = kf
|
|
2166
|
+
const duration = kfDuration(kf);
|
|
1455
2167
|
const ease = kf.ease ?? "none";
|
|
1456
2168
|
const fromTint = kf.from?.tint;
|
|
1457
2169
|
const toTint = kf.to?.tint;
|
|
@@ -1508,6 +2220,207 @@ function stripKeysInKeyframe(kf, keys) {
|
|
|
1508
2220
|
from: stripKeys(kf.from, keys)
|
|
1509
2221
|
};
|
|
1510
2222
|
}
|
|
2223
|
+
|
|
2224
|
+
// src/sequences/gradient.ts
|
|
2225
|
+
function cssColor(c) {
|
|
2226
|
+
return typeof c === "number" ? "#" + c.toString(16).padStart(6, "0") : c;
|
|
2227
|
+
}
|
|
2228
|
+
function normalizedStops(g) {
|
|
2229
|
+
const stops = g.stops ?? [];
|
|
2230
|
+
if (stops.length < 2) throw new Error('pixi-effects: fillGradient needs at least two colour stops, e.g. stops: [[0, "#000"], [1, "#fff"]]');
|
|
2231
|
+
return stops.map((s) => Array.isArray(s) ? { offset: s[0], color: s[1] } : { offset: s.offset, color: s.color });
|
|
2232
|
+
}
|
|
2233
|
+
function gradientOptions(g) {
|
|
2234
|
+
const colorStops = normalizedStops(g);
|
|
2235
|
+
const a = (g.angle ?? 90) * Math.PI / 180;
|
|
2236
|
+
const dx = Math.cos(a), dy = Math.sin(a);
|
|
2237
|
+
const h = (Math.abs(dx) + Math.abs(dy)) / 2;
|
|
2238
|
+
return {
|
|
2239
|
+
type: "linear",
|
|
2240
|
+
start: { x: 0.5 - dx * h, y: 0.5 - dy * h },
|
|
2241
|
+
end: { x: 0.5 + dx * h, y: 0.5 + dy * h },
|
|
2242
|
+
colorStops,
|
|
2243
|
+
textureSpace: "local"
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
function paintRadial(ctx, size, g) {
|
|
2247
|
+
const stops = normalizedStops(g);
|
|
2248
|
+
const [cx, cy] = g.center ?? [0.5, 0.5];
|
|
2249
|
+
const grad = ctx.createRadialGradient(cx * size, cy * size, (g.innerRadius ?? 0) * size, cx * size, cy * size, (g.radius ?? 0.5) * size);
|
|
2250
|
+
for (const s of stops) grad.addColorStop(s.offset, cssColor(s.color));
|
|
2251
|
+
ctx.fillStyle = grad;
|
|
2252
|
+
ctx.fillRect(0, 0, size, size);
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
// src/sequences/gradientAnim.ts
|
|
2256
|
+
var PATCH_KEYS = ["type", "angle", "center", "innerRadius", "radius", "stops"];
|
|
2257
|
+
function gradStateFrom(g) {
|
|
2258
|
+
return {
|
|
2259
|
+
type: g.type ?? "linear",
|
|
2260
|
+
angle: g.angle ?? 90,
|
|
2261
|
+
center: [...g.center ?? [0.5, 0.5]],
|
|
2262
|
+
innerRadius: g.innerRadius ?? 0,
|
|
2263
|
+
radius: g.radius ?? 0.5,
|
|
2264
|
+
stops: (g.stops ?? []).map((s) => Array.isArray(s) ? { offset: s[0], color: cssColor(s[1]) } : { offset: s.offset, color: cssColor(s.color) })
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
function mergeGrad(base, patch) {
|
|
2268
|
+
const out = { ...base, center: [...base.center], stops: base.stops.map((s) => ({ ...s })) };
|
|
2269
|
+
if (patch === null || typeof patch !== "object" || Array.isArray(patch)) return out;
|
|
2270
|
+
const p = patch;
|
|
2271
|
+
if (typeof p.angle === "number") out.angle = p.angle;
|
|
2272
|
+
if (Array.isArray(p.center)) out.center = [Number(p.center[0]), Number(p.center[1])];
|
|
2273
|
+
if (typeof p.innerRadius === "number") out.innerRadius = p.innerRadius;
|
|
2274
|
+
if (typeof p.radius === "number") out.radius = p.radius;
|
|
2275
|
+
if (p.stops !== void 0) {
|
|
2276
|
+
const next = gradStateFrom({ stops: p.stops }).stops;
|
|
2277
|
+
if (next.length === base.stops.length) out.stops = next;
|
|
2278
|
+
}
|
|
2279
|
+
return out;
|
|
2280
|
+
}
|
|
2281
|
+
var objectHint = "a keyframe's fillGradient must be an object like { angle: 200, stops: [[0, '#f00'], [1, '#00f']] }";
|
|
2282
|
+
function validateGradientKeyframes(spec, who, warn = (m) => console.warn(m), kind = "shape") {
|
|
2283
|
+
const said = /* @__PURE__ */ new Set();
|
|
2284
|
+
const say = (m) => {
|
|
2285
|
+
if (!said.has(m)) {
|
|
2286
|
+
said.add(m);
|
|
2287
|
+
warn(`pixi-effects: ${who}: ${m}`);
|
|
2288
|
+
}
|
|
2289
|
+
};
|
|
2290
|
+
const startSpec = spec.fillGradient ?? spec.initial?.fillGradient;
|
|
2291
|
+
const start = startSpec && typeof startSpec === "object" && Array.isArray(startSpec.stops) ? gradStateFrom(startSpec) : null;
|
|
2292
|
+
let animated = false;
|
|
2293
|
+
if (kind === "text") {
|
|
2294
|
+
const radial = (g) => !!g && typeof g === "object" && ["type", "center", "innerRadius", "radius"].some((k) => g[k] !== void 0 && !(k === "type" && g.type === "linear"));
|
|
2295
|
+
const asked = radial(startSpec) || (spec.keyframes ?? []).some((kf) => [kf.set, kf.to, kf.from].some((b) => radial(b?.fillGradient)));
|
|
2296
|
+
if (asked) say("a text gradient is linear: type, center, innerRadius and radius are ignored (only angle and stops apply); use a shape for a radial gradient");
|
|
2297
|
+
}
|
|
2298
|
+
for (const kf of spec.keyframes ?? []) {
|
|
2299
|
+
for (const bag of [kf.set, kf.to, kf.from]) {
|
|
2300
|
+
if (!bag) continue;
|
|
2301
|
+
for (const [key, value] of Object.entries(bag)) {
|
|
2302
|
+
if (key === "fillGradient") {
|
|
2303
|
+
animated = true;
|
|
2304
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
2305
|
+
say(objectHint);
|
|
2306
|
+
continue;
|
|
2307
|
+
}
|
|
2308
|
+
const p = value;
|
|
2309
|
+
for (const k of Object.keys(p)) {
|
|
2310
|
+
if (PATCH_KEYS.includes(k)) continue;
|
|
2311
|
+
const guess = suggestName(k, PATCH_KEYS.filter((x) => x !== "type"));
|
|
2312
|
+
say(`fillGradient.${k} is not a gradient property${guess ? ` \u2014 did you mean "${guess}"?` : ""} (valid: ${PATCH_KEYS.join(", ")}); it is ignored`);
|
|
2313
|
+
}
|
|
2314
|
+
if (p.type !== void 0 && start && p.type !== start.type) say("fillGradient.type cannot change over time (linear <-> radial); it is ignored");
|
|
2315
|
+
if (p.stops !== void 0 && start) {
|
|
2316
|
+
const n = Array.isArray(p.stops) ? p.stops.length : 0;
|
|
2317
|
+
if (n !== start.stops.length) say(`a fillGradient keyframe must give the same number of stops as the gradient has (${start.stops.length}, got ${n}); the stops are left as they were`);
|
|
2318
|
+
}
|
|
2319
|
+
} else if (/^fillGradient\./i.test(key) || /^gradient/i.test(key)) {
|
|
2320
|
+
const tail = key.replace(/^fillGradient\./i, "").replace(/^gradient/i, "");
|
|
2321
|
+
const sub = tail ? tail[0].toLowerCase() + tail.slice(1) : "angle";
|
|
2322
|
+
say(`"${key}" is not an animatable property: write fillGradient: { ${PATCH_KEYS.includes(sub) ? sub : "angle"} } (the whole gradient is one object) in the keyframe's to / from / set`);
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
if (animated && !start) say("a keyframe animates fillGradient but the layer has none to start from: give it a fillGradient first (top level or in initial)");
|
|
2328
|
+
}
|
|
2329
|
+
function tweenGradient(timeline, holder, fromPatch, toPatch, duration, ease, at, colorSpace, onChange, loop = {}) {
|
|
2330
|
+
let a = null, b = null;
|
|
2331
|
+
let resting = null;
|
|
2332
|
+
let colors = [];
|
|
2333
|
+
const proxy = { p: 0 };
|
|
2334
|
+
timeline.fromTo(proxy, { p: 0 }, {
|
|
2335
|
+
p: 1,
|
|
2336
|
+
duration,
|
|
2337
|
+
ease,
|
|
2338
|
+
...loop,
|
|
2339
|
+
onStart: () => {
|
|
2340
|
+
const live = holder.grad;
|
|
2341
|
+
a = fromPatch !== void 0 ? mergeGrad(live, fromPatch) : live;
|
|
2342
|
+
if (toPatch === void 0) resting ??= live;
|
|
2343
|
+
b = toPatch !== void 0 ? mergeGrad(a, toPatch) : resting;
|
|
2344
|
+
colors = a.stops.map((s, i) => colorSpace === "rgb" ? gsap.utils.interpolate(s.color, b.stops[i].color) : buildColorInterp(s.color, b.stops[i].color, colorSpace));
|
|
2345
|
+
},
|
|
2346
|
+
onUpdate: () => {
|
|
2347
|
+
if (!a || !b) return;
|
|
2348
|
+
const t = proxy.p;
|
|
2349
|
+
const l = (x, y) => x + (y - x) * t;
|
|
2350
|
+
holder.grad = {
|
|
2351
|
+
type: a.type,
|
|
2352
|
+
angle: l(a.angle, b.angle),
|
|
2353
|
+
center: [l(a.center[0], b.center[0]), l(a.center[1], b.center[1])],
|
|
2354
|
+
innerRadius: l(a.innerRadius, b.innerRadius),
|
|
2355
|
+
radius: l(a.radius, b.radius),
|
|
2356
|
+
stops: a.stops.map((s, i) => ({ offset: l(s.offset, b.stops[i].offset), color: colors[i](t) }))
|
|
2357
|
+
};
|
|
2358
|
+
onChange();
|
|
2359
|
+
}
|
|
2360
|
+
}, at);
|
|
2361
|
+
}
|
|
2362
|
+
var PAINT_SIZE = 512;
|
|
2363
|
+
var GradientPainter = class {
|
|
2364
|
+
constructor(size = PAINT_SIZE) {
|
|
2365
|
+
this.size = size;
|
|
2366
|
+
this.canvas.width = size;
|
|
2367
|
+
this.canvas.height = size;
|
|
2368
|
+
this.tex = Texture.from(this.canvas);
|
|
2369
|
+
globalThis.__gradientPainters = (globalThis.__gradientPainters ?? 0) + 1;
|
|
2370
|
+
}
|
|
2371
|
+
size;
|
|
2372
|
+
canvas = document.createElement("canvas");
|
|
2373
|
+
tex;
|
|
2374
|
+
/** Paint `g` and return what `Graphics.fill(...)` takes. */
|
|
2375
|
+
paint(g) {
|
|
2376
|
+
const ctx = this.canvas.getContext("2d"), s = this.size;
|
|
2377
|
+
if (!ctx) return new FillGradient(gradientOptions({ angle: g.angle, stops: g.stops.map((x) => [x.offset, x.color]) }));
|
|
2378
|
+
ctx.clearRect(0, 0, s, s);
|
|
2379
|
+
if (g.type === "radial") {
|
|
2380
|
+
paintRadial(ctx, s, { stops: g.stops.map((x) => [x.offset, x.color]), center: g.center, innerRadius: g.innerRadius, radius: g.radius });
|
|
2381
|
+
} else {
|
|
2382
|
+
const a = g.angle * Math.PI / 180, dx = Math.cos(a), dy = Math.sin(a);
|
|
2383
|
+
const h = (Math.abs(dx) + Math.abs(dy)) / 2;
|
|
2384
|
+
const grad = ctx.createLinearGradient((0.5 - dx * h) * s, (0.5 - dy * h) * s, (0.5 + dx * h) * s, (0.5 + dy * h) * s);
|
|
2385
|
+
for (const st of g.stops) grad.addColorStop(Math.min(1, Math.max(0, st.offset)), st.color);
|
|
2386
|
+
ctx.fillStyle = grad;
|
|
2387
|
+
ctx.fillRect(0, 0, s, s);
|
|
2388
|
+
}
|
|
2389
|
+
this.tex.source.update();
|
|
2390
|
+
return { texture: this.tex, textureSpace: "local" };
|
|
2391
|
+
}
|
|
2392
|
+
/** Give the texture and the canvas back (a layer that is destroyed must not leave them). */
|
|
2393
|
+
destroy() {
|
|
2394
|
+
this.tex.destroy(true);
|
|
2395
|
+
this.canvas.width = this.canvas.height = 0;
|
|
2396
|
+
globalThis.__gradientPainters = Math.max(0, (globalThis.__gradientPainters ?? 1) - 1);
|
|
2397
|
+
}
|
|
2398
|
+
};
|
|
2399
|
+
function bindGradientKeyframes(timeline, holder, keyframes, duration, offset, colorSpace, onChange) {
|
|
2400
|
+
const bag = (b) => b?.fillGradient;
|
|
2401
|
+
for (const kf of keyframes) {
|
|
2402
|
+
const at = offset + resolveAt(kf.at, duration);
|
|
2403
|
+
const setV = bag(kf.set), fromV = bag(kf.from), toV = bag(kf.to);
|
|
2404
|
+
if (setV !== void 0) {
|
|
2405
|
+
revertibleSet(
|
|
2406
|
+
timeline,
|
|
2407
|
+
at,
|
|
2408
|
+
() => ({ state: holder.grad }),
|
|
2409
|
+
(v) => {
|
|
2410
|
+
holder.grad = v.state ?? mergeGrad(holder.grad, v.patch);
|
|
2411
|
+
onChange();
|
|
2412
|
+
},
|
|
2413
|
+
{ patch: setV }
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
if (fromV !== void 0 || toV !== void 0) {
|
|
2417
|
+
tweenGradient(timeline, holder, fromV, toV, kfDuration(kf), kf.ease ?? "none", at, colorSpace, onChange, loopVars(kf));
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
var hasGradientKeys = (keyframes) => (keyframes ?? []).some((kf) => [kf.set, kf.to, kf.from].some((b) => b && b.fillGradient !== void 0));
|
|
2422
|
+
|
|
2423
|
+
// src/sequences/Text.ts
|
|
1511
2424
|
var STYLE_OPAQUE_KEYS = [
|
|
1512
2425
|
"fontFamily",
|
|
1513
2426
|
"fill",
|
|
@@ -1542,6 +2455,21 @@ var TextSequence = class extends Sequence {
|
|
|
1542
2455
|
/** `visibleChars` is in play (initial or animated): otherwise the whole text shows. */
|
|
1543
2456
|
_typed = false;
|
|
1544
2457
|
_lastText = "";
|
|
2458
|
+
/** A gradient fill for the text (`fillGradient`, static or animated): rebuilt as a FillGradient on each change (the text is re-rasterised). */
|
|
2459
|
+
_grad = null;
|
|
2460
|
+
_fillGradient = null;
|
|
2461
|
+
_applyGradient() {
|
|
2462
|
+
const g = this._grad.grad;
|
|
2463
|
+
const next = new FillGradient(gradientOptions({ angle: g.angle, stops: g.stops.map((s) => [s.offset, s.color]) }));
|
|
2464
|
+
this.target.style.fill = next;
|
|
2465
|
+
this._fillGradient?.destroy();
|
|
2466
|
+
this._fillGradient = next;
|
|
2467
|
+
}
|
|
2468
|
+
destroy() {
|
|
2469
|
+
super.destroy();
|
|
2470
|
+
this._fillGradient?.destroy();
|
|
2471
|
+
this._fillGradient = null;
|
|
2472
|
+
}
|
|
1545
2473
|
/** A typewriter that has typed nothing yet: the layer is empty on purpose (inspect must not call that "no size"). */
|
|
1546
2474
|
get showsNothingYet() {
|
|
1547
2475
|
return this._typed && this._lastText === "" && this._template !== "";
|
|
@@ -1588,6 +2516,12 @@ var TextSequence = class extends Sequence {
|
|
|
1588
2516
|
this.intrinsicWidth = text.width;
|
|
1589
2517
|
this.intrinsicHeight = text.height;
|
|
1590
2518
|
}
|
|
2519
|
+
validateGradientKeyframes(this.spec, describeLayer(this.spec), void 0, "text");
|
|
2520
|
+
const gradientSpec = this.spec.fillGradient ?? initialProps.fillGradient;
|
|
2521
|
+
if (gradientSpec) {
|
|
2522
|
+
this._grad = { grad: gradStateFrom(gradientSpec) };
|
|
2523
|
+
this._applyGradient();
|
|
2524
|
+
}
|
|
1591
2525
|
const scope = this.scope();
|
|
1592
2526
|
if (initialProps.value !== void 0) this._counters.value = resolveNumber(initialProps.value, scope);
|
|
1593
2527
|
this._typed = initialProps.visibleChars !== void 0 || animates(this.spec.keyframes, "visibleChars");
|
|
@@ -1606,11 +2540,12 @@ var TextSequence = class extends Sequence {
|
|
|
1606
2540
|
bindTimeline(timeline, offset = 0) {
|
|
1607
2541
|
if (!this.target) return;
|
|
1608
2542
|
const colorSpace = this.spec.colorSpace ?? "rgb";
|
|
1609
|
-
const stripped = stripKeys2(this.spec, ["fill", "value", "text", "visibleChars"]);
|
|
2543
|
+
const stripped = stripKeys2(this.spec, ["fill", "value", "text", "visibleChars", "fillGradient"]);
|
|
1610
2544
|
runSuperWithStrippedSpec(this, stripped, timeline, offset);
|
|
1611
2545
|
const keyframes = this.spec.keyframes ?? [];
|
|
1612
2546
|
const origin = offset + this.at;
|
|
1613
2547
|
bindFillKeyframes(timeline, this.target, keyframes, this.duration, origin, colorSpace);
|
|
2548
|
+
if (this._grad) bindGradientKeyframes(timeline, this._grad, keyframes, this.duration, origin, colorSpace, () => this._applyGradient());
|
|
1614
2549
|
const refresh = () => this._refreshText();
|
|
1615
2550
|
bindCounterKeyframes(timeline, this._counters, "value", refresh, keyframes, this.duration, origin, this.scope());
|
|
1616
2551
|
bindCounterKeyframes(timeline, this._counters, "visibleChars", refresh, keyframes, this.duration, origin, this.scope());
|
|
@@ -1660,7 +2595,7 @@ function bindCounterKeyframes(timeline, state, key, refresh, keyframes, parentDu
|
|
|
1660
2595
|
const toV = kf.to?.[key];
|
|
1661
2596
|
if (setV === void 0 && fromV === void 0 && toV === void 0) continue;
|
|
1662
2597
|
const at = origin + resolveAt(kf.at, parentDuration);
|
|
1663
|
-
const duration = kf
|
|
2598
|
+
const duration = kfDuration(kf);
|
|
1664
2599
|
const ease = kf.ease ?? "none";
|
|
1665
2600
|
const loop = loopVars(kf);
|
|
1666
2601
|
if (setV !== void 0) {
|
|
@@ -1706,7 +2641,7 @@ function bindFillKeyframes(timeline, text, keyframes, parentDuration, offset, co
|
|
|
1706
2641
|
};
|
|
1707
2642
|
for (const kf of keyframes) {
|
|
1708
2643
|
const at = offset + resolveAt(kf.at, parentDuration);
|
|
1709
|
-
const duration = kf
|
|
2644
|
+
const duration = kfDuration(kf);
|
|
1710
2645
|
const ease = kf.ease ?? "none";
|
|
1711
2646
|
const setFill = pickFill(kf.set);
|
|
1712
2647
|
const fromFill = pickFill(kf.from);
|
|
@@ -1744,6 +2679,7 @@ var AudioSequence = class extends Sequence {
|
|
|
1744
2679
|
_audioBuffer = null;
|
|
1745
2680
|
_synth = null;
|
|
1746
2681
|
_source = "";
|
|
2682
|
+
_remap = null;
|
|
1747
2683
|
async build() {
|
|
1748
2684
|
this.target = null;
|
|
1749
2685
|
if (this.spec.music !== void 0) return this._buildMusic(this.spec);
|
|
@@ -1752,13 +2688,16 @@ var AudioSequence = class extends Sequence {
|
|
|
1752
2688
|
this._source = `asset "${spec.asset}"`;
|
|
1753
2689
|
const data = await Assets.get(spec.asset);
|
|
1754
2690
|
this._audioBuffer = data.audioBuffer;
|
|
2691
|
+
this._remap = remapOf(spec, "time", data.duration ?? null);
|
|
2692
|
+
const constant = this._remap && this._remap.timeKfs.length === 0 ? Math.abs(this._remap.speed ?? 1) : 1;
|
|
1755
2693
|
if (this.duration === void 0) {
|
|
1756
2694
|
const rest = Math.max(0, (this.parent?.duration ?? this.root.duration) - this.at);
|
|
1757
|
-
this.duration = spec.duration ?? (spec.loop ? rest : data.duration) ?? this.root.duration;
|
|
2695
|
+
this.duration = spec.duration ?? (spec.loop ? rest : data.duration !== void 0 ? data.duration / constant : void 0) ?? this.root.duration;
|
|
1758
2696
|
}
|
|
1759
|
-
|
|
2697
|
+
const playable = this._remap && this._remap.timeKfs.length > 0 ? Infinity : (data.duration ?? Infinity) / constant;
|
|
2698
|
+
if (!spec.loop && Number.isFinite(playable) && this.duration > playable + 0.05) {
|
|
1760
2699
|
console.warn(
|
|
1761
|
-
`pixi-effects: ${describeLayer(spec)}: audio asset "${spec.asset}"
|
|
2700
|
+
`pixi-effects: ${describeLayer(spec)}: audio asset "${spec.asset}" plays for ${playable.toFixed(1)}s (${data.duration.toFixed(1)}s at speed ${constant}) but the layer lasts ${this.duration.toFixed(1)}s, so it goes silent after ${playable.toFixed(1)}s. Add loop: true, or shorten the layer's duration.`
|
|
1762
2701
|
);
|
|
1763
2702
|
}
|
|
1764
2703
|
}
|
|
@@ -1767,7 +2706,7 @@ var AudioSequence = class extends Sequence {
|
|
|
1767
2706
|
const raw = spec;
|
|
1768
2707
|
if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and sfx \u2014 the sfx plays; remove one`);
|
|
1769
2708
|
if (raw.loop) console.warn(`pixi-effects: ${who}: loop has no effect on an sfx \u2014 to repeat it, add one audio layer per hit (a JS loop)`);
|
|
1770
|
-
const { resolveSfx, renderSfx, sfxKey } = await import('./sfx-
|
|
2709
|
+
const { resolveSfx, renderSfx, sfxKey } = await import('./sfx-U3GM2MED.js');
|
|
1771
2710
|
const sfx = resolveSfx(spec.sfx, spec.duration, who);
|
|
1772
2711
|
if (!sfx) return;
|
|
1773
2712
|
this.duration = sfx.length;
|
|
@@ -1786,7 +2725,7 @@ var AudioSequence = class extends Sequence {
|
|
|
1786
2725
|
const raw = spec;
|
|
1787
2726
|
if (raw.asset !== void 0) console.warn(`pixi-effects: ${who}: has both asset and music \u2014 the music plays; remove one`);
|
|
1788
2727
|
if (raw.sfx !== void 0) console.warn(`pixi-effects: ${who}: has both sfx and music \u2014 the music plays; remove one`);
|
|
1789
|
-
const { resolveMusic, renderMusic, musicLength, musicKey } = await import('./music-
|
|
2728
|
+
const { resolveMusic, renderMusic, musicLength, musicKey } = await import('./music-CFEC7HKL.js');
|
|
1790
2729
|
const music = resolveMusic(spec.music, who);
|
|
1791
2730
|
if (!music) return;
|
|
1792
2731
|
const natural = musicLength(music);
|
|
@@ -1812,7 +2751,9 @@ var AudioSequence = class extends Sequence {
|
|
|
1812
2751
|
const initialVolume = this.spec.volume ?? this.spec.initial?.volume ?? 1;
|
|
1813
2752
|
const t0 = baseTime + this.at;
|
|
1814
2753
|
const dur = this.duration;
|
|
2754
|
+
const sourceMap = this._remap ? clockTable(this._remap, "time", dur, t0, this.scope(), t0 + dur).at : void 0;
|
|
1815
2755
|
out.push({
|
|
2756
|
+
...sourceMap ? { sourceMap } : {},
|
|
1816
2757
|
...this._audioBuffer ? { buffer: this._audioBuffer } : { synth: this._synth },
|
|
1817
2758
|
layer: describeLayer(this.spec),
|
|
1818
2759
|
source: this._source,
|
|
@@ -1871,7 +2812,7 @@ var FrameCache = class {
|
|
|
1871
2812
|
this.capacity = options.capacity ?? 30;
|
|
1872
2813
|
}
|
|
1873
2814
|
_key(time) {
|
|
1874
|
-
return Math.round(time *
|
|
2815
|
+
return Math.round(time * 1e6);
|
|
1875
2816
|
}
|
|
1876
2817
|
async getFrameAt(time) {
|
|
1877
2818
|
const key = this._key(time);
|
|
@@ -1936,6 +2877,15 @@ var FrameCache = class {
|
|
|
1936
2877
|
}
|
|
1937
2878
|
};
|
|
1938
2879
|
|
|
2880
|
+
// src/core/sourceTime.ts
|
|
2881
|
+
var FRAME_EPS = 5e-5;
|
|
2882
|
+
function sourceLookup(v, sourceDuration, loop) {
|
|
2883
|
+
if (!(sourceDuration > 0)) return Math.max(0, v);
|
|
2884
|
+
const w = v + FRAME_EPS;
|
|
2885
|
+
if (loop) return (w % sourceDuration + sourceDuration) % sourceDuration;
|
|
2886
|
+
return Math.min(Math.max(w, 0), sourceDuration);
|
|
2887
|
+
}
|
|
2888
|
+
|
|
1939
2889
|
// src/sequences/Video.ts
|
|
1940
2890
|
var VideoSequence = class extends Sequence {
|
|
1941
2891
|
_sourceDuration = 0;
|
|
@@ -1944,9 +2894,15 @@ var VideoSequence = class extends Sequence {
|
|
|
1944
2894
|
_canvas = null;
|
|
1945
2895
|
_ctx = null;
|
|
1946
2896
|
_drawSeq = 0;
|
|
2897
|
+
/** The playhead value (seconds in the file) the timeline last wrote: already remapped, and local inside a remapped composition. */
|
|
2898
|
+
_cur = 0;
|
|
2899
|
+
_remap = null;
|
|
2900
|
+
/** Movie asks this layer to draw the frame its own timeline is at, not the one for a time it computed. */
|
|
2901
|
+
selfTimed = true;
|
|
1947
2902
|
async build() {
|
|
1948
2903
|
const data = await Assets.get(this.spec.asset);
|
|
1949
2904
|
this._sourceDuration = data.duration;
|
|
2905
|
+
this._remap = remapOf(this.spec, "currentTime", this._sourceDuration);
|
|
1950
2906
|
this._audioBuffer = this.spec.audio !== false ? data.audioBuffer : null;
|
|
1951
2907
|
this._cache = new FrameCache(data.sink, { capacity: 30 });
|
|
1952
2908
|
const probeFrame = await this._cache.getFrameAt(0);
|
|
@@ -1965,7 +2921,8 @@ var VideoSequence = class extends Sequence {
|
|
|
1965
2921
|
this.intrinsicWidth = w;
|
|
1966
2922
|
this.intrinsicHeight = h;
|
|
1967
2923
|
if (this.duration === void 0) {
|
|
1968
|
-
this.
|
|
2924
|
+
const constant = this._remap && this._remap.timeKfs.length === 0 ? Math.abs(this._remap.speed ?? 1) : 1;
|
|
2925
|
+
this.duration = this.spec.duration ?? (data.duration ?? this.root.duration * constant) / constant;
|
|
1969
2926
|
}
|
|
1970
2927
|
let currentTime = 0;
|
|
1971
2928
|
const seq = this;
|
|
@@ -1975,8 +2932,9 @@ var VideoSequence = class extends Sequence {
|
|
|
1975
2932
|
},
|
|
1976
2933
|
set(v) {
|
|
1977
2934
|
currentTime = v;
|
|
2935
|
+
seq._cur = v;
|
|
1978
2936
|
const mySeq = ++seq._drawSeq;
|
|
1979
|
-
const lookup =
|
|
2937
|
+
const lookup = sourceLookup(v, seq._sourceDuration, !!seq.spec.loop);
|
|
1980
2938
|
seq._cache.getFrameAt(lookup).then((frame) => {
|
|
1981
2939
|
if (mySeq !== seq._drawSeq) return;
|
|
1982
2940
|
if (!frame || !seq._ctx) return;
|
|
@@ -1989,21 +2947,33 @@ var VideoSequence = class extends Sequence {
|
|
|
1989
2947
|
});
|
|
1990
2948
|
this.buildFilters();
|
|
1991
2949
|
}
|
|
2950
|
+
displayProps() {
|
|
2951
|
+
const r = this._remap;
|
|
2952
|
+
return r ? { initial: r.restInitial, keyframes: r.restKfs } : super.displayProps();
|
|
2953
|
+
}
|
|
1992
2954
|
bindTimeline(timeline, offset = 0) {
|
|
1993
2955
|
super.bindTimeline(timeline, offset);
|
|
2956
|
+
const at = offset + this.at;
|
|
2957
|
+
if (this._remap) {
|
|
2958
|
+
bindClock(timeline, this.target, "currentTime", this._remap, this.duration, at, this.scope());
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
1994
2961
|
const playDuration = this.spec.loop ? this.duration : Math.min(this.duration, this._sourceDuration);
|
|
1995
2962
|
timeline.fromTo(
|
|
1996
2963
|
this.target,
|
|
1997
2964
|
{ currentTime: 0 },
|
|
1998
2965
|
{ currentTime: playDuration, duration: playDuration, ease: "none" },
|
|
1999
|
-
|
|
2966
|
+
at
|
|
2000
2967
|
// offset = start of the enclosing composition(s); dropping it misplaced videos inside nested compositions
|
|
2001
2968
|
);
|
|
2002
2969
|
}
|
|
2003
2970
|
collectAudio(out, baseTime) {
|
|
2004
2971
|
if (!this._audioBuffer) return;
|
|
2005
2972
|
const initialVolume = this.spec.volume ?? 1;
|
|
2973
|
+
const t0 = baseTime + this.at;
|
|
2974
|
+
const sourceMap = this._remap ? clockTable(this._remap, "currentTime", this.duration, t0, this.scope(), t0 + this.duration).at : void 0;
|
|
2006
2975
|
out.push({
|
|
2976
|
+
...sourceMap ? { sourceMap } : {},
|
|
2007
2977
|
buffer: this._audioBuffer,
|
|
2008
2978
|
layer: describeLayer(this.spec),
|
|
2009
2979
|
source: `video "${this.spec.asset}"`,
|
|
@@ -2014,9 +2984,12 @@ var VideoSequence = class extends Sequence {
|
|
|
2014
2984
|
volumeKeyframes: []
|
|
2015
2985
|
});
|
|
2016
2986
|
}
|
|
2017
|
-
|
|
2987
|
+
/** Draw the frame the playhead is at NOW (`_cur`). The argument (a time Movie worked out for layers that need it) is not used. */
|
|
2988
|
+
async awaitFrameAt(_time) {
|
|
2989
|
+
const time = this._cur;
|
|
2990
|
+
if (!this.keepHidden && !this.target?.renderable) return;
|
|
2018
2991
|
const mySeq = ++this._drawSeq;
|
|
2019
|
-
const lookup =
|
|
2992
|
+
const lookup = sourceLookup(time, this._sourceDuration, !!this.spec.loop);
|
|
2020
2993
|
let frame = null;
|
|
2021
2994
|
try {
|
|
2022
2995
|
frame = await this._cache.getFrameAt(lookup);
|
|
@@ -2040,39 +3013,6 @@ var VideoSequence = class extends Sequence {
|
|
|
2040
3013
|
super.destroy();
|
|
2041
3014
|
}
|
|
2042
3015
|
};
|
|
2043
|
-
|
|
2044
|
-
// src/sequences/gradient.ts
|
|
2045
|
-
function cssColor(c) {
|
|
2046
|
-
return typeof c === "number" ? "#" + c.toString(16).padStart(6, "0") : c;
|
|
2047
|
-
}
|
|
2048
|
-
function normalizedStops(g) {
|
|
2049
|
-
const stops = g.stops ?? [];
|
|
2050
|
-
if (stops.length < 2) throw new Error('pixi-effects: fillGradient needs at least two colour stops, e.g. stops: [[0, "#000"], [1, "#fff"]]');
|
|
2051
|
-
return stops.map((s) => Array.isArray(s) ? { offset: s[0], color: s[1] } : { offset: s.offset, color: s.color });
|
|
2052
|
-
}
|
|
2053
|
-
function gradientOptions(g) {
|
|
2054
|
-
const colorStops = normalizedStops(g);
|
|
2055
|
-
const a = (g.angle ?? 90) * Math.PI / 180;
|
|
2056
|
-
const dx = Math.cos(a), dy = Math.sin(a);
|
|
2057
|
-
const h = (Math.abs(dx) + Math.abs(dy)) / 2;
|
|
2058
|
-
return {
|
|
2059
|
-
type: "linear",
|
|
2060
|
-
start: { x: 0.5 - dx * h, y: 0.5 - dy * h },
|
|
2061
|
-
end: { x: 0.5 + dx * h, y: 0.5 + dy * h },
|
|
2062
|
-
colorStops,
|
|
2063
|
-
textureSpace: "local"
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
function paintRadial(ctx, size, g) {
|
|
2067
|
-
const stops = normalizedStops(g);
|
|
2068
|
-
const [cx, cy] = g.center ?? [0.5, 0.5];
|
|
2069
|
-
const grad = ctx.createRadialGradient(cx * size, cy * size, (g.innerRadius ?? 0) * size, cx * size, cy * size, (g.radius ?? 0.5) * size);
|
|
2070
|
-
for (const s of stops) grad.addColorStop(s.offset, cssColor(s.color));
|
|
2071
|
-
ctx.fillStyle = grad;
|
|
2072
|
-
ctx.fillRect(0, 0, size, size);
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
|
-
// src/sequences/gradientFill.ts
|
|
2076
3016
|
var RADIAL_SIZE = 256;
|
|
2077
3017
|
function makeGradientFill(g) {
|
|
2078
3018
|
if (g.type === "radial") {
|
|
@@ -2264,7 +3204,7 @@ function morphAt(pairs, t) {
|
|
|
2264
3204
|
}
|
|
2265
3205
|
|
|
2266
3206
|
// src/sequences/Shape.ts
|
|
2267
|
-
var
|
|
3207
|
+
var STYLE_KEYS2 = ["fillColor", "fillAlpha", "strokeColor", "strokeAlpha", "strokeWidth"];
|
|
2268
3208
|
var COLOR_KEYS = /* @__PURE__ */ new Set(["fillColor", "strokeColor"]);
|
|
2269
3209
|
var TRIM_KEYS = ["trimStart", "trimEnd"];
|
|
2270
3210
|
var GEOMETRY_KEYS_BY_SHAPE = {
|
|
@@ -2290,7 +3230,7 @@ var ShapeSequence = class extends Sequence {
|
|
|
2290
3230
|
// by `Container.onRender`.
|
|
2291
3231
|
_drawGeometry = () => {
|
|
2292
3232
|
};
|
|
2293
|
-
_liveKeys = new Set(
|
|
3233
|
+
_liveKeys = new Set(STYLE_KEYS2);
|
|
2294
3234
|
async build() {
|
|
2295
3235
|
const graphics = new Graphics({ label: this.spec.name });
|
|
2296
3236
|
graphics.cullable = true;
|
|
@@ -2316,7 +3256,15 @@ var ShapeSequence = class extends Sequence {
|
|
|
2316
3256
|
this._state.trimEnd ??= 1;
|
|
2317
3257
|
}
|
|
2318
3258
|
const gradientSpec = this.spec.fillGradient ?? this.spec.initial?.fillGradient;
|
|
2319
|
-
|
|
3259
|
+
validateGradientKeyframes(this.spec, describeLayer(this.spec));
|
|
3260
|
+
if (gradientSpec) {
|
|
3261
|
+
if (hasGradientKeys(this.spec.keyframes)) {
|
|
3262
|
+
this._grad = { grad: gradStateFrom(gradientSpec) };
|
|
3263
|
+
this._painter = new GradientPainter();
|
|
3264
|
+
this._gradDirty = true;
|
|
3265
|
+
this._liveKeys.add("fillGradient");
|
|
3266
|
+
} else this._state.fillStyle = makeGradientFill(gradientSpec);
|
|
3267
|
+
}
|
|
2320
3268
|
const morph = this._setupMorph();
|
|
2321
3269
|
this._drawGeometry = makeDrawGeometry(this.spec, scope, this._state, morph);
|
|
2322
3270
|
this._outline = makeOutline(this.spec, scope, this._state, morph);
|
|
@@ -2375,11 +3323,23 @@ var ShapeSequence = class extends Sequence {
|
|
|
2375
3323
|
this._state.morph ??= 0;
|
|
2376
3324
|
return { pairs, to: String(top.morphTo) };
|
|
2377
3325
|
}
|
|
3326
|
+
destroy() {
|
|
3327
|
+
super.destroy();
|
|
3328
|
+
this._painter?.destroy();
|
|
3329
|
+
this._painter = null;
|
|
3330
|
+
}
|
|
2378
3331
|
_fresh = false;
|
|
3332
|
+
_grad = null;
|
|
3333
|
+
_painter = null;
|
|
3334
|
+
_gradDirty = false;
|
|
2379
3335
|
_outline = null;
|
|
2380
3336
|
_redraw() {
|
|
2381
3337
|
const graphics = this.target;
|
|
2382
3338
|
const s = this._state;
|
|
3339
|
+
if (this._grad && this._gradDirty) {
|
|
3340
|
+
s.fillStyle = this._painter.paint(this._grad.grad);
|
|
3341
|
+
this._gradDirty = false;
|
|
3342
|
+
}
|
|
2383
3343
|
graphics.clear();
|
|
2384
3344
|
const trimmed = (s.trimStart ?? 0) > 0 || (s.trimEnd ?? 1) < 1;
|
|
2385
3345
|
if (!trimmed || !this._outline || !(s.strokeColor !== void 0 && s.strokeWidth > 0)) {
|
|
@@ -2425,6 +3385,9 @@ var ShapeSequence = class extends Sequence {
|
|
|
2425
3385
|
applyKeyframes(timeline, this.target, keyframes, this.duration, scope, [], startTime);
|
|
2426
3386
|
const colorSpace = this.spec.colorSpace ?? "rgb";
|
|
2427
3387
|
bindLiveKeyframes(timeline, this._state, this._liveKeys, this.spec.keyframes ?? [], this.duration, scope, startTime, colorSpace);
|
|
3388
|
+
if (this._grad) bindGradientKeyframes(timeline, this._grad, this.spec.keyframes ?? [], this.duration, startTime, colorSpace, () => {
|
|
3389
|
+
this._gradDirty = true;
|
|
3390
|
+
});
|
|
2428
3391
|
this.absoluteStart = startTime;
|
|
2429
3392
|
const endTime = startTime + this.duration;
|
|
2430
3393
|
this.target.renderable = startTime <= 0;
|
|
@@ -2461,7 +3424,7 @@ function seedStrokeStyle(state, spec, initial, who) {
|
|
|
2461
3424
|
}
|
|
2462
3425
|
}
|
|
2463
3426
|
function seedStyle(state, initial, scope) {
|
|
2464
|
-
for (const k of
|
|
3427
|
+
for (const k of STYLE_KEYS2) {
|
|
2465
3428
|
const v = initial[k];
|
|
2466
3429
|
if (v === void 0) continue;
|
|
2467
3430
|
state[k] = COLOR_KEYS.has(k) ? v : numOrZero(v, scope);
|
|
@@ -2684,13 +3647,14 @@ function makeOutline(spec, scope, s, morph) {
|
|
|
2684
3647
|
function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration, scope, offset, colorSpace) {
|
|
2685
3648
|
for (const kf of keyframes) {
|
|
2686
3649
|
const at = offset + resolveAt(kf.at, parentDuration);
|
|
2687
|
-
const duration = kf
|
|
3650
|
+
const duration = kfDuration(kf);
|
|
2688
3651
|
const ease = kf.ease ?? "none";
|
|
2689
3652
|
const loop = loopVars(kf);
|
|
2690
3653
|
if (kf.set) {
|
|
2691
3654
|
const live = pickLive(kf.set, liveKeys);
|
|
2692
3655
|
if (live) {
|
|
2693
3656
|
for (const [k, v] of Object.entries(live)) {
|
|
3657
|
+
if (k === "fillGradient") continue;
|
|
2694
3658
|
const resolved = resolveLiveValue(k, v, scope);
|
|
2695
3659
|
const live2 = state;
|
|
2696
3660
|
revertibleSet(timeline, at, () => live2[k], (v2) => {
|
|
@@ -2706,6 +3670,7 @@ function bindLiveKeyframes(timeline, state, liveKeys, keyframes, parentDuration,
|
|
|
2706
3670
|
...toLive ? Object.keys(toLive) : []
|
|
2707
3671
|
]);
|
|
2708
3672
|
for (const k of allKeys) {
|
|
3673
|
+
if (k === "fillGradient") continue;
|
|
2709
3674
|
const key = k;
|
|
2710
3675
|
const fromRaw = fromLive?.[key];
|
|
2711
3676
|
const toRaw = toLive?.[key];
|
|
@@ -2754,7 +3719,7 @@ function numOrZero(v, scope) {
|
|
|
2754
3719
|
var _CompositionSequence = null;
|
|
2755
3720
|
async function getCompositionSequence() {
|
|
2756
3721
|
if (!_CompositionSequence) {
|
|
2757
|
-
_CompositionSequence = (await import('./Composition-
|
|
3722
|
+
_CompositionSequence = (await import('./Composition-6PA4C4TV.js')).CompositionSequence;
|
|
2758
3723
|
}
|
|
2759
3724
|
return _CompositionSequence;
|
|
2760
3725
|
}
|
|
@@ -2792,6 +3757,6 @@ async function buildSequenceTree(specs, parent, root) {
|
|
|
2792
3757
|
return out;
|
|
2793
3758
|
}
|
|
2794
3759
|
|
|
2795
|
-
export { CameraSequence, DEFAULT_FOV, DEG, NEAR, NullSequence, Sequence, buildScope, buildSequenceTree, cameraBasis, checkEase, collectPropKeys, describeLayer, ensureFilterLibrary, evaluateExpr, flattenSvgPath, homeCamera, homeDistance, lintText, lintTiming, noise, projectLayer, rand, random, registerSequenceType, resolveAt, summarizeWarnings };
|
|
2796
|
-
//# sourceMappingURL=chunk-
|
|
2797
|
-
//# sourceMappingURL=chunk-
|
|
3760
|
+
export { CameraSequence, DEFAULT_FOV, DEG, NEAR, NullSequence, Sequence, bindClock, buildScope, buildSequenceTree, cameraBasis, checkEase, clockTable, collectPropKeys, contentLength, describeLayer, ensureFilterLibrary, evaluateExpr, flattenSvgPath, homeCamera, homeDistance, lintKeys, lintText, lintTiming, noise, projectLayer, rand, random, registerSequenceType, remapOf, resolveAt, summarizeWarnings, timeRemapOf };
|
|
3761
|
+
//# sourceMappingURL=chunk-DQHXVNOI.js.map
|
|
3762
|
+
//# sourceMappingURL=chunk-DQHXVNOI.js.map
|