frameyard 0.3.0 → 0.5.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 +117 -0
- package/GLOSSARY.md +46 -2
- package/README.md +174 -106
- package/dist/bindings.d.ts +42 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +144 -0
- package/dist/bindings.js.map +1 -0
- package/dist/cli.js +32 -21
- package/dist/cli.js.map +1 -1
- package/dist/composition/adapter.d.ts +30 -0
- package/dist/composition/adapter.d.ts.map +1 -0
- package/dist/composition/adapter.js +2 -0
- package/dist/composition/adapter.js.map +1 -0
- package/dist/composition/animation.d.ts +19 -0
- package/dist/composition/animation.d.ts.map +1 -0
- package/dist/composition/animation.js +55 -0
- package/dist/composition/animation.js.map +1 -0
- package/dist/composition/attributes.d.ts +7 -0
- package/dist/composition/attributes.d.ts.map +1 -0
- package/dist/composition/attributes.js +24 -0
- package/dist/composition/attributes.js.map +1 -0
- package/dist/composition/descriptor.d.ts +30 -0
- package/dist/composition/descriptor.d.ts.map +1 -0
- package/dist/composition/descriptor.js +188 -0
- package/dist/composition/descriptor.js.map +1 -0
- package/dist/composition/diagnostics.d.ts +12 -0
- package/dist/composition/diagnostics.d.ts.map +1 -0
- package/dist/composition/diagnostics.js +38 -0
- package/dist/composition/diagnostics.js.map +1 -0
- package/dist/composition/index.d.ts +12 -0
- package/dist/composition/index.d.ts.map +1 -0
- package/dist/composition/index.js +7 -0
- package/dist/composition/index.js.map +1 -0
- package/dist/composition/media.d.ts +27 -0
- package/dist/composition/media.d.ts.map +1 -0
- package/dist/composition/media.js +162 -0
- package/dist/composition/media.js.map +1 -0
- package/dist/composition/paths.d.ts +5 -0
- package/dist/composition/paths.d.ts.map +1 -0
- package/dist/composition/paths.js +16 -0
- package/dist/composition/paths.js.map +1 -0
- package/dist/composition/transcripts.d.ts +19 -0
- package/dist/composition/transcripts.d.ts.map +1 -0
- package/dist/composition/transcripts.js +57 -0
- package/dist/composition/transcripts.js.map +1 -0
- package/dist/composition/walk.d.ts +10 -0
- package/dist/composition/walk.d.ts.map +1 -0
- package/dist/composition/walk.js +32 -0
- package/dist/composition/walk.js.map +1 -0
- package/dist/contracts.d.ts +75 -47
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +14 -159
- package/dist/contracts.js.map +1 -1
- package/dist/errors.d.ts +15 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +8 -0
- package/dist/errors.js.map +1 -1
- package/dist/html/document-adapter.d.ts +5 -0
- package/dist/html/document-adapter.d.ts.map +1 -0
- package/dist/html/document-adapter.js +17 -0
- package/dist/html/document-adapter.js.map +1 -0
- package/dist/html/index.d.ts +5 -0
- package/dist/html/index.d.ts.map +1 -0
- package/dist/html/index.js +3 -0
- package/dist/html/index.js.map +1 -0
- package/dist/html/parse.d.ts +26 -0
- package/dist/html/parse.d.ts.map +1 -0
- package/dist/html/parse.js +80 -0
- package/dist/html/parse.js.map +1 -0
- package/dist/html/validate.d.ts +14 -0
- package/dist/html/validate.d.ts.map +1 -0
- package/dist/html/validate.js +648 -0
- package/dist/html/validate.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/preview/main.d.ts +10 -3
- package/dist/preview/main.d.ts.map +1 -1
- package/dist/preview/main.js +189 -89
- package/dist/preview/main.js.map +1 -1
- package/dist/project.d.ts +2 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +4 -0
- package/dist/project.js.map +1 -1
- package/dist/renderer/browser.d.ts +11 -1
- package/dist/renderer/browser.d.ts.map +1 -1
- package/dist/renderer/browser.js +93 -14
- package/dist/renderer/browser.js.map +1 -1
- package/dist/renderer/index.d.ts +25 -5
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +82 -16
- package/dist/renderer/index.js.map +1 -1
- package/dist/runtime/bindings.d.ts +11 -0
- package/dist/runtime/bindings.d.ts.map +1 -0
- package/dist/runtime/bindings.js +164 -0
- package/dist/runtime/bindings.js.map +1 -0
- package/dist/runtime/diagnostics.d.ts +4 -0
- package/dist/runtime/diagnostics.d.ts.map +1 -0
- package/dist/runtime/diagnostics.js +107 -0
- package/dist/runtime/diagnostics.js.map +1 -0
- package/dist/runtime/document-adapter.d.ts +4 -0
- package/dist/runtime/document-adapter.d.ts.map +1 -0
- package/dist/runtime/document-adapter.js +19 -0
- package/dist/runtime/document-adapter.js.map +1 -0
- package/dist/runtime/host.d.ts +14 -0
- package/dist/runtime/host.d.ts.map +1 -0
- package/dist/runtime/host.js +596 -0
- package/dist/runtime/host.js.map +1 -0
- package/dist/runtime/index.d.ts +0 -29
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +0 -360
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/media.d.ts.map +1 -1
- package/dist/runtime/media.js +20 -51
- package/dist/runtime/media.js.map +1 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +177 -84
- package/dist/scaffold.js.map +1 -1
- package/dist/server/composition-html.d.ts +20 -0
- package/dist/server/composition-html.d.ts.map +1 -0
- package/dist/server/composition-html.js +45 -0
- package/dist/server/composition-html.js.map +1 -0
- package/dist/server/preview-html.d.ts.map +1 -1
- package/dist/server/preview-html.js +19 -14
- package/dist/server/preview-html.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +94 -6
- package/dist/server/server.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/adr/0001-keep-composition-authoring-web-native.md +2 -0
- package/docs/adr/0006-seek-embedded-media-from-the-frame-clock.md +7 -1
- package/docs/adr/0007-make-html-documents-the-composition-unit.md +31 -0
- package/docs/adr/0008-share-the-composition-document-grammar.md +18 -0
- package/docs/adr/0009-composite-production-frames-in-software.md +37 -0
- package/docs/migration-0.4.md +279 -0
- package/docs/v0.1-design.md +78 -56
- package/examples/demo/README.md +8 -4
- package/examples/demo/compositions/demo.controller.js +120 -0
- package/examples/demo/compositions/demo.html +253 -0
- package/examples/demo/video.config.js +1 -1
- package/examples/video/README.md +61 -0
- package/examples/video/assets/clip.mp4 +0 -0
- package/examples/video/assets/clip.webm +0 -0
- package/examples/video/compositions/video-mp4.html +362 -0
- package/examples/video/compositions/video.html +362 -0
- package/examples/video/props/video.json +8 -0
- package/examples/video/scripts/make-clip.sh +109 -0
- package/examples/video/video.config.js +6 -0
- package/examples/webgl/README.md +30 -0
- package/examples/webgl/compositions/webgl.controller.js +200 -0
- package/examples/webgl/compositions/webgl.html +215 -0
- package/examples/webgl/props/webgl.json +6 -0
- package/examples/webgl/video.config.js +5 -0
- package/package.json +7 -3
- package/skills/frameyard/README.md +4 -0
- package/skills/frameyard/SKILL.md +40 -12
- package/skills/frameyard/references/audio-assets.md +25 -14
- package/skills/frameyard/references/bindings.md +73 -0
- package/skills/frameyard/references/common-mistakes.md +59 -0
- package/skills/frameyard/references/controllers.md +126 -0
- package/skills/frameyard/references/deterministic-authoring.md +143 -52
- package/skills/frameyard/references/diagnostics.md +90 -0
- package/skills/frameyard/references/failure-recovery.md +37 -14
- package/skills/frameyard/references/json-results.md +43 -9
- package/skills/frameyard/references/subtitles-karaoke.md +10 -18
- package/examples/demo/compositions/demo.js +0 -206
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<script type="application/json" data-fy="props">
|
|
6
|
+
{
|
|
7
|
+
"title": "Frameyard",
|
|
8
|
+
"subtitle": "Deterministic video, authored with the web platform.",
|
|
9
|
+
"accent": "#b8f36b"
|
|
10
|
+
}
|
|
11
|
+
</script>
|
|
12
|
+
<style>
|
|
13
|
+
@keyframes orbit {
|
|
14
|
+
from {
|
|
15
|
+
transform: rotate(0deg) translateX(210px) rotate(0deg);
|
|
16
|
+
}
|
|
17
|
+
to {
|
|
18
|
+
transform: rotate(360deg) translateX(210px) rotate(-360deg);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
@keyframes breathe {
|
|
22
|
+
0%,
|
|
23
|
+
100% {
|
|
24
|
+
opacity: 0.35;
|
|
25
|
+
transform: scale(1);
|
|
26
|
+
}
|
|
27
|
+
50% {
|
|
28
|
+
opacity: 0.7;
|
|
29
|
+
transform: scale(1.12);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
@keyframes title-in {
|
|
33
|
+
from {
|
|
34
|
+
opacity: 0;
|
|
35
|
+
transform: translateY(46px);
|
|
36
|
+
}
|
|
37
|
+
to {
|
|
38
|
+
opacity: 1;
|
|
39
|
+
transform: translateY(0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
@keyframes subtitle-in {
|
|
43
|
+
from {
|
|
44
|
+
opacity: 0;
|
|
45
|
+
transform: translateY(26px);
|
|
46
|
+
}
|
|
47
|
+
to {
|
|
48
|
+
opacity: 1;
|
|
49
|
+
transform: translateY(0);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
@keyframes scene-out {
|
|
53
|
+
from {
|
|
54
|
+
opacity: 1;
|
|
55
|
+
}
|
|
56
|
+
to {
|
|
57
|
+
opacity: 0;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
html,
|
|
61
|
+
body,
|
|
62
|
+
fy-composition {
|
|
63
|
+
background: #080a0d;
|
|
64
|
+
}
|
|
65
|
+
#scene {
|
|
66
|
+
position: relative;
|
|
67
|
+
width: 100%;
|
|
68
|
+
height: 100%;
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
background: #080a0d;
|
|
71
|
+
color: #eef2f7;
|
|
72
|
+
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
|
73
|
+
animation: scene-out 1s linear both;
|
|
74
|
+
}
|
|
75
|
+
#field {
|
|
76
|
+
position: absolute;
|
|
77
|
+
inset: 0;
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 100%;
|
|
80
|
+
}
|
|
81
|
+
#orb {
|
|
82
|
+
position: absolute;
|
|
83
|
+
top: 46%;
|
|
84
|
+
left: 38%;
|
|
85
|
+
width: 460px;
|
|
86
|
+
height: 460px;
|
|
87
|
+
margin: -230px 0 0 -230px;
|
|
88
|
+
border-radius: 50%;
|
|
89
|
+
background: var(--fy-prop-accent);
|
|
90
|
+
filter: blur(110px);
|
|
91
|
+
opacity: 0.28;
|
|
92
|
+
animation: orbit 30s linear infinite;
|
|
93
|
+
}
|
|
94
|
+
#glow {
|
|
95
|
+
position: absolute;
|
|
96
|
+
top: 8%;
|
|
97
|
+
right: 6%;
|
|
98
|
+
width: 280px;
|
|
99
|
+
height: 280px;
|
|
100
|
+
border-radius: 50%;
|
|
101
|
+
background: var(--fy-prop-accent);
|
|
102
|
+
filter: blur(90px);
|
|
103
|
+
animation: breathe 9s ease-in-out infinite;
|
|
104
|
+
}
|
|
105
|
+
#rail {
|
|
106
|
+
position: absolute;
|
|
107
|
+
top: 92px;
|
|
108
|
+
left: 160px;
|
|
109
|
+
right: 160px;
|
|
110
|
+
height: 1px;
|
|
111
|
+
background: rgba(255, 255, 255, 0.14);
|
|
112
|
+
}
|
|
113
|
+
#eyebrow {
|
|
114
|
+
position: absolute;
|
|
115
|
+
top: 64px;
|
|
116
|
+
left: 160px;
|
|
117
|
+
font:
|
|
118
|
+
600 14px/1 ui-monospace,
|
|
119
|
+
monospace;
|
|
120
|
+
letter-spacing: 0.28em;
|
|
121
|
+
text-transform: uppercase;
|
|
122
|
+
color: var(--fy-prop-accent);
|
|
123
|
+
}
|
|
124
|
+
#meta {
|
|
125
|
+
position: absolute;
|
|
126
|
+
top: 64px;
|
|
127
|
+
right: 160px;
|
|
128
|
+
font:
|
|
129
|
+
500 14px/1 ui-monospace,
|
|
130
|
+
monospace;
|
|
131
|
+
letter-spacing: 0.12em;
|
|
132
|
+
color: #788595;
|
|
133
|
+
}
|
|
134
|
+
#copy {
|
|
135
|
+
position: absolute;
|
|
136
|
+
left: 160px;
|
|
137
|
+
top: 236px;
|
|
138
|
+
width: 1320px;
|
|
139
|
+
}
|
|
140
|
+
#title {
|
|
141
|
+
margin: 0;
|
|
142
|
+
font-size: 156px;
|
|
143
|
+
line-height: 0.92;
|
|
144
|
+
letter-spacing: -0.055em;
|
|
145
|
+
font-weight: 680;
|
|
146
|
+
opacity: 0;
|
|
147
|
+
animation: title-in 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
148
|
+
}
|
|
149
|
+
#subtitle {
|
|
150
|
+
margin: 34px 0 0;
|
|
151
|
+
max-width: 820px;
|
|
152
|
+
font-size: 31px;
|
|
153
|
+
line-height: 1.45;
|
|
154
|
+
color: #96a2b2;
|
|
155
|
+
font-weight: 420;
|
|
156
|
+
opacity: 0;
|
|
157
|
+
animation: subtitle-in 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
158
|
+
}
|
|
159
|
+
#signal {
|
|
160
|
+
position: absolute;
|
|
161
|
+
left: 0;
|
|
162
|
+
bottom: 0;
|
|
163
|
+
width: 1920px;
|
|
164
|
+
height: 1080px;
|
|
165
|
+
overflow: visible;
|
|
166
|
+
}
|
|
167
|
+
#caption {
|
|
168
|
+
position: absolute;
|
|
169
|
+
left: 160px;
|
|
170
|
+
bottom: 92px;
|
|
171
|
+
font:
|
|
172
|
+
500 13px/1 ui-monospace,
|
|
173
|
+
monospace;
|
|
174
|
+
letter-spacing: 0.16em;
|
|
175
|
+
color: #7e8b9b;
|
|
176
|
+
}
|
|
177
|
+
#caption strong {
|
|
178
|
+
color: var(--fy-prop-accent);
|
|
179
|
+
font-weight: 600;
|
|
180
|
+
}
|
|
181
|
+
#progress-track {
|
|
182
|
+
position: absolute;
|
|
183
|
+
left: 160px;
|
|
184
|
+
right: 160px;
|
|
185
|
+
bottom: 72px;
|
|
186
|
+
height: 2px;
|
|
187
|
+
background: rgba(255, 255, 255, 0.1);
|
|
188
|
+
}
|
|
189
|
+
#progress {
|
|
190
|
+
position: absolute;
|
|
191
|
+
inset: 0 auto 0 0;
|
|
192
|
+
width: calc(var(--fy-progress) * 100%);
|
|
193
|
+
background: var(--fy-prop-accent);
|
|
194
|
+
box-shadow: 0 0 18px var(--fy-prop-accent);
|
|
195
|
+
}
|
|
196
|
+
#vignette {
|
|
197
|
+
position: absolute;
|
|
198
|
+
inset: 0;
|
|
199
|
+
background: radial-gradient(ellipse at center, transparent 48%, rgba(0, 0, 0, 0.42) 100%);
|
|
200
|
+
pointer-events: none;
|
|
201
|
+
}
|
|
202
|
+
</style>
|
|
203
|
+
</head>
|
|
204
|
+
<body>
|
|
205
|
+
<fy-composition width="1920" height="1080" fps="30" duration-in-frames="900">
|
|
206
|
+
<div id="scene" data-fy-anim-start="824" data-fy-anim-duration="70" data-fy-anim-easing="linear">
|
|
207
|
+
<canvas id="field" width="1920" height="1080"></canvas>
|
|
208
|
+
<div id="orb"></div>
|
|
209
|
+
<div id="glow"></div>
|
|
210
|
+
<div id="vignette"></div>
|
|
211
|
+
<div id="rail"></div>
|
|
212
|
+
<div id="eyebrow">Frameyard / Field Notes</div>
|
|
213
|
+
<div id="meta">1920×1080 · 30 FPS · 30.00 S</div>
|
|
214
|
+
<div id="copy">
|
|
215
|
+
<h1
|
|
216
|
+
id="title"
|
|
217
|
+
data-fy-text="title"
|
|
218
|
+
data-fy-anim-start="0"
|
|
219
|
+
data-fy-anim-duration="52"
|
|
220
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
221
|
+
></h1>
|
|
222
|
+
<p
|
|
223
|
+
id="subtitle"
|
|
224
|
+
data-fy-text="subtitle"
|
|
225
|
+
data-fy-anim-start="26"
|
|
226
|
+
data-fy-anim-duration="62"
|
|
227
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
228
|
+
></p>
|
|
229
|
+
</div>
|
|
230
|
+
<svg id="signal" viewBox="0 0 1920 1080" fill="none" aria-hidden="true">
|
|
231
|
+
<path
|
|
232
|
+
id="signal-path"
|
|
233
|
+
stroke="var(--fy-prop-accent)"
|
|
234
|
+
stroke-width="3"
|
|
235
|
+
stroke-linecap="round"
|
|
236
|
+
stroke-linejoin="round"
|
|
237
|
+
opacity=".82"
|
|
238
|
+
></path>
|
|
239
|
+
<g id="signal-bars"></g>
|
|
240
|
+
</svg>
|
|
241
|
+
<div id="caption">FRAME <strong id="frame">000</strong> / 899 · SEEK-SAFE CANVAS + SVG + CSS</div>
|
|
242
|
+
<div id="progress-track"><div id="progress"></div></div>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<audio src="assets/chime.wav" data-fy-start="18" data-fy-volume="0.5"></audio>
|
|
246
|
+
<audio src="assets/chime.wav" data-fy-start="300" data-fy-volume="0.38"></audio>
|
|
247
|
+
<audio src="assets/chime.wav" data-fy-start="600" data-fy-volume="0.42"></audio>
|
|
248
|
+
<audio src="assets/chime.wav" data-fy-start="840" data-fy-volume="0.55"></audio>
|
|
249
|
+
</fy-composition>
|
|
250
|
+
|
|
251
|
+
<script type="text/fy-controller" src="./demo.controller.js"></script>
|
|
252
|
+
</body>
|
|
253
|
+
</html>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Frameyard video example
|
|
2
|
+
|
|
3
|
+
A 12-second, 1920×1080 composition that treats a local clip as source footage
|
|
4
|
+
and seeks it twice. The project includes the same layout for two source codecs:
|
|
5
|
+
|
|
6
|
+
- `video` reads `assets/clip.webm` (VP9 + Opus)
|
|
7
|
+
- `video-mp4` reads `assets/clip.mp4` (H.264 + AAC)
|
|
8
|
+
|
|
9
|
+
Both compositions include:
|
|
10
|
+
|
|
11
|
+
- a full-bleed hero pass whose unmuted soundtrack joins the composition audio timeline
|
|
12
|
+
- a persistent detail pass with its own `data-fy-start`, `data-fy-trim`, and `data-fy-duration`
|
|
13
|
+
- `data-fy-audio="off"` on the detail pass so only the hero contributes audio
|
|
14
|
+
- a frame counter derived from `--fy-frame` in CSS, with static source-range labels
|
|
15
|
+
- frame-scheduled CSS typography and a frame-state progress bar
|
|
16
|
+
- a one-second fade to black over the final second of the composition
|
|
17
|
+
|
|
18
|
+
The hero pass uses the full 12-second source at volume `0.85`. The detail window
|
|
19
|
+
is visible for the whole composition: it holds the source at 5.8 seconds until
|
|
20
|
+
composition frame 120, then plays 5 seconds of the same file, and holds the final
|
|
21
|
+
source frame after frame 270. The result shows two source moments side by side.
|
|
22
|
+
The example is entirely declarative: the runtime owns loading, pausing, and
|
|
23
|
+
frame seeking, and CSS derives the frame counter from the frame state. No
|
|
24
|
+
controller is needed. The header, copy, and footer fade individually rather
|
|
25
|
+
than the whole scene; a full-scene opacity animation above a video can leave
|
|
26
|
+
the load frame on screen in interactive preview after a large seek.
|
|
27
|
+
|
|
28
|
+
The compositions live in `compositions/video.html` and
|
|
29
|
+
`compositions/video-mp4.html`. Both clips are 12-second 1280×720 files generated
|
|
30
|
+
procedurally for this example; the MP4 is transcoded from the WebM source.
|
|
31
|
+
Replace either asset with a local, seekable video file that is at least
|
|
32
|
+
12 seconds long; remote URLs and live streams are not supported.
|
|
33
|
+
|
|
34
|
+
Regenerate both clips from scratch with the bundled FFmpeg script:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
bash examples/video/scripts/make-clip.sh
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The script writes both files into `assets/`; pass `--out-dir <directory>` to
|
|
41
|
+
write elsewhere. It requires FFmpeg with `libvpx-vp9`, `libx264`, Opus, and AAC
|
|
42
|
+
encoders, and fixed noise seeds keep the generated grain stable across runs on
|
|
43
|
+
the same FFmpeg build.
|
|
44
|
+
|
|
45
|
+
From the repository root:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
pnpm build
|
|
49
|
+
node dist/cli.js check video --project examples/video
|
|
50
|
+
node dist/cli.js check video-mp4 --project examples/video
|
|
51
|
+
node dist/cli.js still video --frame 210 --project examples/video --out video-still.png
|
|
52
|
+
node dist/cli.js still video-mp4 --frame 210 --project examples/video --out video-mp4-still.png
|
|
53
|
+
node dist/cli.js render video --project examples/video --props examples/video/props/video.json
|
|
54
|
+
node dist/cli.js render video-mp4 --project examples/video --props examples/video/props/video.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Start the interactive preview with:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
node dist/cli.js dev --project examples/video
|
|
61
|
+
```
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<script type="application/json" data-fy="props">
|
|
6
|
+
{
|
|
7
|
+
"kicker": "Frameyard / Clip Study 001",
|
|
8
|
+
"title": "Moving",
|
|
9
|
+
"titleAccent": "Image",
|
|
10
|
+
"subtitle": "A local clip, loaded once and seeked twice from the composition clock.",
|
|
11
|
+
"detail": "Detail / second pass",
|
|
12
|
+
"accent": "#ffc37a"
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
<style>
|
|
16
|
+
@keyframes fade-in {
|
|
17
|
+
from {
|
|
18
|
+
opacity: 0;
|
|
19
|
+
}
|
|
20
|
+
to {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
@keyframes rise {
|
|
25
|
+
from {
|
|
26
|
+
opacity: 0;
|
|
27
|
+
transform: translateY(42px);
|
|
28
|
+
}
|
|
29
|
+
to {
|
|
30
|
+
opacity: 1;
|
|
31
|
+
transform: translateY(0);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
@keyframes draw {
|
|
35
|
+
from {
|
|
36
|
+
opacity: 0;
|
|
37
|
+
transform: scaleX(0);
|
|
38
|
+
}
|
|
39
|
+
to {
|
|
40
|
+
opacity: 1;
|
|
41
|
+
transform: scaleX(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
@keyframes outro-fade {
|
|
45
|
+
from {
|
|
46
|
+
opacity: 0;
|
|
47
|
+
}
|
|
48
|
+
to {
|
|
49
|
+
opacity: 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
html,
|
|
53
|
+
body,
|
|
54
|
+
fy-composition {
|
|
55
|
+
background: #04080b;
|
|
56
|
+
}
|
|
57
|
+
#scene {
|
|
58
|
+
position: relative;
|
|
59
|
+
width: 100%;
|
|
60
|
+
height: 100%;
|
|
61
|
+
overflow: hidden;
|
|
62
|
+
isolation: isolate;
|
|
63
|
+
background: #04080b;
|
|
64
|
+
color: #eef4f1;
|
|
65
|
+
font-family: Inter, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
|
|
66
|
+
}
|
|
67
|
+
#hero {
|
|
68
|
+
position: absolute;
|
|
69
|
+
inset: 0;
|
|
70
|
+
display: block;
|
|
71
|
+
width: 100%;
|
|
72
|
+
height: 100%;
|
|
73
|
+
object-fit: cover;
|
|
74
|
+
transform: scale(calc(1 + var(--fy-progress) * 0.035));
|
|
75
|
+
transform-origin: center;
|
|
76
|
+
}
|
|
77
|
+
#scrim {
|
|
78
|
+
position: absolute;
|
|
79
|
+
inset: 0;
|
|
80
|
+
background:
|
|
81
|
+
linear-gradient(
|
|
82
|
+
90deg,
|
|
83
|
+
rgba(4, 8, 11, 0.94) 0%,
|
|
84
|
+
rgba(4, 8, 11, 0.8) 24%,
|
|
85
|
+
rgba(4, 8, 11, 0.24) 52%,
|
|
86
|
+
rgba(4, 8, 11, 0) 74%
|
|
87
|
+
),
|
|
88
|
+
linear-gradient(0deg, rgba(4, 8, 11, 0.9) 0%, rgba(4, 8, 11, 0) 34%);
|
|
89
|
+
}
|
|
90
|
+
#vignette {
|
|
91
|
+
position: absolute;
|
|
92
|
+
inset: 0;
|
|
93
|
+
background: radial-gradient(ellipse at 62% 44%, transparent 42%, rgba(0, 0, 0, 0.3) 100%);
|
|
94
|
+
pointer-events: none;
|
|
95
|
+
}
|
|
96
|
+
#frame-border {
|
|
97
|
+
position: absolute;
|
|
98
|
+
inset: 36px;
|
|
99
|
+
border: 1px solid rgba(238, 244, 241, 0.16);
|
|
100
|
+
pointer-events: none;
|
|
101
|
+
}
|
|
102
|
+
#header {
|
|
103
|
+
position: absolute;
|
|
104
|
+
top: 80px;
|
|
105
|
+
left: 148px;
|
|
106
|
+
right: 148px;
|
|
107
|
+
display: flex;
|
|
108
|
+
justify-content: space-between;
|
|
109
|
+
align-items: center;
|
|
110
|
+
gap: 32px;
|
|
111
|
+
animation: fade-in 1s linear both;
|
|
112
|
+
}
|
|
113
|
+
#kicker {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 15px;
|
|
117
|
+
font:
|
|
118
|
+
600 14px/1 ui-monospace,
|
|
119
|
+
monospace;
|
|
120
|
+
letter-spacing: 0.24em;
|
|
121
|
+
text-transform: uppercase;
|
|
122
|
+
color: var(--fy-prop-accent);
|
|
123
|
+
}
|
|
124
|
+
#kicker::before {
|
|
125
|
+
content: "";
|
|
126
|
+
width: 8px;
|
|
127
|
+
height: 8px;
|
|
128
|
+
background: var(--fy-prop-accent);
|
|
129
|
+
box-shadow: 0 0 16px var(--fy-prop-accent);
|
|
130
|
+
}
|
|
131
|
+
#format {
|
|
132
|
+
font:
|
|
133
|
+
500 12px/1 ui-monospace,
|
|
134
|
+
monospace;
|
|
135
|
+
letter-spacing: 0.16em;
|
|
136
|
+
text-transform: uppercase;
|
|
137
|
+
color: #8b999c;
|
|
138
|
+
}
|
|
139
|
+
#copy {
|
|
140
|
+
position: absolute;
|
|
141
|
+
top: 278px;
|
|
142
|
+
left: 148px;
|
|
143
|
+
width: 860px;
|
|
144
|
+
}
|
|
145
|
+
#title {
|
|
146
|
+
margin: 0;
|
|
147
|
+
font-size: 174px;
|
|
148
|
+
line-height: 0.84;
|
|
149
|
+
letter-spacing: -0.064em;
|
|
150
|
+
font-weight: 660;
|
|
151
|
+
}
|
|
152
|
+
#title span,
|
|
153
|
+
#title em {
|
|
154
|
+
display: block;
|
|
155
|
+
animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
156
|
+
}
|
|
157
|
+
#title em {
|
|
158
|
+
margin-top: 0.06em;
|
|
159
|
+
font-family: "Iowan Old Style", "Palatino Linotype", Georgia, ui-serif, serif;
|
|
160
|
+
font-style: italic;
|
|
161
|
+
font-weight: 400;
|
|
162
|
+
letter-spacing: -0.035em;
|
|
163
|
+
color: var(--fy-prop-accent);
|
|
164
|
+
}
|
|
165
|
+
#subtitle {
|
|
166
|
+
width: 720px;
|
|
167
|
+
margin: 38px 0 0;
|
|
168
|
+
font-size: 29px;
|
|
169
|
+
line-height: 1.5;
|
|
170
|
+
font-weight: 420;
|
|
171
|
+
color: #a6b3b6;
|
|
172
|
+
animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
173
|
+
}
|
|
174
|
+
#footer {
|
|
175
|
+
position: absolute;
|
|
176
|
+
left: 148px;
|
|
177
|
+
right: 148px;
|
|
178
|
+
bottom: 70px;
|
|
179
|
+
contain: layout paint style;
|
|
180
|
+
animation: fade-in 1s linear both;
|
|
181
|
+
}
|
|
182
|
+
#rule {
|
|
183
|
+
position: relative;
|
|
184
|
+
height: 1px;
|
|
185
|
+
background: rgba(238, 244, 241, 0.16);
|
|
186
|
+
transform-origin: left center;
|
|
187
|
+
animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
188
|
+
}
|
|
189
|
+
#progress {
|
|
190
|
+
position: absolute;
|
|
191
|
+
inset: 0 auto 0 0;
|
|
192
|
+
width: calc(var(--fy-progress) * 100%);
|
|
193
|
+
background: var(--fy-prop-accent);
|
|
194
|
+
box-shadow: 0 0 14px var(--fy-prop-accent);
|
|
195
|
+
}
|
|
196
|
+
#footer-row {
|
|
197
|
+
display: flex;
|
|
198
|
+
justify-content: space-between;
|
|
199
|
+
align-items: baseline;
|
|
200
|
+
gap: 24px;
|
|
201
|
+
margin-top: 18px;
|
|
202
|
+
font:
|
|
203
|
+
500 12px/1 ui-monospace,
|
|
204
|
+
monospace;
|
|
205
|
+
letter-spacing: 0.14em;
|
|
206
|
+
color: #879598;
|
|
207
|
+
text-transform: uppercase;
|
|
208
|
+
}
|
|
209
|
+
#footer-row strong {
|
|
210
|
+
color: #edf5f2;
|
|
211
|
+
font-weight: 600;
|
|
212
|
+
}
|
|
213
|
+
#footer-row .accent {
|
|
214
|
+
color: var(--fy-prop-accent);
|
|
215
|
+
}
|
|
216
|
+
#frame-counter {
|
|
217
|
+
display: inline-block;
|
|
218
|
+
min-width: 3ch;
|
|
219
|
+
text-align: right;
|
|
220
|
+
}
|
|
221
|
+
#frame-counter::after {
|
|
222
|
+
counter-reset: fy-frame var(--fy-frame);
|
|
223
|
+
content: counter(fy-frame);
|
|
224
|
+
}
|
|
225
|
+
#detail {
|
|
226
|
+
position: absolute;
|
|
227
|
+
right: 148px;
|
|
228
|
+
bottom: 154px;
|
|
229
|
+
width: 440px;
|
|
230
|
+
}
|
|
231
|
+
#detail-bar {
|
|
232
|
+
display: flex;
|
|
233
|
+
justify-content: space-between;
|
|
234
|
+
align-items: baseline;
|
|
235
|
+
gap: 24px;
|
|
236
|
+
margin-bottom: 10px;
|
|
237
|
+
contain: layout paint style;
|
|
238
|
+
font:
|
|
239
|
+
600 11px/1 ui-monospace,
|
|
240
|
+
monospace;
|
|
241
|
+
letter-spacing: 0.18em;
|
|
242
|
+
text-transform: uppercase;
|
|
243
|
+
}
|
|
244
|
+
#detail-bar > span:first-child {
|
|
245
|
+
color: var(--fy-prop-accent);
|
|
246
|
+
}
|
|
247
|
+
#detail-time {
|
|
248
|
+
color: #98a5a6;
|
|
249
|
+
font-weight: 500;
|
|
250
|
+
}
|
|
251
|
+
#detail-window {
|
|
252
|
+
position: relative;
|
|
253
|
+
width: 440px;
|
|
254
|
+
height: 248px;
|
|
255
|
+
overflow: hidden;
|
|
256
|
+
border: 1px solid rgba(255, 195, 122, 0.65);
|
|
257
|
+
background: #000;
|
|
258
|
+
box-shadow: 0 24px 90px rgba(0, 0, 0, 0.55);
|
|
259
|
+
}
|
|
260
|
+
#detail-clip {
|
|
261
|
+
display: block;
|
|
262
|
+
width: 100%;
|
|
263
|
+
height: 100%;
|
|
264
|
+
object-fit: cover;
|
|
265
|
+
object-position: 68% 50%;
|
|
266
|
+
transform: scale(1.3);
|
|
267
|
+
}
|
|
268
|
+
#detail-tint {
|
|
269
|
+
position: absolute;
|
|
270
|
+
inset: 0;
|
|
271
|
+
background: linear-gradient(0deg, rgba(4, 8, 11, 0.28) 0%, rgba(4, 8, 11, 0) 45%);
|
|
272
|
+
pointer-events: none;
|
|
273
|
+
}
|
|
274
|
+
#outro-fade {
|
|
275
|
+
position: absolute;
|
|
276
|
+
inset: 0;
|
|
277
|
+
background: #04080b;
|
|
278
|
+
pointer-events: none;
|
|
279
|
+
opacity: 0;
|
|
280
|
+
animation: outro-fade 1s linear both;
|
|
281
|
+
}
|
|
282
|
+
</style>
|
|
283
|
+
</head>
|
|
284
|
+
<body>
|
|
285
|
+
<fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
|
|
286
|
+
<div id="scene">
|
|
287
|
+
<video
|
|
288
|
+
id="hero"
|
|
289
|
+
src="assets/clip.mp4"
|
|
290
|
+
data-fy-start="0"
|
|
291
|
+
data-fy-trim="0"
|
|
292
|
+
data-fy-duration="12"
|
|
293
|
+
data-fy-volume="0.85"
|
|
294
|
+
></video>
|
|
295
|
+
<div id="scrim"></div>
|
|
296
|
+
<div id="vignette"></div>
|
|
297
|
+
<div id="frame-border"></div>
|
|
298
|
+
|
|
299
|
+
<header id="header" data-fy-anim-start="8" data-fy-anim-duration="28">
|
|
300
|
+
<div id="kicker" data-fy-text="kicker"></div>
|
|
301
|
+
<div id="format">Embedded video / H.264 + AAC / 30 FPS</div>
|
|
302
|
+
</header>
|
|
303
|
+
|
|
304
|
+
<div id="copy">
|
|
305
|
+
<h1 id="title">
|
|
306
|
+
<span
|
|
307
|
+
data-fy-text="title"
|
|
308
|
+
data-fy-anim-start="14"
|
|
309
|
+
data-fy-anim-duration="44"
|
|
310
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
311
|
+
></span>
|
|
312
|
+
<em
|
|
313
|
+
data-fy-text="titleAccent"
|
|
314
|
+
data-fy-anim-start="26"
|
|
315
|
+
data-fy-anim-duration="44"
|
|
316
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
317
|
+
></em>
|
|
318
|
+
</h1>
|
|
319
|
+
<p
|
|
320
|
+
id="subtitle"
|
|
321
|
+
data-fy-text="subtitle"
|
|
322
|
+
data-fy-anim-start="40"
|
|
323
|
+
data-fy-anim-duration="40"
|
|
324
|
+
data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
|
|
325
|
+
></p>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<div id="detail">
|
|
329
|
+
<div id="detail-bar">
|
|
330
|
+
<span data-fy-text="detail"></span>
|
|
331
|
+
<span id="detail-time">SRC 5.8–10.8 S</span>
|
|
332
|
+
</div>
|
|
333
|
+
<div id="detail-window">
|
|
334
|
+
<video
|
|
335
|
+
id="detail-clip"
|
|
336
|
+
src="assets/clip.mp4"
|
|
337
|
+
data-fy-start="120"
|
|
338
|
+
data-fy-trim="5.8"
|
|
339
|
+
data-fy-duration="5"
|
|
340
|
+
data-fy-audio="off"
|
|
341
|
+
></video>
|
|
342
|
+
<div id="detail-tint"></div>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<div id="footer" data-fy-anim-start="46" data-fy-anim-duration="30">
|
|
347
|
+
<div id="rule" data-fy-anim-start="52" data-fy-anim-duration="50">
|
|
348
|
+
<div id="progress"></div>
|
|
349
|
+
</div>
|
|
350
|
+
<div id="footer-row">
|
|
351
|
+
<span>Hero / SRC <strong>0.0–12.0 S</strong></span>
|
|
352
|
+
<span>Frame <strong id="frame-counter"></strong> / 359</span>
|
|
353
|
+
<span class="accent">Frame-seeked</span>
|
|
354
|
+
<span>12.00 S · 30 FPS</span>
|
|
355
|
+
</div>
|
|
356
|
+
</div>
|
|
357
|
+
|
|
358
|
+
<div id="outro-fade" data-fy-anim-start="329" data-fy-anim-duration="30"></div>
|
|
359
|
+
</div>
|
|
360
|
+
</fy-composition>
|
|
361
|
+
</body>
|
|
362
|
+
</html>
|