File size: 2,235 Bytes
34b0226
00a4363
 
34b0226
00a4363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34b0226
00a4363
 
 
34b0226
 
 
 
 
00a4363
34b0226
00a4363
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import { AbsoluteFill, Audio, Sequence, spring, useCurrentFrame, useVideoConfig } from "remotion";
import { FPS, type ShortVerticalProps } from "../schema";
import { theme } from "../theme";
import { Captions, Logo, ProgressBar, SceneCard } from "./shared";
import { Starfield } from "./Starfield";

const HOOK_S = 2; // hook lands in the first 2 seconds

export function shortVerticalDuration(props: ShortVerticalProps): number {
  return Math.round((HOOK_S + props.scenes.reduce((s, sc) => s + sc.durationS, 0)) * FPS);
}

function Hook({ text, accent }: { text: string; accent: string }) {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const s = spring({ frame, fps, config: { damping: 14 } });
  return (
    <AbsoluteFill style={{ justifyContent: "center", alignItems: "center", padding: 80 }}>
      <h1
        style={{
          fontFamily: theme.display,
          fontSize: 92,
          lineHeight: 1.05,
          color: theme.white,
          textAlign: "center",
          transform: `scale(${0.85 + s * 0.15})`,
          opacity: s,
          textShadow: `0 0 40px ${accent}aa`,
        }}
      >
        {text}
      </h1>
    </AbsoluteFill>
  );
}

export function ShortVertical(props: ShortVerticalProps) {
  const accent = props.brand.accent ?? theme.blue;
  let cursor = HOOK_S * FPS;
  return (
    <AbsoluteFill>
      <Starfield accent={accent} />
      <Sequence durationInFrames={HOOK_S * FPS}>
        <Hook text={props.hook} accent={accent} />
      </Sequence>
      {props.scenes.map((scene, i) => {
        const from = cursor;
        const dur = Math.round(scene.durationS * FPS);
        cursor += dur;
        return (
          <Sequence key={i} from={from} durationInFrames={dur}>
            <SceneCard scene={scene} accent={accent} />
          </Sequence>
        );
      })}
      {/* Narration and caption timings start with the first scene, after the hook. */}
      <Sequence from={HOOK_S * FPS}>
        {props.audioUrl && <Audio src={props.audioUrl} />}
        <Captions words={props.captions} accent={accent} />
      </Sequence>
      <ProgressBar accent={accent} />
      <Logo url={props.brand.logoUrl} size={96} inset={70} />
    </AbsoluteFill>
  );
}