reframe-video 0.5.0 → 0.6.1
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/dist/bin.js +103 -10
- package/dist/browserEntry.js +120 -16
- package/dist/cli.js +65 -9
- package/dist/index.js +178 -16
- package/dist/labels.js +65 -9
- package/dist/player.js +72 -0
- package/dist/renderer-canvas.js +34 -6
- package/dist/trace-cli.js +2 -2
- package/dist/types/camera.d.ts +32 -0
- package/dist/types/compile.d.ts +2 -0
- package/dist/types/dsl.d.ts +2 -1
- package/dist/types/evaluate.d.ts +9 -7
- package/dist/types/gradient.d.ts +28 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/ir.d.ts +59 -6
- package/dist/types/path.d.ts +6 -0
- package/guides/edsl-guide.md +51 -0
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scene camera — a single affine viewport applied to the whole scene.
|
|
3
|
+
*
|
|
4
|
+
* Semantics (look-at): `camera.{x,y}` is the scene point centered in frame,
|
|
5
|
+
* `zoom` scales about it, `rotation` (degrees) turns about it. Defaults
|
|
6
|
+
* (`x=W/2, y=H/2, zoom=1, rotation=0`) are the identity, so a scene without a
|
|
7
|
+
* camera renders byte-identically.
|
|
8
|
+
*
|
|
9
|
+
* The camera is animated through the SAME machinery as nodes: tween / motionPath /
|
|
10
|
+
* behaviors targeting the reserved id `"camera"` with props x/y/zoom/rotation.
|
|
11
|
+
* `cameraTo` is a thin readable wrapper. Because those are ordinary labeled
|
|
12
|
+
* timeline steps, camera keyframes are overlay-addressable for free, so human
|
|
13
|
+
* edits survive AI regeneration.
|
|
14
|
+
*/
|
|
15
|
+
import type { CameraIR, Ease, Size, TimelineIR } from "./ir.js";
|
|
16
|
+
import type { Mat2D } from "./evaluate.js";
|
|
17
|
+
/** Reserved timeline/behavior target id for the camera. */
|
|
18
|
+
export declare const CAMERA_ID = "camera";
|
|
19
|
+
/** The animatable camera props (look-at point + zoom + rotation). */
|
|
20
|
+
export declare const CAMERA_PROPS: readonly ["x", "y", "zoom", "rotation"];
|
|
21
|
+
/**
|
|
22
|
+
* The camera's affine matrix: `T(W/2,H/2) · R(rotation) · S(zoom) · T(-x,-y)`,
|
|
23
|
+
* i.e. center the focal point, then zoom/rotate about the frame centre. Defaults
|
|
24
|
+
* collapse to the identity.
|
|
25
|
+
*/
|
|
26
|
+
export declare function cameraMatrix(cam: CameraIR, size: Size): Mat2D;
|
|
27
|
+
/** Keyframe the camera: a `tween` on the reserved "camera" target. */
|
|
28
|
+
export declare function cameraTo(props: CameraIR, opts?: {
|
|
29
|
+
duration?: number;
|
|
30
|
+
ease?: Ease;
|
|
31
|
+
label?: string;
|
|
32
|
+
}): TimelineIR;
|
package/dist/types/compile.d.ts
CHANGED
|
@@ -49,5 +49,7 @@ export interface CompiledScene {
|
|
|
49
49
|
labelTimes: Map<string, LabelSpan>;
|
|
50
50
|
/** The subset of label spans that come from beat nodes — keyed by beat name. */
|
|
51
51
|
beatTimes: Map<string, LabelSpan>;
|
|
52
|
+
/** True iff the scene declares or animates a `camera` (gates the camera matrix). */
|
|
53
|
+
hasCamera: boolean;
|
|
52
54
|
}
|
|
53
55
|
export declare function compileScene(ir: SceneIR): CompiledScene;
|
package/dist/types/dsl.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The eDSL surface: thin factories that build plain IR objects.
|
|
3
3
|
* `scene()` validates and returns the IR — the return value is the document.
|
|
4
4
|
*/
|
|
5
|
-
import type { AudioIR, BehaviorIR, CompositionIR, CompositionSceneEntry, Ease, EllipseProps, GroupProps, ImageProps, LineProps, NodeIR, PathProps, PropValue, RectProps, SceneIR, Size, StateOverride, TextProps, TimelineIR } from "./ir.js";
|
|
5
|
+
import type { AudioIR, BehaviorIR, CameraIR, CompositionIR, CompositionSceneEntry, Ease, EllipseProps, GroupProps, ImageProps, LineProps, NodeIR, PathProps, PropValue, RectProps, SceneIR, Size, StateOverride, TextProps, TimelineIR } from "./ir.js";
|
|
6
6
|
export interface SceneInput {
|
|
7
7
|
id: string;
|
|
8
8
|
size: Size;
|
|
@@ -10,6 +10,7 @@ export interface SceneInput {
|
|
|
10
10
|
duration?: number;
|
|
11
11
|
background?: string;
|
|
12
12
|
nodes: NodeIR[];
|
|
13
|
+
camera?: CameraIR;
|
|
13
14
|
states?: Record<string, StateOverride>;
|
|
14
15
|
initial?: string;
|
|
15
16
|
timeline?: TimelineIR;
|
package/dist/types/evaluate.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* always. Renderers only draw; they never compute animation.
|
|
5
5
|
*/
|
|
6
6
|
import type { CompiledScene } from "./compile.js";
|
|
7
|
-
import type { ClipShape, PropValue } from "./ir.js";
|
|
7
|
+
import type { ClipShape, Paint, PropValue } from "./ir.js";
|
|
8
8
|
/** Canvas-style 2D affine matrix [a, b, c, d, e, f]. */
|
|
9
9
|
export type Mat2D = [number, number, number, number, number, number];
|
|
10
10
|
/** A clip from an ancestor group: its shape in the group's coordinate space,
|
|
@@ -31,8 +31,8 @@ export type DisplayOp = (OpBase & {
|
|
|
31
31
|
height: number;
|
|
32
32
|
offsetX: number;
|
|
33
33
|
offsetY: number;
|
|
34
|
-
fill?:
|
|
35
|
-
stroke?:
|
|
34
|
+
fill?: Paint;
|
|
35
|
+
stroke?: Paint;
|
|
36
36
|
strokeWidth?: number;
|
|
37
37
|
radius?: number;
|
|
38
38
|
}) | (OpBase & {
|
|
@@ -41,8 +41,8 @@ export type DisplayOp = (OpBase & {
|
|
|
41
41
|
height: number;
|
|
42
42
|
offsetX: number;
|
|
43
43
|
offsetY: number;
|
|
44
|
-
fill?:
|
|
45
|
-
stroke?:
|
|
44
|
+
fill?: Paint;
|
|
45
|
+
stroke?: Paint;
|
|
46
46
|
strokeWidth?: number;
|
|
47
47
|
}) | (OpBase & {
|
|
48
48
|
type: "line";
|
|
@@ -76,9 +76,11 @@ export type DisplayOp = (OpBase & {
|
|
|
76
76
|
d: string;
|
|
77
77
|
/** 0..1 fraction of the stroke outline drawn (draw-on). */
|
|
78
78
|
progress: number;
|
|
79
|
-
fill?:
|
|
80
|
-
stroke?:
|
|
79
|
+
fill?: Paint;
|
|
80
|
+
stroke?: Paint;
|
|
81
81
|
strokeWidth?: number;
|
|
82
|
+
/** Local-space bbox [x,y,w,h] for mapping a gradient paint (set only when one is used). */
|
|
83
|
+
bbox?: [number, number, number, number];
|
|
82
84
|
});
|
|
83
85
|
export type DisplayList = DisplayOp[];
|
|
84
86
|
/**
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gradient paints — the structured alternative to a solid color `fill`/`stroke`.
|
|
3
|
+
*
|
|
4
|
+
* Coordinates are normalized to the node's bounding box (0..1), so a gradient is
|
|
5
|
+
* just an angle + color stops, independent of the node's size. The renderer maps
|
|
6
|
+
* 0..1 → the node's local box and the paint is applied in node-local space, so
|
|
7
|
+
* rotating/scaling the NODE moves the gradient with it (the "animated gradient"
|
|
8
|
+
* idiom — gradients themselves are static this version). Pure + deterministic.
|
|
9
|
+
*/
|
|
10
|
+
import type { ColorStop, Gradient, Paint } from "./ir.js";
|
|
11
|
+
/** True when a paint is a gradient object (vs a plain color string). */
|
|
12
|
+
export declare function isGradient(p: Paint | undefined): p is Gradient;
|
|
13
|
+
/** A linear gradient. `angle` in degrees: 0 = left→right, 90 = top→bottom. */
|
|
14
|
+
export declare function linearGradient(stops: (string | ColorStop)[], opts?: {
|
|
15
|
+
angle?: number;
|
|
16
|
+
}): Gradient;
|
|
17
|
+
/** A radial gradient. `cx/cy` centre (0..1 of the box, default 0.5), `r` radius (0..1, default 0.5). */
|
|
18
|
+
export declare function radialGradient(stops: (string | ColorStop)[], opts?: {
|
|
19
|
+
cx?: number;
|
|
20
|
+
cy?: number;
|
|
21
|
+
r?: number;
|
|
22
|
+
}): Gradient;
|
|
23
|
+
/** A conic (angular sweep) gradient. `angle` start in degrees, `cx/cy` centre (0..1). */
|
|
24
|
+
export declare function conicGradient(stops: (string | ColorStop)[], opts?: {
|
|
25
|
+
angle?: number;
|
|
26
|
+
cx?: number;
|
|
27
|
+
cy?: number;
|
|
28
|
+
}): Gradient;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export { compileComposition, type CompiledComposition, type ScenePlacement, } fr
|
|
|
5
5
|
export { composeScene, formatComposeReport, type OverlayDoc, type ComposeReport, } from "./compose.js";
|
|
6
6
|
export { compileScene, type CompiledScene, type PropertySegment, type LabelSpan, type MotionDriver } from "./compile.js";
|
|
7
7
|
export { pathPoint, pathTangentAngle, type Pt } from "./path.js";
|
|
8
|
+
export { cameraTo, cameraMatrix, CAMERA_ID, CAMERA_PROPS } from "./camera.js";
|
|
9
|
+
export { linearGradient, radialGradient, conicGradient, isGradient } from "./gradient.js";
|
|
8
10
|
export { motionPreset, PRESET_NAMES, type PresetName, type PresetRig, type PresetOpts } from "./presets.js";
|
|
9
11
|
export { devicePreset, deviceScreen, deviceScreenCenter, deviceBounds, deviceScreenPoint, DEVICE_PRESET_NAMES, type DevicePresetName, type DevicePresetOpts } from "./devicePreset.js";
|
|
10
12
|
export { cursor, cursorTo, cursorPath, cursorClick, cursorDouble, type CursorStyle, type CursorOpts, type CursorToOpts, type CursorPathOpts, type CursorClickOpts } from "./cursor.js";
|
package/dist/types/ir.d.ts
CHANGED
|
@@ -31,20 +31,54 @@ export interface BaseProps {
|
|
|
31
31
|
skewX?: number;
|
|
32
32
|
skewY?: number;
|
|
33
33
|
anchor?: Anchor;
|
|
34
|
+
/**
|
|
35
|
+
* Pin a TOP-LEVEL node to the screen so the scene `camera` does not move it —
|
|
36
|
+
* for HUD / titles / watermark layers. No-op when the scene has no camera.
|
|
37
|
+
*/
|
|
38
|
+
fixed?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A paint is a solid color string OR a gradient. Coordinates are normalized to the
|
|
42
|
+
* node's bounding box (0..1, SVG `objectBoundingBox` style) so a gradient is just an
|
|
43
|
+
* angle + stops, size-independent. Applied in node-local space, so animating the
|
|
44
|
+
* node's transform (rotation/scale) moves the gradient with it. Build with
|
|
45
|
+
* `linearGradient`/`radialGradient`/`conicGradient` (`gradient.ts`).
|
|
46
|
+
*/
|
|
47
|
+
export interface ColorStop {
|
|
48
|
+
offset: number;
|
|
49
|
+
color: string;
|
|
34
50
|
}
|
|
51
|
+
export type Gradient = {
|
|
52
|
+
kind: "linear";
|
|
53
|
+
angle?: number;
|
|
54
|
+
stops: ColorStop[];
|
|
55
|
+
} | {
|
|
56
|
+
kind: "radial";
|
|
57
|
+
cx?: number;
|
|
58
|
+
cy?: number;
|
|
59
|
+
r?: number;
|
|
60
|
+
stops: ColorStop[];
|
|
61
|
+
} | {
|
|
62
|
+
kind: "conic";
|
|
63
|
+
angle?: number;
|
|
64
|
+
cx?: number;
|
|
65
|
+
cy?: number;
|
|
66
|
+
stops: ColorStop[];
|
|
67
|
+
};
|
|
68
|
+
export type Paint = string | Gradient;
|
|
35
69
|
export interface RectProps extends BaseProps {
|
|
36
70
|
width: number;
|
|
37
71
|
height: number;
|
|
38
|
-
fill?:
|
|
39
|
-
stroke?:
|
|
72
|
+
fill?: Paint;
|
|
73
|
+
stroke?: Paint;
|
|
40
74
|
strokeWidth?: number;
|
|
41
75
|
radius?: number;
|
|
42
76
|
}
|
|
43
77
|
export interface EllipseProps extends BaseProps {
|
|
44
78
|
width: number;
|
|
45
79
|
height: number;
|
|
46
|
-
fill?:
|
|
47
|
-
stroke?:
|
|
80
|
+
fill?: Paint;
|
|
81
|
+
stroke?: Paint;
|
|
48
82
|
strokeWidth?: number;
|
|
49
83
|
}
|
|
50
84
|
export interface LineProps {
|
|
@@ -57,12 +91,16 @@ export interface LineProps {
|
|
|
57
91
|
opacity?: number;
|
|
58
92
|
/** 0..1 — how much of the line is drawn (for draw-on effects). */
|
|
59
93
|
progress?: number;
|
|
94
|
+
/** Pin to the screen so the scene `camera` does not move it (top-level only). */
|
|
95
|
+
fixed?: boolean;
|
|
60
96
|
}
|
|
61
97
|
export interface TextProps extends BaseProps {
|
|
62
98
|
/** Numbers interpolate (count-up) and render via toFixed(contentDecimals). */
|
|
63
99
|
content: string | number;
|
|
64
100
|
/** Decimal places when content is numeric (default 0). */
|
|
65
101
|
contentDecimals?: number;
|
|
102
|
+
/** Group the integer part with thousands separators (e.g. 35,786). */
|
|
103
|
+
contentThousands?: boolean;
|
|
66
104
|
fontFamily: string;
|
|
67
105
|
fontSize: number;
|
|
68
106
|
fontWeight?: number;
|
|
@@ -95,8 +133,8 @@ export interface GroupProps extends BaseProps {
|
|
|
95
133
|
export interface PathProps extends BaseProps {
|
|
96
134
|
/** SVG path data (the `d` attribute). Drawn as a true vector — crisp at any zoom. */
|
|
97
135
|
d: string;
|
|
98
|
-
fill?:
|
|
99
|
-
stroke?:
|
|
136
|
+
fill?: Paint;
|
|
137
|
+
stroke?: Paint;
|
|
100
138
|
strokeWidth?: number;
|
|
101
139
|
/**
|
|
102
140
|
* 0..1 — fraction of the OUTLINE drawn, for a self-drawing "draw-on" effect
|
|
@@ -302,6 +340,19 @@ export interface AudioIR {
|
|
|
302
340
|
};
|
|
303
341
|
cues?: AudioCueIR[];
|
|
304
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* The scene camera: a viewport over the whole scene. `(x,y)` is the scene point
|
|
345
|
+
* centered in frame (defaults to the frame centre), `zoom` scales about it,
|
|
346
|
+
* `rotation` (degrees) turns about it. Defaults (`x=W/2, y=H/2, zoom=1, rotation=0`)
|
|
347
|
+
* are the identity. Animate it by tweening the reserved target `"camera"`
|
|
348
|
+
* (or the `cameraTo` helper); pin layers out of it with a node's `fixed` flag.
|
|
349
|
+
*/
|
|
350
|
+
export interface CameraIR {
|
|
351
|
+
x?: number;
|
|
352
|
+
y?: number;
|
|
353
|
+
zoom?: number;
|
|
354
|
+
rotation?: number;
|
|
355
|
+
}
|
|
305
356
|
export interface SceneIR {
|
|
306
357
|
version: 1;
|
|
307
358
|
id: string;
|
|
@@ -312,6 +363,8 @@ export interface SceneIR {
|
|
|
312
363
|
duration?: number;
|
|
313
364
|
background?: string;
|
|
314
365
|
nodes: NodeIR[];
|
|
366
|
+
/** A viewport over the scene, keyframable via the reserved target "camera". */
|
|
367
|
+
camera?: CameraIR;
|
|
315
368
|
states?: Record<string, StateOverride>;
|
|
316
369
|
/** State applied at t=0. */
|
|
317
370
|
initial?: string;
|
package/dist/types/path.d.ts
CHANGED
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
* spacing ever overshoots.
|
|
10
10
|
*/
|
|
11
11
|
export type Pt = [number, number];
|
|
12
|
+
/**
|
|
13
|
+
* A loose bounding box `[x, y, w, h]` from a path `d`'s coordinate extents — used
|
|
14
|
+
* only to map a gradient across the shape. Exact for M/L/C/Q/S/T paths (every
|
|
15
|
+
* number is an x/y coord, control points included); loose for the rare H/V/A.
|
|
16
|
+
*/
|
|
17
|
+
export declare function pathBBox(d: string): [number, number, number, number];
|
|
12
18
|
/**
|
|
13
19
|
* Position on the spline at progress u in [0,1]. `curviness` scales the
|
|
14
20
|
* Catmull-Rom tangents (GSAP's idea): 1 = standard smooth (the default and the
|
package/guides/edsl-guide.md
CHANGED
|
@@ -121,6 +121,57 @@ bound — e.g. a pulse only during the hold:
|
|
|
121
121
|
`oscillate("title", "scale", { amplitude: 0.04, frequency: 1.2 }, { from: 1.5, until: 3.5 })`.
|
|
122
122
|
Omit the window to run for the whole scene.
|
|
123
123
|
|
|
124
|
+
## Camera (one keyframable viewport)
|
|
125
|
+
|
|
126
|
+
A scene-level camera moves the whole scene at once: a look-at point + zoom +
|
|
127
|
+
rotation, animated over the timeline. Add it as a top-level `camera` field and
|
|
128
|
+
keyframe it with `cameraTo` (or by tweening the reserved target `"camera"`):
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
scene({
|
|
132
|
+
// ...
|
|
133
|
+
camera: { x: W/2, y: H/2, zoom: 1, rotation: 0 }, // (x,y) = scene point centred in frame; defaults = frame centre, zoom 1, rot 0 (= no camera)
|
|
134
|
+
timeline: seq(
|
|
135
|
+
cameraTo({ x: 300, y: 400, zoom: 4 }, { duration: 1.5, ease: "easeInOutCubic", label: "push-in" }), // zoom into a detail
|
|
136
|
+
cameraTo({ x: 800, y: 200, zoom: 2, rotation: -5 }, { duration: 1.2 }), // pan + slight bank
|
|
137
|
+
cameraTo({ x: W/2, y: H/2, zoom: 1, rotation: 0 }, { duration: 1.6 }), // pull back
|
|
138
|
+
),
|
|
139
|
+
})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- `cameraTo(props, { duration?, ease?, label? })` keyframes the camera; it is a
|
|
143
|
+
`tween` on the `"camera"` target, so `motionPath("camera", pts, …)` (pan along
|
|
144
|
+
a curve) and `oscillate/wiggle("camera", "rotation"|"x"|…)` (handheld drift)
|
|
145
|
+
also work.
|
|
146
|
+
- **Pin HUD/titles to the screen** with `fixed: true` on a TOP-LEVEL node — the
|
|
147
|
+
camera won't move it (for overlays, watermarks, captions).
|
|
148
|
+
- Defaults are the identity, so a scene without a camera is unchanged. Don't name
|
|
149
|
+
a node `"camera"` if you use the scene camera (the id can't be both).
|
|
150
|
+
|
|
151
|
+
See `examples/scenes/camera-demo.ts`.
|
|
152
|
+
|
|
153
|
+
## Gradients (fill / stroke)
|
|
154
|
+
|
|
155
|
+
`fill` and `stroke` on **rect / ellipse / path** accept a gradient as well as a
|
|
156
|
+
color string. Coordinates are normalized to the node's bounding box (0..1), so a
|
|
157
|
+
gradient is just an angle + stops:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
rect({ id: "card", x, y, width: 300, height: 300, anchor: "center",
|
|
161
|
+
fill: linearGradient(["#FF5C3A", "#FFC24B"], { angle: 60 }) }) // 0=L→R, 90=T→B
|
|
162
|
+
ellipse({ id: "orb", /* … */ fill: radialGradient(["#9B7CFF", "#221A4A"], { cx: 0.4, cy: 0.4, r: 0.6 }) })
|
|
163
|
+
path({ id: "star", d, fill: conicGradient(["#00C2A8", "#3AA0FF", "#7C5CFF", "#00C2A8"], { angle: -90 }) })
|
|
164
|
+
ellipse({ id: "ring", /* … */ fill: "none", stroke: linearGradient(["#3AA0FF", "#46E5A0"]), strokeWidth: 10 })
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
- `linearGradient(stops, { angle })`, `radialGradient(stops, { cx, cy, r })`,
|
|
168
|
+
`conicGradient(stops, { angle, cx, cy })`. `stops` is a color array (even offsets)
|
|
169
|
+
or `[{ offset, color }]`. `cx/cy/r` are 0..1 of the box (centre defaults to 0.5).
|
|
170
|
+
- **Gradients are static** (not keyframed). The gradient lives in the node's local
|
|
171
|
+
space, so **animate the NODE** (`tween(id, { rotation: 360 })`, scale, move) and the
|
|
172
|
+
gradient sweeps/stretches with it. Color-string fills still tween as today.
|
|
173
|
+
- text fill and line stroke are color-only for now. See `examples/scenes/gradient-demo.ts`.
|
|
174
|
+
|
|
124
175
|
## Character rig (skeleton, poses, IK)
|
|
125
176
|
|
|
126
177
|
A first-class, declarative character rig that **compiles to plain IR** (nested
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reframe-video",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Declarative motion graphics that AI can write and humans can tweak — human edits survive AI regeneration. Deterministic mp4 renders from a plain-data scene format.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"motion-graphics",
|