frameyard 0.4.0 → 0.6.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 +101 -0
- package/GLOSSARY.md +156 -24
- package/README.md +103 -31
- package/dist/cli.js +112 -19
- 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 +14 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- 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/parse.d.ts +2 -6
- package/dist/html/parse.d.ts.map +1 -1
- package/dist/html/parse.js.map +1 -1
- package/dist/html/validate.d.ts.map +1 -1
- package/dist/html/validate.js +92 -375
- package/dist/html/validate.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/preview/main.d.ts.map +1 -1
- package/dist/preview/main.js +93 -13
- package/dist/preview/main.js.map +1 -1
- package/dist/preview/timeline-data.d.ts +85 -0
- package/dist/preview/timeline-data.d.ts.map +1 -0
- package/dist/preview/timeline-data.js +2 -0
- package/dist/preview/timeline-data.js.map +1 -0
- package/dist/preview/timeline-host.d.ts +19 -0
- package/dist/preview/timeline-host.d.ts.map +1 -0
- package/dist/preview/timeline-host.js +297 -0
- package/dist/preview/timeline-host.js.map +1 -0
- package/dist/preview/timeline-main.d.ts +17 -0
- package/dist/preview/timeline-main.d.ts.map +1 -0
- package/dist/preview/timeline-main.js +364 -0
- package/dist/preview/timeline-main.js.map +1 -0
- package/dist/project.d.ts +16 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +81 -26
- package/dist/project.js.map +1 -1
- package/dist/renderer/browser.d.ts +2 -0
- package/dist/renderer/browser.d.ts.map +1 -1
- package/dist/renderer/browser.js +38 -6
- package/dist/renderer/browser.js.map +1 -1
- package/dist/renderer/ffmpeg.d.ts +61 -0
- package/dist/renderer/ffmpeg.d.ts.map +1 -1
- package/dist/renderer/ffmpeg.js +198 -0
- package/dist/renderer/ffmpeg.js.map +1 -1
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +18 -7
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/timeline.d.ts +109 -0
- package/dist/renderer/timeline.d.ts.map +1 -0
- package/dist/renderer/timeline.js +984 -0
- package/dist/renderer/timeline.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 +2 -0
- package/dist/runtime/host.d.ts.map +1 -1
- package/dist/runtime/host.js +78 -183
- package/dist/runtime/host.js.map +1 -1
- package/dist/runtime/media.d.ts.map +1 -1
- package/dist/runtime/media.js +19 -50
- package/dist/runtime/media.js.map +1 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +93 -23
- package/dist/scaffold.js.map +1 -1
- package/dist/server/preview-html.d.ts.map +1 -1
- package/dist/server/preview-html.js +15 -1
- package/dist/server/preview-html.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +39 -1
- package/dist/server/server.js.map +1 -1
- package/dist/server/timeline-preview-data.d.ts +4 -0
- package/dist/server/timeline-preview-data.d.ts.map +1 -0
- package/dist/server/timeline-preview-data.js +97 -0
- package/dist/server/timeline-preview-data.js.map +1 -0
- package/dist/server/timeline-preview-html.d.ts +4 -0
- package/dist/server/timeline-preview-html.d.ts.map +1 -0
- package/dist/server/timeline-preview-html.js +227 -0
- package/dist/server/timeline-preview-html.js.map +1 -0
- package/dist/timeline/plan.d.ts +32 -0
- package/dist/timeline/plan.d.ts.map +1 -0
- package/dist/timeline/plan.js +68 -0
- package/dist/timeline/plan.js.map +1 -0
- package/dist/timeline/resolve.d.ts +60 -0
- package/dist/timeline/resolve.d.ts.map +1 -0
- package/dist/timeline/resolve.js +523 -0
- package/dist/timeline/resolve.js.map +1 -0
- package/dist/timeline/schema.d.ts +71 -0
- package/dist/timeline/schema.d.ts.map +1 -0
- package/dist/timeline/schema.js +425 -0
- package/dist/timeline/schema.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/adr/0002-make-frame-and-props-the-only-animation-state.md +2 -0
- package/docs/adr/0005-keep-parallel-rendering-opt-in-and-frame-ordered.md +2 -0
- package/docs/adr/0006-seek-embedded-media-from-the-frame-clock.md +4 -0
- package/docs/adr/0007-make-html-documents-the-composition-unit.md +1 -1
- 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/adr/0010-make-timelines-the-render-unit.md +24 -0
- package/docs/adr/0011-compose-timeline-frames-in-a-live-browser-host.md +22 -0
- package/docs/adr/0012-render-timelines-with-segmented-lossless-clip-intermediates.md +31 -0
- package/docs/v0.1-design.md +135 -85
- package/examples/demo/README.md +11 -4
- package/examples/demo/timelines/demo.timeline.json +12 -0
- package/examples/demo/video.config.js +4 -0
- package/examples/video/README.md +105 -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-overlay.html +350 -0
- package/examples/video/compositions/video.html +362 -0
- package/examples/video/props/video-timeline.json +10 -0
- package/examples/video/props/video.json +8 -0
- package/examples/video/scripts/make-clip.sh +109 -0
- package/examples/video/timelines/video-timeline-mp4.timeline.json +44 -0
- package/examples/video/timelines/video-timeline.timeline.json +44 -0
- package/examples/video/video.config.js +12 -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 +4 -2
- package/skills/frameyard/SKILL.md +18 -13
- package/skills/frameyard/references/json-results.md +2 -2
- package/skills/frameyard/references/timelines.md +141 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Frameyard video example
|
|
2
|
+
|
|
3
|
+
A 12-second, 1920×1080 project that treats a local clip as source footage. The
|
|
4
|
+
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
|
+
## Timeline with the video on a track
|
|
29
|
+
|
|
30
|
+
`video-timeline` and `video-timeline-mp4` move the footage out of the
|
|
31
|
+
composition and onto timeline tracks. The HTML is now a transparent overlay
|
|
32
|
+
above it:
|
|
33
|
+
|
|
34
|
+
- `v1` holds the hero clip: the whole 12-second source, audio at `0.85`
|
|
35
|
+
- `v2` holds the detail pass from source frame 174 (5.8 s) for 150 frames,
|
|
36
|
+
muted, placed in a 440×248 transform box in the bottom right
|
|
37
|
+
- `v3` holds `video-overlay`, an HTML-only composition with no `<video>`
|
|
38
|
+
elements and a transparent document background
|
|
39
|
+
|
|
40
|
+
`video-timeline` reads `assets/clip.webm` (VP9 + Opus) and
|
|
41
|
+
`video-timeline-mp4` reads `assets/clip.mp4` (H.264 + AAC). Both share the same
|
|
42
|
+
overlay composition and props file; the overlay clip sets its own `format` prop
|
|
43
|
+
so the header reports the codec of each timeline.
|
|
44
|
+
|
|
45
|
+
The detail clip uses `transform.width` and `transform.height` instead of
|
|
46
|
+
`scale`, with `fit: "cover"` filling that box. The box matches the overlay's
|
|
47
|
+
detail window exactly, so the video and its chrome stay aligned in production
|
|
48
|
+
and preview.
|
|
49
|
+
|
|
50
|
+
The overlay draws the scrim, typography, frame border, detail-window chrome,
|
|
51
|
+
progress bar, and outro fade. `video-overlay` declares its own frame animations,
|
|
52
|
+
so the title and footer still animate on the timeline clock. Production captures
|
|
53
|
+
the overlay with alpha and composites it over the video layers with FFmpeg; the
|
|
54
|
+
interactive timeline preview composites the same layers in Chromium.
|
|
55
|
+
|
|
56
|
+
Both timelines accept `props/video-timeline.json`, which overrides the overlay
|
|
57
|
+
composition's props. Timeline `--props` is keyed by composition clip id, so the
|
|
58
|
+
file targets the `overlay` clip.
|
|
59
|
+
|
|
60
|
+
Open the preview with `node dist/cli.js dev --project examples/video`; the
|
|
61
|
+
compositions and both timelines are listed.
|
|
62
|
+
|
|
63
|
+
The compositions live in `compositions/video.html` and
|
|
64
|
+
`compositions/video-mp4.html`; the overlay lives in
|
|
65
|
+
`compositions/video-overlay.html`, and the timelines in
|
|
66
|
+
`timelines/video-timeline.timeline.json` and
|
|
67
|
+
`timelines/video-timeline-mp4.timeline.json`. Both clips are 12-second 1280×720
|
|
68
|
+
files generated procedurally for this example; the MP4 is transcoded from the
|
|
69
|
+
WebM source. Replace either asset with a local, seekable video file that is at
|
|
70
|
+
least 12 seconds long; remote URLs and live streams are not supported.
|
|
71
|
+
|
|
72
|
+
Regenerate both clips from scratch with the bundled FFmpeg script:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
bash examples/video/scripts/make-clip.sh
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The script writes both files into `assets/`; pass `--out-dir <directory>` to
|
|
79
|
+
write elsewhere. It requires FFmpeg with `libvpx-vp9`, `libx264`, Opus, and AAC
|
|
80
|
+
encoders, and fixed noise seeds keep the generated grain stable across runs on
|
|
81
|
+
the same FFmpeg build.
|
|
82
|
+
|
|
83
|
+
From the repository root:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
pnpm build
|
|
87
|
+
node dist/cli.js check video --project examples/video
|
|
88
|
+
node dist/cli.js check video-mp4 --project examples/video
|
|
89
|
+
node dist/cli.js check video-timeline --project examples/video
|
|
90
|
+
node dist/cli.js check video-timeline-mp4 --project examples/video
|
|
91
|
+
node dist/cli.js still video --frame 210 --project examples/video --out video-still.png
|
|
92
|
+
node dist/cli.js still video-mp4 --frame 210 --project examples/video --out video-mp4-still.png
|
|
93
|
+
node dist/cli.js still video-timeline --frame 210 --project examples/video --out video-timeline-still.png
|
|
94
|
+
node dist/cli.js still video-timeline-mp4 --frame 210 --project examples/video --out video-timeline-mp4-still.png
|
|
95
|
+
node dist/cli.js render video --project examples/video --props examples/video/props/video.json
|
|
96
|
+
node dist/cli.js render video-mp4 --project examples/video --props examples/video/props/video.json
|
|
97
|
+
node dist/cli.js render video-timeline --project examples/video --props examples/video/props/video-timeline.json
|
|
98
|
+
node dist/cli.js render video-timeline-mp4 --project examples/video --props examples/video/props/video-timeline.json
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Start the interactive preview with:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
node dist/cli.js dev --project examples/video
|
|
105
|
+
```
|
|
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>
|