CODE · 5.6 KB
remotion/FailureThrillerPrototype.jsx
Workspace snapshot · 09/04 14:52
import React from "react";
import { AbsoluteFill, Easing, Img, Sequence, interpolate, staticFile, useCurrentFrame } from "remotion";
import hook from "../vendor/OpenMontage/projects/avs-first-party-video-lab-v1/assets/keyframes/01-hook-90-to-67-v1.png";
import suspects from "../vendor/OpenMontage/projects/avs-first-party-video-lab-v1/assets/keyframes/02-suspect-lineup-v1.png";
import sameHole from "../vendor/OpenMontage/projects/avs-first-party-video-lab-v1/assets/keyframes/02b-same-story-hole-v2.png";
import evidenceFirst from "../vendor/OpenMontage/projects/avs-first-party-video-lab-v1/assets/keyframes/03b-evidence-first-v1.png";
import drawer from "../vendor/OpenMontage/projects/avs-first-party-video-lab-v1/assets/keyframes/04-evidence-before-generation-v1.png";
const clamp = { extrapolateLeft: "clamp", extrapolateRight: "clamp" };
const ease = Easing.bezier(0.16, 1, 0.3, 1);
function FullFrame({ src, scale = [1, 1.04], x = [0, 0], y = [0, 0], rotate = [0, 0], duration }) {
const frame = useCurrentFrame();
const progress = interpolate(frame, [0, duration], [0, 1], clamp);
const zoom = scale[0] + (scale[1] - scale[0]) * progress;
const tx = x[0] + (x[1] - x[0]) * progress;
const ty = y[0] + (y[1] - y[0]) * progress;
const turn = rotate[0] + (rotate[1] - rotate[0]) * progress;
return <Img src={src} style={{ width: "100%", height: "100%", objectFit: "cover", transform: `translate(${tx}px, ${ty}px) scale(${zoom}) rotate(${turn}deg)` }} />;
}
function ScanLine({ start = 0, end = 60, from = 250, to = 1450, opacity = 1 }) {
const frame = useCurrentFrame();
const y = interpolate(frame, [start, end], [from, to], { ...clamp, easing: ease });
const alpha = interpolate(frame, [start, start + 4, end - 4, end], [0, opacity, opacity, 0], clamp);
return <div style={{ position: "absolute", left: -80, right: -80, top: y, height: 7, opacity: alpha, background: "#ff2519", boxShadow: "0 0 28px 8px rgba(255,37,25,.46)", transform: "rotate(-2deg)" }} />;
}
function CutFlash({ at = 0 }) {
const frame = useCurrentFrame();
const opacity = interpolate(frame, [at, at + 1, at + 4], [0, 0.82, 0], clamp);
return <AbsoluteFill style={{ background: "white", opacity }} />;
}
function Shot01() {
const frame = useCurrentFrame();
const press = interpolate(frame, [28, 42, 50], [0, 0, 1], { ...clamp, easing: ease });
const freeze = frame > 42 ? Math.sin(frame * 2.3) * 1.5 * (1 - press) : 0;
return <AbsoluteFill style={{ background: "#07111e", overflow: "hidden", transform: `translateX(${freeze}px)` }}>
<FullFrame src={hook} duration={66} scale={[1.06, 1.0]} y={[16, 0]} />
<ScanLine start={6} end={41} from={420} to={1060} />
<CutFlash at={42} />
<AbsoluteFill style={{ background: "rgba(0,0,0,.13)", opacity: press }} />
</AbsoluteFill>;
}
function Shot02() {
const frame = useCurrentFrame();
const sway = Math.sin(frame / 6) * 8 * Math.exp(-frame / 120);
return <AbsoluteFill style={{ background: "#f5f5f2", overflow: "hidden" }}>
<FullFrame src={suspects} duration={96} scale={[1.03, 1.0]} x={[sway, -sway]} />
<ScanLine start={12} end={82} from={640} to={1030} opacity={0.8} />
</AbsoluteFill>;
}
function Shot03() {
const frame = useCurrentFrame();
const snap = interpolate(frame, [0, 18, 34], [1.09, 1.09, 1], { ...clamp, easing: ease });
const pulse = interpolate(frame, [38, 48, 62], [1, 1.06, 1], clamp);
return <AbsoluteFill style={{ background: "#f5f5f2", overflow: "hidden" }}>
<Img src={sameHole} style={{ width: "100%", height: "100%", objectFit: "cover", transform: `scale(${snap * pulse})` }} />
<ScanLine start={22} end={74} from={820} to={940} opacity={0.9} />
</AbsoluteFill>;
}
function Shot04() {
const frame = useCurrentFrame();
const sourceFrame = Math.min(180, frame + 1);
const frameName = `frame-${String(sourceFrame).padStart(4, "0")}.jpg`;
return <AbsoluteFill style={{ background: "#f7f7f4", overflow: "hidden" }}>
<Img src={staticFile(`media/veo-s04-frames/${frameName}`)} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
</AbsoluteFill>;
}
function Shot05() {
const frame = useCurrentFrame();
const drop = interpolate(frame, [0, 22, 36], [-32, 4, 0], { ...clamp, easing: ease });
const settle = Math.sin(Math.max(0, frame - 22) / 4) * 2 * Math.exp(-Math.max(0, frame - 22) / 24);
return <AbsoluteFill style={{ background: "#f7f7f4", overflow: "hidden" }}>
<div style={{ width: "100%", height: "100%", transform: `translateY(${drop + settle}px) scale(1.035)` }}><FullFrame src={evidenceFirst} duration={108} scale={[1, 1.015]} /></div>
</AbsoluteFill>;
}
function Shot06() {
const frame = useCurrentFrame();
const slide = interpolate(frame, [0, 120], [42, -8], { ...clamp, easing: ease });
const dim = interpolate(frame, [132, 186], [0, 0.12], clamp);
return <AbsoluteFill style={{ background: "#f7f7f4", overflow: "hidden" }}>
<div style={{ width: "100%", height: "100%", transform: `translateY(${slide}px)` }}><FullFrame src={drawer} duration={186} scale={[1.025, 1.0]} /></div>
<AbsoluteFill style={{ background: "#061327", opacity: dim }} />
</AbsoluteFill>;
}
export function FailureThrillerPrototype() {
return <AbsoluteFill style={{ background: "#050b14" }}>
<Sequence from={0} durationInFrames={66}><Shot01 /></Sequence>
<Sequence from={66} durationInFrames={96}><Shot02 /></Sequence>
<Sequence from={162} durationInFrames={96}><Shot03 /></Sequence>
<Sequence from={258} durationInFrames={174}><Shot04 /></Sequence>
<Sequence from={432} durationInFrames={108}><Shot05 /></Sequence>
<Sequence from={540} durationInFrames={240}><Shot06 /></Sequence>
</AbsoluteFill>;
}