애니메이션 로어 서드
이 레시피를 마치면 어떤 클립 위에든 애니메이션 로어 서드를 얹을 수 있습니다. 강조 막대와 이름, 직함이 들어간 카드가 미끄러져 들어와 잠시 머문 뒤 다시 빠져나갑니다. 카드는 레이아웃 제너레이터이므로 상자가 텍스트에 맞춰 커지고, 같은 문서가 브라우저와 네이티브 export에서 똑같은 카드를 그립니다.
프로젝트
섹션 제목: “프로젝트”{ "schema_version": 1, "id": "lower-third", "name": "Lower third", "settings": { "width": 1280, "height": 720, "frame_rate": { "num": 30, "den": 1 }, "sample_rate": 48000 }, "sources": [ { "id": "platform", "locator": "asset://platform", "kind": "image" }, { "id": "third", "kind": "generated", "generator": { "type": "layout", "root": { "flex_direction": "column", "justify_content": "end", "align_items": "start", "padding": { "mode": "constant", "value": { "top": 0.06, "right": 0.06, "bottom": 0.08, "left": 0.06 } }, "font_family": "Hanken Grotesk", "children": [ { "flex_direction": "row", "gap": { "mode": "constant", "value": 0.016 }, "padding": { "mode": "constant", "value": { "top": 0.022, "right": 0.03, "bottom": 0.022, "left": 0.022 } }, "background": { "mode": "constant", "value": { "type": "solid", "color": { "red": 0.04, "green": 0.05, "blue": 0.09, "alpha": 0.9 } } }, "border_radius": { "mode": "constant", "value": 0.012 }, "max_width": { "mode": "constant", "value": "60%" }, "opacity": { "mode": "keyframes", "keyframes": [ { "time": 0, "value": 0, "interpolation": { "type": "cubic_bezier", "x1": 0.2, "y1": 0, "x2": 0, "y2": 1 } }, { "time": 60000, "value": 1, "interpolation": { "type": "hold" } }, { "time": 408000, "value": 1, "interpolation": { "type": "cubic_bezier", "x1": 0.4, "y1": 0, "x2": 1, "y2": 1 } }, { "time": 468000, "value": 0, "interpolation": { "type": "hold" } } ] }, "translate": { "mode": "keyframes", "keyframes": [ { "time": 0, "value": { "x": -0.04, "y": 0 }, "interpolation": { "type": "cubic_bezier", "x1": 0.2, "y1": 0, "x2": 0, "y2": 1 } }, { "time": 60000, "value": { "x": 0, "y": 0 }, "interpolation": { "type": "hold" } }, { "time": 408000, "value": { "x": 0, "y": 0 }, "interpolation": { "type": "cubic_bezier", "x1": 0.4, "y1": 0, "x2": 1, "y2": 1 } }, { "time": 468000, "value": { "x": -0.04, "y": 0 }, "interpolation": { "type": "hold" } } ] }, "children": [ { "width": { "mode": "constant", "value": 0.01 }, "flex_shrink": { "mode": "constant", "value": 0 }, "background": { "mode": "constant", "value": { "type": "solid", "color": { "red": 0.17, "green": 0.36, "blue": 0.99, "alpha": 1 } } }, "border_radius": { "mode": "constant", "value": 0.005 } }, { "flex_direction": "column", "gap": { "mode": "constant", "value": 0.006 }, "children": [ { "text": { "mode": "constant", "value": "Ada Lovelace" }, "font_size": { "mode": "constant", "value": 0.07 } }, { "text": { "mode": "constant", "value": "Wrote the first published algorithm, 1843" }, "font_size": { "mode": "constant", "value": 0.04 }, "fill": { "red": 0.8, "green": 0.82, "blue": 0.86, "alpha": 1 } } ] } ] } ] } } } ], "timeline": { "tracks": [ { "id": "back", "name": "Picture", "kind": "video", "clips": [ { "id": "photo", "source_id": "platform", "timeline_range": { "start": 0, "end": 480000 }, "source_range": { "start": 0, "end": 480000 } } ], "transitions": [] }, { "id": "titles", "name": "Lower third", "kind": "video", "clips": [ { "id": "third-clip", "source_id": "third", "timeline_range": { "start": 0, "end": 480000 }, "source_range": { "start": 0, "end": 480000 } } ], "transitions": [] } ] }}동작 방식
섹션 제목: “동작 방식”이 프로젝트에는 소스 두 개와 트랙 두 개가 있습니다. 사진은 back 트랙에 있고, 로어 서드는 그 위의 titles 트랙에 놓인 생성 소스이므로 사진 위에 그려집니다.
제너레이터. third 소스는 kind: "generated"이고 layout 제너레이터를 씁니다. 레이아웃이 그리는 그림은 캔버스 크기이며, root 상자가 곧 그 그림입니다. 루트는 CSS flexbox로 내용을 배치합니다. flex_direction: "column"에 justify_content: "end"를 주면 카드가 아래쪽에 놓이고, align_items: "start"는 카드를 왼쪽에 붙입니다. 루트의 padding은 카드를 가장자리에서 띄웁니다. 길이는 모두 그림의 짧은 변에 대한 비율이므로, 0.06은 16:9 캔버스에서는 높이의 6%, 9:16 캔버스에서는 너비의 6%입니다.
카드. 루트의 유일한 자식은 행(flex_direction: "row")으로, gap, 자체 padding, 반투명 background, border_radius를 가집니다. max_width: "60%"는 직함이 길어도 화면을 가로지르지 않게 막습니다. 대신 텍스트가 줄바꿈되고 카드가 세로로 커집니다.
강조 막대와 텍스트. 행 안에는 파란 배경의 너비 0.01짜리 상자(막대)와 텍스트 상자 두 개가 든 열이 있습니다. flex_shrink: 0은 텍스트가 길어도 막대가 눌리지 않게 합니다. 막대에는 높이가 따로 없습니다. CSS의 flex 아이템처럼 행의 높이만큼 늘어납니다. font_family는 루트에 지정되어 두 텍스트가 상속합니다. 직함은 자체 font_size와 회색 fill을 지정합니다.
애니메이션. 카드의 opacity와 translate는 레이아웃이 끝난 뒤 적용되므로, 다른 상자를 밀지 않고 카드만 움직입니다. 둘 다 키프레임 목록입니다. 키프레임의 interpolation은 그 키프레임에서 시작하는 구간의 모양을 정합니다.
| 시간(틱) | 초 | 불투명도 | 이동 | 보간 |
|---|---|---|---|---|
| 0 | 0 | 0 | x −0.04 | ease out (cubic_bezier 0.2, 0, 0, 1) |
| 60000 | 0.5 | 1 | 0 | 다음 키까지 hold |
| 408000 | 3.4 | 1 | 0 | ease in (cubic_bezier 0.4, 0, 1, 1) |
| 468000 | 3.9 | 0 | x −0.04 | hold |
시간은 틱(tick) 단위이며 1초가 120,000틱입니다. 제너레이터의 키프레임은 그림 시간, 즉 클립이 보여 주는 소스 시간 기준입니다. 이 클립은 0에서 시작해 소스를 0부터 읽으므로 여기서는 그림 시간과 타임라인 시간이 같습니다. 클립을 10초로 옮겨도 카드는 클립이 시작할 때 들어옵니다.
export하기
섹션 제목: “export하기”사진 소스의 id가 platform이므로 --asset으로 그 파일을 지정합니다. 텍스트는 넘겨준 폰트 파일로 그려집니다. 프리뷰와 같은 폰트를 넘겨야 줄바꿈 위치도 같아집니다.
alpha2-host export --project lower-third.json \ --asset platform=platform.jpg \ --font HankenGrotesk.ttf \ --out lower-third.mp4import { readFile } from "node:fs/promises";import { exportProject } from "@alpha2-sh/node";
const project = JSON.parse(await readFile("lower-third.json", "utf8"));await exportProject({ project, assets: { platform: "platform.jpg" }, fonts: ["HankenGrotesk.ttf"], startTicks: 0, endTicks: 480_000, // 4초 frameRate: { num: 30, den: 1 }, output: "lower-third.mp4",});CLI는 --end-ticks가 없으면 타임라인 전체를 export합니다. @alpha2-sh/node에는 범위를 지정해야 합니다.
