설명 애니메이션
비디오는 src/timeline.ts에 있는 장면들의 순서 목록입니다. 세 가지 종류가 있습니다:
intro: 훅(hook) 카드입니다. 첫 번째 줄은 훅이며, 크게 표시되고 단어가 음성에 맞춰 강조됩니다(자체 자막 역할을 합니다).anim: 레지스트리에 있는 하나의 애니메이션 컴포넌트로, 장면의 내레이션 라인과 동기화됩니다.outro: AGILabs 배지,javieraguilar.ai및SETTINGS.articleTitle의 기사 제목이 포함된 종료 카드입니다.
타임라인 작성하기
import type { Cuts } from './types.ts';
import { L, anim, intro, outro, say, videos } from './narrate.ts';
const build = (): Cuts => ({
main: [
intro('hook', [say(0.4, 'EN hook line.', 'ES hook line.')]),
anim(
'result',
'BarCompare',
{ bars: [{ label: L('이유 있음', 'Con el porqué'), value: 14, display: '14/20', highlight: true },
{ label: L('이유 없음', 'Sin él'), value: 4, display: '4/20' }], max: 20 },
[say(0.3, '첫 번째 줄.', 'Primera línea.'), say(0, '두 번째 줄.', 'Segunda línea.')],
{ min: 4 },
),
outro('outro', [say(0.4, 'javieraguilar.ai에서 더 보기.', 'Más en javieraguilar.ai.')]),
],
});
export const VIDEOS = videos(build);
export const TIMELINE = VIDEOS.en.main;
say(at, en, es, spoken?): 하나의 내레이션 라인입니다.at은 해당 라인이 시작될 수 있는 가장 빠른 장면 시작 시간(초)입니다. 라인은 절대 겹치지 않으며(0.35초 간격), 장면은 마지막 라인이 끝난 후 0.8초 또는min값 중 더 긴 시간까지 지속됩니다.spoken({ en?, es? })은 TTS가 자막 텍스트와 다르게 읽어야 할 때 사용하는 값입니다(narration참조).- 비트(Beats). 장면 내 각 라인의 시작 시간을 비트라고 합니다. 컴포넌트는 비트 i에서 단계 i를 재생하므로(막대 i가 성장하고, 노드 i가 나타나고, 타일 i가 튀어나옴), 공개하려는 단계당 하나의 라인을 작성하세요. TTS 이전에는 비트가 길이 추정치에서 나오지만,
npm run tts이후에는 실제 오디오에서 비트가 생성되므로 내레이션 후에 스틸을 다시 확인하세요. L(en, es): 이중 언어 화면 레이블입니다. 일반 문자열은 두 언어 모두에 사용됩니다.- 장면 ID는 고유해야 합니다. 라인 키(
<scene>-01, ...)는 오디오 파일의 이름을 지정합니다. anim의opts.chapter는 장면에 저장되지만 아직 어떤 컴포넌트에서도 그려지지 않습니다.- 변경 후에는 항상
npm run lint를 실행하세요.targetSeconds대비 길이(±20 %), 0.8초 미만 또는 초당 17자를 초과하는 자막 큐, 레지스트리에서 누락된 컴포넌트 이름을 확인합니다.
컴포넌트 카탈로그
모든 컴포넌트는 장면에서 t, beats, dur, lang, vertical을 받습니다. 아래 props는 타임라인에서 가져옵니다. 화면에 표시되는 숫자는 기사의 정확한 형식을 사용합니다. 기사에서 "14/20", "0.0018" 또는 "83 %"라고 쓰여 있다면 계산된 텍스트보다 display / 문자열 value를 선호하세요.
| 컴포넌트 | 용도 | Props |
|---|---|---|
Counter | 하나의 헤드라인 숫자가 카운트업되고 레이블이 아래에 표시됩니다 | value: number, label: Label, prefix?, suffix?, decimals? (0-8), beat? (카운트가 시작되는 비트 인덱스, 기본값 0) |
BarCompare | 2-5개의 가로 막대가 비트당 하나씩 성장하며, 강조된 막대는 액센트 그라데이션으로 표시됩니다 | bars: { label: Label; value: number; display?: string; highlight?: boolean; at?: number }[], max? (스케일 상한, 기본값은 가장 큰 값), unit? (display가 없을 때 추가됨) |
FlowDiagram | 상자와 화살표가 순서대로 나타납니다. broken 엣지는 점선으로 표시되고 흐리게 처리되며 간격이 있습니다 | nodes: { id; label: Label; at?: number }[], edges: { from; to; label?: Label; broken?: boolean }[], layout: 'vertical' | 'horizontal' (9:16 컷에는 vertical 사용) |
KeyQuote | 핵심 문장이 단어별로 표시되며, 강조된 단어는 액센트 색상과 밑줄로 표시됩니다 | text: Label, emphasis?: string[] (단어 또는 구절, 대소문자 및 구두점 무시하고 일치), beat? |
StatGrid | 2-4개의 숫자 타일이 비트당 하나씩 튀어나옵니다 | stats: { value: string; label: Label; at?: number }[] |
막대, 노드 또는 타일의 at은 자체 비트 대신 장면 시작 시간(초)에 고정됩니다. 비트나 at이 없는 단계는 이전 단계 이후 0.6초 후에 진행됩니다. 모든 컴포넌트는 장면이 끝날 때까지 완료되도록 맞춰집니다. 마지막 프레임은 항상 완료된 상태를 표시합니다.
전체 라이브러리를 보려면: npm run studio를 실행하고 Gallery-vertical을 열거나 스틸을 렌더링하세요.
맞춤형 컴포넌트 추가하기
스크립트가 장면을 "bespoke"로 표시한 경우:
- 상태(state)를 먼저, 테스트와 함께. props 타입과 순수
frameState(props, t, beats, dur)함수(일반 숫자와 문자열 반환)를 포함한src/anim/<Name>.state.ts를 작성하세요. 거기에는 Remotion이나 React 임포트가 없어야 합니다.src/anim/steps.ts(sceneTime,stepTimes,fit,progress,beatAt,easeOutCubic)를 기반으로 구축하여 완전성을 확보하세요. 모든t에서 유한한 값, [0, 1] 범위의 진행 필드, 첫 번째 비트 이전에는 아무것도 표시되지 않음,t = dur에서의 최종 상태. test/anim.test.ts의CASES에 케이스를 추가하고(자체 동작 테스트 포함),npm test를 실행하세요.t < 0, 모든 비트,dur,dur이후를 확인하며, 누락되거나 늦거나 매우 짧은 비트도 테스트합니다.- 그리기
src/anim/<Name>.tsx에서:frameState를 호출하고,<Stage vertical={...}>내부에 그리고,src/brand.ts의 브랜드 토큰(C,GRADIENT,alpha)과 Inter를 사용하세요. 새로운 색상이나 글꼴은 안 됩니다. - 등록하기
src/anim/index.ts의ANIMS에 추가하세요.ANIMS는 이름의 일반 객체 리터럴로 유지하세요.npm run lint가 소스에서 읽습니다. - 선택적으로
src/Gallery.tsx에 샘플을 추가하세요. 재사용 가능하다면 플러그인의 템플릿에 다시 속합니다.
생성된 이미지. 대부분의 장면은 코드(SVG 및 브랜드 토큰의 CSS)로 그려야 합니다. 장면에 생성된 이미지(배경, 기본 이미지, 질감이 있는 순간)가 정말로 필요한 경우, 이미지 모델(예: Codex CLI의 이미지 생성)이 만들 수 있습니다. public/ 아래에 저장하고, staticFile()로 로드하고, 브랜드 팔레트 내에 유지하고, 스틸을 확인하세요.
세로형 안전 영역 (1080x1920)
- 플랫폼 UI는 상단 약 220px, 하단 약 380px를 덮습니다. 측면은 72px를 유지하세요.
- 애니메이션은 스테이지 내에서만 그려집니다: y = 280(AGILabs 배지 아래)에서 y = 1280(두 줄의 자막 위)까지, 너비 936px.
Stage/stageOf()가 해당 박스를 제공합니다. - 자막은 스테이지 아래에 위치하며, 최대 두 줄, 약 32자, 너비 약 900px 이내입니다.
스틸: 확인하세요
비디오를 렌더링하기 전에 스틸을 렌더링하고 모든 PNG를 읽으세요. 깔끔한 린트나 빌드는 오버플로우, 잘린 텍스트 또는 잘못된 숫자를 보여주지 않습니다.
npx remotion still src/index.ts Main-EN-vertical out/stills/f0150.png --frame=150
각 장면의 마지막 단계가 재생된 후 한 장의 스틸을 찍고, 약 3초마다(30fps에서 90프레임마다) 한 장씩 더 찍으세요. 확인할 사항: AGILabs 배지와 그 외 브랜딩 없음, 안전 영역 밖으로 나간 것 없음, 텍스트 상자 넘침 없음, 모든 숫자가 기사와 일치함, 화면의 자막이 해당 순간의 애니메이션과 일치함.