CODE · 8.6 KB
remotion/EightSecondGate.jsx
Workspace snapshot · 09/04 14:52
import React from "react";
import { AbsoluteFill, Audio, Img, Sequence, interpolate, staticFile, useCurrentFrame } from "remotion";
const clamp = { extrapolateLeft: "clamp", extrapolateRight: "clamp" };
const ink = "#101114";
const red = "#ff321f";
const blue = "#0f5cff";
const fit = { width: "100%", height: "100%", objectFit: "cover" };
function CutImage({ src, scale = 1.04, x = 0, y = 0, dim = 0 }) {
const frame = useCurrentFrame();
const zoom = interpolate(frame, [0, 18], [scale, scale + 0.025], clamp);
return <AbsoluteFill style={{ background: ink, overflow: "hidden" }}>
<Img src={staticFile(src)} style={{ ...fit, transform: `translate(${x}px, ${y}px) scale(${zoom})` }} />
{dim > 0 && <AbsoluteFill style={{ background: `rgba(0,0,0,${dim})` }} />}
</AbsoluteFill>;
}
function MessageColdOpen() {
const frame = useCurrentFrame();
const y = interpolate(frame, [0, 7], [70, 0], clamp);
const scale = interpolate(frame, [17, 36], [1, 1.12], clamp);
return <AbsoluteFill style={{ background: "#08090b", color: "white", fontFamily: "'Yu Gothic UI', sans-serif", padding: "118px 64px" }}>
<div style={{ display: "flex", justifyContent: "space-between", fontSize: 24, color: "#858b96", letterSpacing: 2 }}>
<span>00:47</span><span>AGENT VENTURE STUDIO</span>
</div>
<div style={{ marginTop: 280, transform: `translateY(${y}px) scale(${scale})`, transformOrigin: "right center" }}>
<div style={{ marginLeft: 78, background: "#22252b", border: "1px solid #383c44", borderRadius: "30px 30px 8px 30px", padding: "38px 42px", fontSize: 43, lineHeight: 1.6, fontWeight: 700, boxShadow: "0 28px 70px rgba(0,0,0,.45)" }}>
寝るから進められるところまで進めて。<br />動画も生成していいよ。
</div>
<div style={{ textAlign: "right", marginTop: 16, color: "#7f8590", fontSize: 22 }}>植木拓海</div>
</div>
<div style={{ position: "absolute", left: 64, bottom: 100, width: 10, height: 10, borderRadius: 99, background: red, boxShadow: `0 0 25px ${red}` }} />
</AbsoluteFill>;
}
function BudgetStrike() {
const frame = useCurrentFrame();
const amount = Math.round(interpolate(frame, [0, 10], [0, 50000], clamp));
const slash = interpolate(frame, [11, 18], [-110, 1100], clamp);
return <AbsoluteFill style={{ background: "#f4f3ef", color: ink, fontFamily: "Arial, sans-serif", justifyContent: "center", alignItems: "center" }}>
<div style={{ fontSize: 25, letterSpacing: 8, marginBottom: 24 }}>EXPERIMENT BUDGET</div>
<div style={{ fontSize: 154, fontWeight: 900, letterSpacing: -10 }}>¥{amount.toLocaleString("ja-JP")}</div>
<div style={{ width: 740, height: 2, background: "#b8b7b2", marginTop: 28 }} />
<div style={{ fontSize: 31, marginTop: 24, fontWeight: 700 }}>一晩で、何に変える?</div>
<div style={{ position: "absolute", top: 1040, left: slash, width: 170, height: 9, background: red, transform: "rotate(-7deg)", boxShadow: `0 0 16px ${red}` }} />
</AbsoluteFill>;
}
function FeedbackHit({ text, index }) {
const frame = useCurrentFrame();
const x = interpolate(frame, [0, 2], [index % 2 === 0 ? -950 : 950, 0], clamp);
return <AbsoluteFill style={{ background: index === 2 ? red : "#0b0c0e", color: "white", justifyContent: "center", padding: "0 64px", fontFamily: "'Yu Gothic UI', sans-serif" }}>
<div style={{ transform: `translateX(${x}px)`, fontSize: index === 2 ? 104 : 86, lineHeight: 1.08, fontWeight: 900, letterSpacing: -4 }}>{text}</div>
<div style={{ position: "absolute", left: 64, bottom: 86, fontSize: 22, letterSpacing: 3, opacity: .6 }}>ACTUAL FEEDBACK / 01</div>
</AbsoluteFill>;
}
function AssetFlash({ src, label }) {
return <AbsoluteFill style={{ background: "white", overflow: "hidden" }}>
<Img src={staticFile(src)} style={fit} />
<div style={{ position: "absolute", left: 28, bottom: 28, background: "rgba(0,0,0,.8)", color: "white", padding: "12px 18px", font: "700 22px Arial", letterSpacing: 2 }}>{label}</div>
</AbsoluteFill>;
}
function ProviderComparison() {
const frame = useCurrentFrame();
const klingIndex = Math.min(121, Math.floor(frame * 120 / 54) + 1);
const veoIndex = Math.min(180, Math.floor(frame * 179 / 54) + 1);
const file = (index) => `frame-${String(index).padStart(4, "0")}.jpg`;
return <AbsoluteFill style={{ background: ink, color: "white", fontFamily: "Arial, sans-serif" }}>
<div style={{ display: "flex", height: "100%" }}>
<div style={{ position: "relative", width: "50%", overflow: "hidden", borderRight: "4px solid white" }}>
<Img src={staticFile(`media/kling-s04-frames/${file(klingIndex)}`)} style={fit} />
<div style={{ position: "absolute", top: 48, left: 34, background: "#111", padding: "12px 18px", fontSize: 28, fontWeight: 800 }}>KLING</div>
</div>
<div style={{ position: "relative", width: "50%", overflow: "hidden" }}>
<Img src={staticFile(`media/veo-s04-frames/${file(veoIndex)}`)} style={fit} />
<div style={{ position: "absolute", top: 48, left: 34, background: blue, padding: "12px 18px", fontSize: 28, fontWeight: 800 }}>VEO</div>
</div>
</div>
<div style={{ position: "absolute", left: 54, right: 54, bottom: 58, display: "flex", justifyContent: "space-between", fontSize: 26, fontWeight: 800, textShadow: "0 2px 12px #000" }}><span>文字保持</span><span>物理動作</span></div>
</AbsoluteFill>;
}
function ResultCard() {
const frame = useCurrentFrame();
const x = interpolate(frame, [0, 7], [900, 0], clamp);
return <AbsoluteFill style={{ background: "#f5f4f0", color: ink, fontFamily: "'Yu Gothic UI', sans-serif", padding: "140px 68px" }}>
<div style={{ fontSize: 24, letterSpacing: 5, color: "#777" }}>AFTER ONE CONTROLLED TEST</div>
<div style={{ marginTop: 130, fontSize: 132, fontWeight: 900, letterSpacing: -8 }}>$1.16</div>
<div style={{ fontSize: 30, marginTop: 8 }}>推定使用額</div>
<div style={{ marginTop: 100, transform: `translateX(${x}px)`, borderTop: `8px solid ${red}`, paddingTop: 34, fontSize: 54, lineHeight: 1.35, fontWeight: 900 }}>
最強モデル探しをやめる。<br /><span style={{ color: blue }}>カットごとに、配役する。</span>
</div>
</AbsoluteFill>;
}
function LatestFeedback() {
const frame = useCurrentFrame();
const white = interpolate(frame, [24, 29], [0, 1], clamp);
return <AbsoluteFill style={{ background: "#07080a", color: "white", fontFamily: "'Yu Gothic UI', sans-serif", justifyContent: "center", padding: 70 }}>
<div style={{ fontSize: 37, color: "#999", marginBottom: 34 }}>LATEST FEEDBACK</div>
<div style={{ fontSize: 78, fontWeight: 900, lineHeight: 1.2 }}>素材は良くなった。<br /><span style={{ color: red }}>でも、構成が論外。</span></div>
<AbsoluteFill style={{ background: "white", opacity: white }} />
</AbsoluteFill>;
}
export function EightSecondGate() {
return <AbsoluteFill style={{ background: "black" }}>
<Audio src={staticFile("pengin-editorial-beat.wav")} volume={0.34} />
<Sequence from={0} durationInFrames={36}><MessageColdOpen /></Sequence>
<Sequence from={36} durationInFrames={18}><BudgetStrike /></Sequence>
<Sequence from={54} durationInFrames={8}><CutImage src="evidence/scenes/scene-02.png" scale={1.16} /></Sequence>
<Sequence from={62} durationInFrames={8}><CutImage src="evidence/reset-v1/process-film-review-v1/frame-2_0.png" scale={1.1} /></Sequence>
<Sequence from={70} durationInFrames={8}><CutImage src="evidence/reset-v1/process-film-review-v3/contact-sheet.png" scale={1.04} /></Sequence>
<Sequence from={78} durationInFrames={10}><FeedbackHit text="6秒、1シーン。" index={0} /></Sequence>
<Sequence from={88} durationInFrames={10}><FeedbackHit text="ほぼ、動かない。" index={1} /></Sequence>
<Sequence from={98} durationInFrames={10}><FeedbackHit text="比較できない。" index={2} /></Sequence>
<Sequence from={108} durationInFrames={6}><AssetFlash src="evidence/storyboard-assets/shoot-06.jpg" label="PHOTO" /></Sequence>
<Sequence from={114} durationInFrames={6}><AssetFlash src="evidence/inputs/shot-02-conversation.jpg" label="CULTURE" /></Sequence>
<Sequence from={120} durationInFrames={6}><AssetFlash src="evidence/storyboard-assets/wireframe-overview.png" label="DRIVE" /></Sequence>
<Sequence from={126} durationInFrames={6}><AssetFlash src="evidence/storyboard-assets/design-directions.png" label="PROPOSAL" /></Sequence>
<Sequence from={132} durationInFrames={54}><ProviderComparison /></Sequence>
<Sequence from={186} durationInFrames={24}><ResultCard /></Sequence>
<Sequence from={210} durationInFrames={30}><LatestFeedback /></Sequence>
</AbsoluteFill>;
}