reframe-video 0.6.11 → 0.6.12
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 +17 -9
- package/dist/browserEntry.js +4 -2
- package/dist/cli.js +1 -1
- package/dist/index.js +39 -2
- package/dist/labels.js +1 -1
- package/dist/trace-cli.js +2 -2
- package/dist/types/index.d.ts +1 -0
- package/dist/types/ir.d.ts +5 -0
- package/dist/types/layout.d.ts +42 -0
- package/guides/edsl-guide.md +28 -3
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -633,7 +633,7 @@ var init_validate = __esm({
|
|
|
633
633
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
634
634
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
635
635
|
line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
|
|
636
|
-
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
636
|
+
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "prefix", "suffix", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
637
637
|
image: [...COMMON_PROPS, "src", "width", "height", "fit"],
|
|
638
638
|
video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart", "volume"],
|
|
639
639
|
path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
|
|
@@ -968,6 +968,13 @@ var init_effects = __esm({
|
|
|
968
968
|
}
|
|
969
969
|
});
|
|
970
970
|
|
|
971
|
+
// ../core/src/layout.ts
|
|
972
|
+
var init_layout = __esm({
|
|
973
|
+
"../core/src/layout.ts"() {
|
|
974
|
+
"use strict";
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
|
|
971
978
|
// ../core/src/montage.ts
|
|
972
979
|
var init_montage = __esm({
|
|
973
980
|
"../core/src/montage.ts"() {
|
|
@@ -1467,6 +1474,7 @@ var init_src = __esm({
|
|
|
1467
1474
|
init_camera();
|
|
1468
1475
|
init_gradient();
|
|
1469
1476
|
init_effects();
|
|
1477
|
+
init_layout();
|
|
1470
1478
|
init_montage();
|
|
1471
1479
|
init_presets();
|
|
1472
1480
|
init_devicePreset();
|
|
@@ -2467,9 +2475,9 @@ __export(batch_exports, {
|
|
|
2467
2475
|
import { mkdir as mkdir3, mkdtemp as mkdtemp4, readFile as readFile5, rm as rm4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2468
2476
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
2469
2477
|
import { join as join8, dirname as dirname5 } from "node:path";
|
|
2470
|
-
function overlayFromFlat(
|
|
2478
|
+
function overlayFromFlat(row2, name) {
|
|
2471
2479
|
const doc = { reframeOverlay: 1, name };
|
|
2472
|
-
for (const [key2, raw] of Object.entries(
|
|
2480
|
+
for (const [key2, raw] of Object.entries(row2)) {
|
|
2473
2481
|
if (key2.startsWith("_")) continue;
|
|
2474
2482
|
if (raw === null || raw === void 0 || raw === "") continue;
|
|
2475
2483
|
const value = raw;
|
|
@@ -2530,13 +2538,13 @@ function parseCsv(text2) {
|
|
|
2530
2538
|
const headers = split(lines[0]).map((h) => h.trim());
|
|
2531
2539
|
return lines.slice(1).map((line) => {
|
|
2532
2540
|
const cells = split(line);
|
|
2533
|
-
const
|
|
2541
|
+
const row2 = {};
|
|
2534
2542
|
headers.forEach((h, i) => {
|
|
2535
2543
|
const cell = (cells[i] ?? "").trim();
|
|
2536
2544
|
const asNumber = Number(cell);
|
|
2537
|
-
|
|
2545
|
+
row2[h] = cell !== "" && !Number.isNaN(asNumber) ? asNumber : cell;
|
|
2538
2546
|
});
|
|
2539
|
-
return
|
|
2547
|
+
return row2;
|
|
2540
2548
|
});
|
|
2541
2549
|
}
|
|
2542
2550
|
async function loadRows(path2) {
|
|
@@ -2554,11 +2562,11 @@ async function runBatch(scene2, rows, opts) {
|
|
|
2554
2562
|
for (; ; ) {
|
|
2555
2563
|
const index = next++;
|
|
2556
2564
|
if (index >= rows.length) return;
|
|
2557
|
-
const
|
|
2558
|
-
const name = sanitize(String(
|
|
2565
|
+
const row2 = rows[index];
|
|
2566
|
+
const name = sanitize(String(row2._name ?? `row-${index}`));
|
|
2559
2567
|
let result;
|
|
2560
2568
|
try {
|
|
2561
|
-
const rowOverlay = overlayFromFlat(
|
|
2569
|
+
const rowOverlay = overlayFromFlat(row2, name);
|
|
2562
2570
|
const { ir, report } = composeScene(scene2, ...opts.baseOverlays, rowOverlay);
|
|
2563
2571
|
const framesDir = await mkdtemp4(join8(tmpdir5(), `reframe-batch-${index}-`));
|
|
2564
2572
|
const output = join8(opts.outDir, `${name}.mp4`);
|
package/dist/browserEntry.js
CHANGED
|
@@ -343,7 +343,7 @@
|
|
|
343
343
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
344
344
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
345
345
|
line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
|
|
346
|
-
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
346
|
+
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "prefix", "suffix", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
347
347
|
image: [...COMMON_PROPS, "src", "width", "height", "fit"],
|
|
348
348
|
video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart", "volume"],
|
|
349
349
|
path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
|
|
@@ -903,12 +903,14 @@
|
|
|
903
903
|
0,
|
|
904
904
|
Math.round(num(id, "contentDecimals", node.props.contentDecimals ?? 0))
|
|
905
905
|
);
|
|
906
|
+
const body = typeof raw === "number" ? formatNumber(raw, decimals, node.props.contentThousands === true) : raw;
|
|
906
907
|
ops.push({
|
|
907
908
|
type: "text",
|
|
908
909
|
id,
|
|
909
910
|
transform: projDraw(matrix, 0, 0),
|
|
910
911
|
opacity,
|
|
911
|
-
|
|
912
|
+
// static affixes wrap the (possibly counting-up) body; absent ⇒ body unchanged
|
|
913
|
+
content: (node.props.prefix ?? "") + body + (node.props.suffix ?? ""),
|
|
912
914
|
fontFamily: str(id, "fontFamily", node.props.fontFamily),
|
|
913
915
|
fontSize: num(id, "fontSize", node.props.fontSize),
|
|
914
916
|
fontWeight: num(id, "fontWeight", node.props.fontWeight ?? 400),
|
package/dist/cli.js
CHANGED
|
@@ -348,7 +348,7 @@ var PROPS_BY_TYPE = {
|
|
|
348
348
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
349
349
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
350
350
|
line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
|
|
351
|
-
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
351
|
+
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "prefix", "suffix", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
352
352
|
image: [...COMMON_PROPS, "src", "width", "height", "fit"],
|
|
353
353
|
video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart", "volume"],
|
|
354
354
|
path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
|
package/dist/index.js
CHANGED
|
@@ -358,7 +358,7 @@ var PROPS_BY_TYPE = {
|
|
|
358
358
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
359
359
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
360
360
|
line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
|
|
361
|
-
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
361
|
+
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "prefix", "suffix", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
362
362
|
image: [...COMMON_PROPS, "src", "width", "height", "fit"],
|
|
363
363
|
video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart", "volume"],
|
|
364
364
|
path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
|
|
@@ -1032,6 +1032,38 @@ function dropShadow(color, blur = 24, x = 0, y = 12) {
|
|
|
1032
1032
|
return { shadowColor: color, shadowBlur: blur, shadowX: x, shadowY: y };
|
|
1033
1033
|
}
|
|
1034
1034
|
|
|
1035
|
+
// ../core/src/layout.ts
|
|
1036
|
+
function row(count, opts = {}) {
|
|
1037
|
+
if (count <= 0) return [];
|
|
1038
|
+
const center = opts.center ?? 0;
|
|
1039
|
+
if (count === 1) return [center];
|
|
1040
|
+
if (opts.span !== void 0) {
|
|
1041
|
+
const start2 = center - opts.span / 2;
|
|
1042
|
+
const pitch2 = opts.span / (count - 1);
|
|
1043
|
+
return Array.from({ length: count }, (_, i) => start2 + i * pitch2);
|
|
1044
|
+
}
|
|
1045
|
+
const iw = opts.itemWidth ?? 0;
|
|
1046
|
+
const gap = opts.gap ?? 0;
|
|
1047
|
+
const pitch = iw + gap;
|
|
1048
|
+
const total = count * iw + (count - 1) * gap;
|
|
1049
|
+
const start = center - total / 2 + iw / 2;
|
|
1050
|
+
return Array.from({ length: count }, (_, i) => start + i * pitch);
|
|
1051
|
+
}
|
|
1052
|
+
var column = row;
|
|
1053
|
+
function grid(rows, cols, opts = {}) {
|
|
1054
|
+
const axis = (center, gap, item, span) => ({
|
|
1055
|
+
center,
|
|
1056
|
+
...gap !== void 0 ? { gap } : {},
|
|
1057
|
+
...item !== void 0 ? { itemWidth: item } : {},
|
|
1058
|
+
...span !== void 0 ? { span } : {}
|
|
1059
|
+
});
|
|
1060
|
+
const xs = row(cols, axis(opts.center?.x ?? 0, opts.gapX, opts.cellW, opts.spanX));
|
|
1061
|
+
const ys = row(rows, axis(opts.center?.y ?? 0, opts.gapY, opts.cellH, opts.spanY));
|
|
1062
|
+
const out = [];
|
|
1063
|
+
for (let r = 0; r < rows; r++) for (let c = 0; c < cols; c++) out.push({ x: xs[c], y: ys[r] });
|
|
1064
|
+
return out;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1035
1067
|
// ../core/src/montage.ts
|
|
1036
1068
|
var VIDEO_EXT = /\.(mp4|mov|webm|m4v|mkv)$/i;
|
|
1037
1069
|
var isVideoSrc = (src) => VIDEO_EXT.test(src);
|
|
@@ -3345,12 +3377,14 @@ function evaluate(compiled, t) {
|
|
|
3345
3377
|
0,
|
|
3346
3378
|
Math.round(num(id, "contentDecimals", node.props.contentDecimals ?? 0))
|
|
3347
3379
|
);
|
|
3380
|
+
const body = typeof raw === "number" ? formatNumber(raw, decimals, node.props.contentThousands === true) : raw;
|
|
3348
3381
|
ops.push({
|
|
3349
3382
|
type: "text",
|
|
3350
3383
|
id,
|
|
3351
3384
|
transform: projDraw(matrix, 0, 0),
|
|
3352
3385
|
opacity,
|
|
3353
|
-
|
|
3386
|
+
// static affixes wrap the (possibly counting-up) body; absent ⇒ body unchanged
|
|
3387
|
+
content: (node.props.prefix ?? "") + body + (node.props.suffix ?? ""),
|
|
3354
3388
|
fontFamily: str(id, "fontFamily", node.props.fontFamily),
|
|
3355
3389
|
fontSize: num(id, "fontSize", node.props.fontSize),
|
|
3356
3390
|
fontWeight: num(id, "fontWeight", node.props.fontWeight ?? 400),
|
|
@@ -3486,6 +3520,7 @@ export {
|
|
|
3486
3520
|
characterPreset,
|
|
3487
3521
|
collectImageSrcs,
|
|
3488
3522
|
collectVideoSrcs,
|
|
3523
|
+
column,
|
|
3489
3524
|
compileComposition,
|
|
3490
3525
|
compileScene,
|
|
3491
3526
|
composeScene,
|
|
@@ -3507,6 +3542,7 @@ export {
|
|
|
3507
3542
|
figure,
|
|
3508
3543
|
formatComposeReport,
|
|
3509
3544
|
glow,
|
|
3545
|
+
grid,
|
|
3510
3546
|
group,
|
|
3511
3547
|
humanoid,
|
|
3512
3548
|
ikReach,
|
|
@@ -3536,6 +3572,7 @@ export {
|
|
|
3536
3572
|
resolveEase,
|
|
3537
3573
|
rig,
|
|
3538
3574
|
rigPose,
|
|
3575
|
+
row,
|
|
3539
3576
|
sampleBehavior,
|
|
3540
3577
|
sampleProp,
|
|
3541
3578
|
scene,
|
package/dist/labels.js
CHANGED
|
@@ -342,7 +342,7 @@ var PROPS_BY_TYPE = {
|
|
|
342
342
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
343
343
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
344
344
|
line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
|
|
345
|
-
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
345
|
+
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "prefix", "suffix", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
346
346
|
image: [...COMMON_PROPS, "src", "width", "height", "fit"],
|
|
347
347
|
video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart", "volume"],
|
|
348
348
|
path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
|
package/dist/trace-cli.js
CHANGED
|
@@ -12,7 +12,7 @@ var PROPS_BY_TYPE = {
|
|
|
12
12
|
rect: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth", "radius"],
|
|
13
13
|
ellipse: [...COMMON_PROPS, "width", "height", "fill", "stroke", "strokeWidth"],
|
|
14
14
|
line: ["x1", "y1", "x2", "y2", "stroke", "strokeWidth", "opacity", "progress", ...FX_PROPS],
|
|
15
|
-
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
15
|
+
text: [...COMMON_PROPS, "content", "contentDecimals", "contentThousands", "prefix", "suffix", "fontFamily", "fontSize", "fontWeight", "fill", "letterSpacing"],
|
|
16
16
|
image: [...COMMON_PROPS, "src", "width", "height", "fit"],
|
|
17
17
|
video: [...COMMON_PROPS, "src", "width", "height", "fit", "start", "rate", "clipStart", "volume"],
|
|
18
18
|
path: [...COMMON_PROPS, "d", "fill", "stroke", "strokeWidth", "progress", "originX", "originY"],
|
|
@@ -525,7 +525,7 @@ var INPLACE_RATIO = 0.3;
|
|
|
525
525
|
var mean2 = (xs) => xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : 0;
|
|
526
526
|
function backgroundLevel(diff) {
|
|
527
527
|
const flat = [];
|
|
528
|
-
for (const
|
|
528
|
+
for (const row2 of diff) for (const v of row2) flat.push(v);
|
|
529
529
|
if (flat.length === 0) return 0;
|
|
530
530
|
flat.sort((a, b) => a - b);
|
|
531
531
|
return flat[Math.floor(flat.length / 2)];
|
package/dist/types/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export { pathPoint, pathTangentAngle, type Pt } from "./path.js";
|
|
|
8
8
|
export { cameraTo, cameraMatrix, CAMERA_ID, CAMERA_PROPS } from "./camera.js";
|
|
9
9
|
export { linearGradient, radialGradient, conicGradient, isGradient } from "./gradient.js";
|
|
10
10
|
export { glow, dropShadow } from "./effects.js";
|
|
11
|
+
export { row, column, grid, type RowOpts, type GridOpts } from "./layout.js";
|
|
11
12
|
export { photoMontage, videoMontage, type MontageImage, type MontageOpts, type MontageResult, type KenBurns } from "./montage.js";
|
|
12
13
|
export { motionPreset, PRESET_NAMES, type PresetName, type PresetRig, type PresetOpts } from "./presets.js";
|
|
13
14
|
export { devicePreset, deviceScreen, deviceScreenCenter, deviceBounds, deviceScreenPoint, DEVICE_PRESET_NAMES, type DevicePresetName, type DevicePresetOpts } from "./devicePreset.js";
|
package/dist/types/ir.d.ts
CHANGED
|
@@ -139,6 +139,11 @@ export interface TextProps extends BaseProps {
|
|
|
139
139
|
contentDecimals?: number;
|
|
140
140
|
/** Group the integer part with thousands separators (e.g. 35,786). */
|
|
141
141
|
contentThousands?: boolean;
|
|
142
|
+
/** Static affixes wrapped around the rendered content — so a count-up can read
|
|
143
|
+
* "$2.4M" or "+32%" from ONE node (prefix `"$"`, suffix `"M"`) instead of three
|
|
144
|
+
* hand-positioned ones. Absent ⇒ no change. */
|
|
145
|
+
prefix?: string;
|
|
146
|
+
suffix?: string;
|
|
142
147
|
fontFamily: string;
|
|
143
148
|
fontSize: number;
|
|
144
149
|
fontWeight?: number;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout helpers — pure coordinate math for the common "evenly space N things"
|
|
3
|
+
* jobs (a row of cards, a grid of tiles) so authors don't hand-roll a `cx(i)`
|
|
4
|
+
* every time. They return coordinates you spread into node `x`/`y`; no nodes,
|
|
5
|
+
* no renderer involvement — authoring sugar only. Deterministic.
|
|
6
|
+
*
|
|
7
|
+
* const xs = row(3, { center: 960, gap: 60, itemWidth: 440 });
|
|
8
|
+
* xs.map((x, i) => rect({ id: `card-${i}`, x, y: 540, ... }));
|
|
9
|
+
*/
|
|
10
|
+
export interface RowOpts {
|
|
11
|
+
/** Centre of the row/column (default 0). */
|
|
12
|
+
center?: number;
|
|
13
|
+
/** Gap between adjacent items, paired with `itemWidth` (packed layout). */
|
|
14
|
+
gap?: number;
|
|
15
|
+
/** Item extent along the axis, paired with `gap` (packed layout). */
|
|
16
|
+
itemWidth?: number;
|
|
17
|
+
/** Alternative to gap+itemWidth: spread the item CENTRES evenly across this span. */
|
|
18
|
+
span?: number;
|
|
19
|
+
}
|
|
20
|
+
/** N evenly-spaced positions along one axis, centred on `center`. Give either
|
|
21
|
+
* `span` (spread centres across it) or `gap`+`itemWidth` (pack fixed-width items). */
|
|
22
|
+
export declare function row(count: number, opts?: RowOpts): number[];
|
|
23
|
+
/** N evenly-spaced positions along the vertical axis — `row` for the y axis. */
|
|
24
|
+
export declare const column: typeof row;
|
|
25
|
+
export interface GridOpts {
|
|
26
|
+
center?: {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
};
|
|
30
|
+
gapX?: number;
|
|
31
|
+
gapY?: number;
|
|
32
|
+
cellW?: number;
|
|
33
|
+
cellH?: number;
|
|
34
|
+
/** Alternatives to gap+cell: spread cell centres across these spans. */
|
|
35
|
+
spanX?: number;
|
|
36
|
+
spanY?: number;
|
|
37
|
+
}
|
|
38
|
+
/** `rows × cols` cell centres in row-major order, centred on `center`. */
|
|
39
|
+
export declare function grid(rows: number, cols: number, opts?: GridOpts): {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
}[];
|
package/guides/edsl-guide.md
CHANGED
|
@@ -4,6 +4,10 @@ You write a motion-graphics scene as **declarative data** using the reframe
|
|
|
4
4
|
TypeScript eDSL. Your output is a single `.ts` file that default-exports a
|
|
5
5
|
`scene({...})` call. Everything imports from `@reframe/core`.
|
|
6
6
|
|
|
7
|
+
> `See examples/scenes/…` pointers below refer to the GitHub repo
|
|
8
|
+
> (github.com/kiyeonjeon21/reframe), not the installed npm package — this guide is
|
|
9
|
+
> self-contained; you don't need them to write a scene.
|
|
10
|
+
|
|
7
11
|
```ts
|
|
8
12
|
import { scene, group, rect, ellipse, line, text,
|
|
9
13
|
seq, par, stagger, to, tween, wait,
|
|
@@ -30,10 +34,13 @@ Factories return plain data. Every node needs a unique `id`.
|
|
|
30
34
|
- `ellipse({ id, x, y, width, height, fill?, stroke?, strokeWidth?, ... })`
|
|
31
35
|
- `line({ id, x1, y1, x2, y2, stroke, strokeWidth?, opacity?, progress? })` —
|
|
32
36
|
`progress` 0..1 draws the line on (1 = full line).
|
|
33
|
-
- `text({ id, x, y, content, contentDecimals?, fontFamily, fontSize, fontWeight?, fill?, letterSpacing?, ... })` —
|
|
37
|
+
- `text({ id, x, y, content, contentDecimals?, contentThousands?, prefix?, suffix?, fontFamily, fontSize, fontWeight?, fill?, letterSpacing?, ... })` —
|
|
34
38
|
`content` may be a number; numeric content interpolates (count-up) and renders
|
|
35
39
|
via `toFixed(contentDecimals ?? 0)`. For a "8.2"-style label, set
|
|
36
|
-
`contentDecimals: 1
|
|
40
|
+
`contentDecimals: 1`; `contentThousands: true` groups the integer (35,786).
|
|
41
|
+
**`prefix`/`suffix`** wrap the value so a count-up reads `$2.4M` or `+32%` from
|
|
42
|
+
ONE node (`{ content: 2.4, contentDecimals: 1, prefix: "$", suffix: "M" }`) —
|
|
43
|
+
don't hand-position separate `$`/`%` nodes.
|
|
37
44
|
- `path({ id, d, x, y, fill?, stroke?, strokeWidth?, progress?, originX?, originY?, opacity?, rotation?, scale?, anchor? })` —
|
|
38
45
|
a true vector shape from an SVG path `d` string (crisp at any zoom; recolour by
|
|
39
46
|
animating `fill`/`stroke`). `progress` 0..1 draws the stroke OUTLINE on (animate
|
|
@@ -63,6 +70,23 @@ Factories return plain data. Every node needs a unique `id`.
|
|
|
63
70
|
Example: a bar that grows upward = `anchor: "bottom-left"` + animate `height`.
|
|
64
71
|
Font: use `fontFamily: "Inter"` (weights 400/700/800 are available).
|
|
65
72
|
|
|
73
|
+
### Layout helpers (evenly spacing things)
|
|
74
|
+
|
|
75
|
+
Positions are absolute pixels. For a row of cards or a grid of tiles, use the
|
|
76
|
+
layout helpers instead of hand-rolling the column math — they return coordinates
|
|
77
|
+
you spread into `x`/`y`:
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { row, grid } from "@reframe/core";
|
|
81
|
+
// 3 cards, 440px wide, 60px apart, centred on the frame:
|
|
82
|
+
row(3, { center: 960, gap: 60, itemWidth: 440 }).map((x, i) =>
|
|
83
|
+
rect({ id: `card-${i}`, x, y: 540, width: 440, height: 300, anchor: "center", fill: "#1A1F2E" }));
|
|
84
|
+
// or spread centres across a span: row(3, { center: 960, span: 900 })
|
|
85
|
+
// grid(rows, cols, { center: {x,y}, gapX, gapY, cellW, cellH }) → { x, y }[] (row-major)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`column` is `row` for the y axis.
|
|
89
|
+
|
|
66
90
|
## States: declare looks, not motion
|
|
67
91
|
|
|
68
92
|
Base props on nodes describe the **finished design**. A state is a sparse
|
|
@@ -98,7 +122,8 @@ them with normal TS (`Object.fromEntries`, `.map`) for data-driven scenes.
|
|
|
98
122
|
later `tween` can chain from there. Use it for swoops/arcs/orbits — straight
|
|
99
123
|
`tween`s on x and y can't curve. `closed: true` loops the waypoints (orbit).
|
|
100
124
|
`curviness` shapes the path: `1` smooth (default), `0` sharp corners, `>1` loopier.
|
|
101
|
-
- `wait(seconds)` — hold
|
|
125
|
+
- `wait(seconds, label?)` — hold; the optional `label` names the hold so audio
|
|
126
|
+
cues and overlay retiming can address it.
|
|
102
127
|
|
|
103
128
|
Eases: `linear`, `easeIn/Out/InOutQuad`, `easeIn/Out/InOutCubic`,
|
|
104
129
|
`easeIn/Out/InOutQuart`, `easeIn/Out/InOutExpo`, or `{ cubicBezier: [x1,y1,x2,y2] }`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reframe-video",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.12",
|
|
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",
|