@maravilla-labs/frames 0.3.1 → 0.3.3
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/README.md +19 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -2
- package/dist/index.js.map +1 -1
- package/package.json +6 -7
- package/src/index.ts +69 -2
package/README.md
CHANGED
|
@@ -134,6 +134,25 @@ When you pass an explicit `duration`, the helper writes
|
|
|
134
134
|
(Animate.css collapses to 1 ms otherwise). You opted into this
|
|
135
135
|
timeline; the helper assumes you mean it.
|
|
136
136
|
|
|
137
|
+
### Pre-hiding the element
|
|
138
|
+
|
|
139
|
+
If you need the element invisible *before* its `at` time, use
|
|
140
|
+
`visibility: hidden` in your baseline CSS — not `opacity: 0`. The
|
|
141
|
+
helper flips `visibility: visible` automatically when the css
|
|
142
|
+
instruction fires.
|
|
143
|
+
|
|
144
|
+
```css
|
|
145
|
+
.badge { visibility: hidden; }
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Why not `opacity: 0`? Animate.css's "*In" keyframes (`bounceInDown`,
|
|
149
|
+
`fadeInUp`, etc.) define opacity at the start and middle of the
|
|
150
|
+
animation but **not at 100%**. CSS fills missing keyframe values with
|
|
151
|
+
the element's underlying cascade value — so a baseline of
|
|
152
|
+
`opacity: 0` makes the animation interpolate `1 → 0` at the tail end
|
|
153
|
+
and the element fades back out as soon as it lands. `visibility` is
|
|
154
|
+
not animated by the keyframes, so it stays cleanly out of the way.
|
|
155
|
+
|
|
137
156
|
## Video
|
|
138
157
|
|
|
139
158
|
Use `kind: "video"` to schedule a `<video>` element on the timeline.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,cAAc,GAAG;IAC3B,yDAAyD;IACzD,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,uDAAuD;IACvD,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,gCAAgC;IAChC,IAAI,EAAE,KAAK,CAAC;IACZ,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,cAAc,GAAG,UAAU,GAAG,aAAa,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,KAAK,EAAE,CAAA;CAAE,CAAC;AAEzE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,EAAE;YAAE,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;SAAE,CAAC;QACrD,yBAAyB,CAAC,EAAE,OAAO,CAAC;KACrC;CACF;AAaD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,cAAc,GAAG;IAC3B,yDAAyD;IACzD,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,wFAAwF;IACxF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,uDAAuD;IACvD,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,EAAE,EAAE,MAAM,CAAC;IACX,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,gCAAgC;IAChC,IAAI,EAAE,KAAK,CAAC;IACZ,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAC;IACX,mGAAmG;IACnG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG,cAAc,GAAG,UAAU,GAAG,aAAa,CAAC;AAChE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,KAAK,EAAE,CAAA;CAAE,CAAC;AAEzE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,EAAE;YAAE,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;SAAE,CAAC;QACrD,yBAAyB,CAAC,EAAE,OAAO,CAAC;KACrC;CACF;AAaD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAsI3D"}
|
package/dist/index.js
CHANGED
|
@@ -64,6 +64,7 @@ export function defineTimeline(schema) {
|
|
|
64
64
|
// timeline they're authoring.
|
|
65
65
|
const duration = i.duration ?? (natural > 0 ? natural : Math.max(0, schema.duration - i.at));
|
|
66
66
|
el.style.setProperty("animation-duration", duration + "ms", "important");
|
|
67
|
+
el.style.visibility = "visible";
|
|
67
68
|
return newAnims.map((anim) => {
|
|
68
69
|
anim.effect?.updateTiming({ duration });
|
|
69
70
|
anim.pause();
|
|
@@ -79,19 +80,28 @@ export function defineTimeline(schema) {
|
|
|
79
80
|
});
|
|
80
81
|
window.__mvFrames = {
|
|
81
82
|
async applyState(t) {
|
|
83
|
+
const videoWaits = [];
|
|
82
84
|
for (const s of slots) {
|
|
83
85
|
if (s.kind === "css-pending")
|
|
84
86
|
continue; // only relevant in browser preview
|
|
85
87
|
const local = clamp(t - s.at, 0, s.duration);
|
|
86
88
|
if (s.kind === "video") {
|
|
87
89
|
s.el.pause();
|
|
88
|
-
|
|
90
|
+
const target = (s.seek + local) / 1000;
|
|
91
|
+
// Seeking is async: the decoded frame for `target` is NOT ready the
|
|
92
|
+
// instant we assign `currentTime`. The renderer captures each frame
|
|
93
|
+
// with a synchronous CDP `beginFrame` right after `applyState`
|
|
94
|
+
// resolves, so we MUST wait for the seek to land and the frame to
|
|
95
|
+
// decode — otherwise we capture a stale/blank frame, or (under
|
|
96
|
+
// deterministic `--enable-begin-frame-control`) race the media
|
|
97
|
+
// pipeline and wedge/crash the headless tab.
|
|
98
|
+
videoWaits.push(seekVideo(s.el, target));
|
|
89
99
|
}
|
|
90
100
|
else {
|
|
91
101
|
s.anim.currentTime = local;
|
|
92
102
|
}
|
|
93
103
|
}
|
|
94
|
-
await document.fonts.ready;
|
|
104
|
+
await Promise.all([document.fonts.ready, ...videoWaits]);
|
|
95
105
|
},
|
|
96
106
|
};
|
|
97
107
|
if (!rendererPresent) {
|
|
@@ -111,6 +121,14 @@ export function defineTimeline(schema) {
|
|
|
111
121
|
if (s.duration !== null) {
|
|
112
122
|
s.el.style.setProperty("animation-duration", s.duration + "ms", "important");
|
|
113
123
|
}
|
|
124
|
+
// Flip visibility on so users can keep the element
|
|
125
|
+
// `visibility: hidden` in baseline CSS to hide it before its
|
|
126
|
+
// `at` time. Visibility isn't an animatable property in
|
|
127
|
+
// Animate.css keyframes, so it doesn't conflict with the
|
|
128
|
+
// animation the way `opacity: 0` would (a baseline opacity
|
|
129
|
+
// gets pulled into undefined keyframe slots and can make
|
|
130
|
+
// "*In" animations fade back out — surprising).
|
|
131
|
+
s.el.style.visibility = "visible";
|
|
114
132
|
s.el.classList.add(...s.classes);
|
|
115
133
|
}
|
|
116
134
|
else {
|
|
@@ -123,4 +141,51 @@ export function defineTimeline(schema) {
|
|
|
123
141
|
function clamp(v, lo, hi) {
|
|
124
142
|
return v < lo ? lo : v > hi ? hi : v;
|
|
125
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Seek `el` to `targetSec` and resolve only once the seeked frame has
|
|
146
|
+
* actually decoded — so a subsequent capture grabs the right frame instead
|
|
147
|
+
* of racing an in-flight seek.
|
|
148
|
+
*
|
|
149
|
+
* Resolution waits for the `seeked` event, then for one decoded video frame
|
|
150
|
+
* via `requestVideoFrameCallback` where available (fallback: a microtask, so
|
|
151
|
+
* we still yield to the decode). A timeout guards against `seeked` never
|
|
152
|
+
* firing (e.g. seeking to the exact current position, or an unsupported
|
|
153
|
+
* codec) so `applyState` can't hang the whole render. Already-satisfied
|
|
154
|
+
* seeks (`readyState` high enough and time already at target) resolve fast.
|
|
155
|
+
*/
|
|
156
|
+
function seekVideo(el, targetSec) {
|
|
157
|
+
const HAVE_CURRENT_DATA = 2;
|
|
158
|
+
// If we're already at (or within a frame of) the target with data ready,
|
|
159
|
+
// there's nothing to wait for — assigning currentTime won't fire `seeked`.
|
|
160
|
+
const alreadyThere = Math.abs(el.currentTime - targetSec) < 1e-3 && el.readyState >= HAVE_CURRENT_DATA;
|
|
161
|
+
el.currentTime = targetSec;
|
|
162
|
+
if (alreadyThere)
|
|
163
|
+
return Promise.resolve();
|
|
164
|
+
return new Promise((resolve) => {
|
|
165
|
+
let done = false;
|
|
166
|
+
const finish = () => {
|
|
167
|
+
if (done)
|
|
168
|
+
return;
|
|
169
|
+
done = true;
|
|
170
|
+
el.removeEventListener("seeked", onSeeked);
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
resolve();
|
|
173
|
+
};
|
|
174
|
+
const afterSeeked = () => {
|
|
175
|
+
// Wait for one decoded frame to be presented when the API exists.
|
|
176
|
+
const rvfc = el.requestVideoFrameCallback;
|
|
177
|
+
if (typeof rvfc === "function") {
|
|
178
|
+
rvfc.call(el, () => finish());
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// No rVFC: yield a microtask so the decode can settle, then finish.
|
|
182
|
+
Promise.resolve().then(finish);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const onSeeked = () => afterSeeked();
|
|
186
|
+
el.addEventListener("seeked", onSeeked, { once: true });
|
|
187
|
+
// Guard: never let a missing `seeked` stall the render.
|
|
188
|
+
const timer = setTimeout(finish, 2000);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
126
191
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maravilla-labs/frames",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Tiny vanilla helper for declaring Web Animations API timelines that the Maravilla runtime renderer drives frame-accurately.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,11 +18,6 @@
|
|
|
18
18
|
"README.md",
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
|
-
"scripts": {
|
|
22
|
-
"build": "tsc",
|
|
23
|
-
"typecheck": "tsc --noEmit",
|
|
24
|
-
"prepublishOnly": "tsc"
|
|
25
|
-
},
|
|
26
21
|
"keywords": [
|
|
27
22
|
"maravilla",
|
|
28
23
|
"frames",
|
|
@@ -37,5 +32,9 @@
|
|
|
37
32
|
},
|
|
38
33
|
"publishConfig": {
|
|
39
34
|
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"typecheck": "tsc --noEmit"
|
|
40
39
|
}
|
|
41
|
-
}
|
|
40
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -141,6 +141,7 @@ export function defineTimeline(schema: TimelineSchema): void {
|
|
|
141
141
|
// timeline they're authoring.
|
|
142
142
|
const duration = i.duration ?? (natural > 0 ? natural : Math.max(0, schema.duration - i.at));
|
|
143
143
|
el.style.setProperty("animation-duration", duration + "ms", "important");
|
|
144
|
+
el.style.visibility = "visible";
|
|
144
145
|
return newAnims.map<AnimSlot>((anim) => {
|
|
145
146
|
anim.effect?.updateTiming({ duration });
|
|
146
147
|
anim.pause();
|
|
@@ -158,17 +159,26 @@ export function defineTimeline(schema: TimelineSchema): void {
|
|
|
158
159
|
|
|
159
160
|
window.__mvFrames = {
|
|
160
161
|
async applyState(t: number) {
|
|
162
|
+
const videoWaits: Promise<void>[] = [];
|
|
161
163
|
for (const s of slots) {
|
|
162
164
|
if (s.kind === "css-pending") continue; // only relevant in browser preview
|
|
163
165
|
const local = clamp(t - s.at, 0, s.duration);
|
|
164
166
|
if (s.kind === "video") {
|
|
165
167
|
s.el.pause();
|
|
166
|
-
|
|
168
|
+
const target = (s.seek + local) / 1000;
|
|
169
|
+
// Seeking is async: the decoded frame for `target` is NOT ready the
|
|
170
|
+
// instant we assign `currentTime`. The renderer captures each frame
|
|
171
|
+
// with a synchronous CDP `beginFrame` right after `applyState`
|
|
172
|
+
// resolves, so we MUST wait for the seek to land and the frame to
|
|
173
|
+
// decode — otherwise we capture a stale/blank frame, or (under
|
|
174
|
+
// deterministic `--enable-begin-frame-control`) race the media
|
|
175
|
+
// pipeline and wedge/crash the headless tab.
|
|
176
|
+
videoWaits.push(seekVideo(s.el, target));
|
|
167
177
|
} else {
|
|
168
178
|
s.anim.currentTime = local;
|
|
169
179
|
}
|
|
170
180
|
}
|
|
171
|
-
await document.fonts.ready;
|
|
181
|
+
await Promise.all([document.fonts.ready, ...videoWaits]);
|
|
172
182
|
},
|
|
173
183
|
};
|
|
174
184
|
|
|
@@ -188,6 +198,14 @@ export function defineTimeline(schema: TimelineSchema): void {
|
|
|
188
198
|
if (s.duration !== null) {
|
|
189
199
|
s.el.style.setProperty("animation-duration", s.duration + "ms", "important");
|
|
190
200
|
}
|
|
201
|
+
// Flip visibility on so users can keep the element
|
|
202
|
+
// `visibility: hidden` in baseline CSS to hide it before its
|
|
203
|
+
// `at` time. Visibility isn't an animatable property in
|
|
204
|
+
// Animate.css keyframes, so it doesn't conflict with the
|
|
205
|
+
// animation the way `opacity: 0` would (a baseline opacity
|
|
206
|
+
// gets pulled into undefined keyframe slots and can make
|
|
207
|
+
// "*In" animations fade back out — surprising).
|
|
208
|
+
s.el.style.visibility = "visible";
|
|
191
209
|
s.el.classList.add(...s.classes);
|
|
192
210
|
} else {
|
|
193
211
|
s.anim.play();
|
|
@@ -200,3 +218,52 @@ export function defineTimeline(schema: TimelineSchema): void {
|
|
|
200
218
|
function clamp(v: number, lo: number, hi: number): number {
|
|
201
219
|
return v < lo ? lo : v > hi ? hi : v;
|
|
202
220
|
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Seek `el` to `targetSec` and resolve only once the seeked frame has
|
|
224
|
+
* actually decoded — so a subsequent capture grabs the right frame instead
|
|
225
|
+
* of racing an in-flight seek.
|
|
226
|
+
*
|
|
227
|
+
* Resolution waits for the `seeked` event, then for one decoded video frame
|
|
228
|
+
* via `requestVideoFrameCallback` where available (fallback: a microtask, so
|
|
229
|
+
* we still yield to the decode). A timeout guards against `seeked` never
|
|
230
|
+
* firing (e.g. seeking to the exact current position, or an unsupported
|
|
231
|
+
* codec) so `applyState` can't hang the whole render. Already-satisfied
|
|
232
|
+
* seeks (`readyState` high enough and time already at target) resolve fast.
|
|
233
|
+
*/
|
|
234
|
+
function seekVideo(el: HTMLVideoElement, targetSec: number): Promise<void> {
|
|
235
|
+
const HAVE_CURRENT_DATA = 2;
|
|
236
|
+
// If we're already at (or within a frame of) the target with data ready,
|
|
237
|
+
// there's nothing to wait for — assigning currentTime won't fire `seeked`.
|
|
238
|
+
const alreadyThere =
|
|
239
|
+
Math.abs(el.currentTime - targetSec) < 1e-3 && el.readyState >= HAVE_CURRENT_DATA;
|
|
240
|
+
el.currentTime = targetSec;
|
|
241
|
+
if (alreadyThere) return Promise.resolve();
|
|
242
|
+
|
|
243
|
+
return new Promise<void>((resolve) => {
|
|
244
|
+
let done = false;
|
|
245
|
+
const finish = () => {
|
|
246
|
+
if (done) return;
|
|
247
|
+
done = true;
|
|
248
|
+
el.removeEventListener("seeked", onSeeked);
|
|
249
|
+
clearTimeout(timer);
|
|
250
|
+
resolve();
|
|
251
|
+
};
|
|
252
|
+
const afterSeeked = () => {
|
|
253
|
+
// Wait for one decoded frame to be presented when the API exists.
|
|
254
|
+
const rvfc = (el as unknown as {
|
|
255
|
+
requestVideoFrameCallback?: (cb: () => void) => number;
|
|
256
|
+
}).requestVideoFrameCallback;
|
|
257
|
+
if (typeof rvfc === "function") {
|
|
258
|
+
rvfc.call(el, () => finish());
|
|
259
|
+
} else {
|
|
260
|
+
// No rVFC: yield a microtask so the decode can settle, then finish.
|
|
261
|
+
Promise.resolve().then(finish);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
const onSeeked = () => afterSeeked();
|
|
265
|
+
el.addEventListener("seeked", onSeeked, { once: true });
|
|
266
|
+
// Guard: never let a missing `seeked` stall the render.
|
|
267
|
+
const timer = setTimeout(finish, 2000);
|
|
268
|
+
});
|
|
269
|
+
}
|