브라우저 탭에서 작동하는 모션 그래픽 편집기 제작 및 AI 애니메이션 적용
요약
브라우저 탭에서 작동하는 모션 그래픽 편집기 'Riptide'가 소개되었습니다. 이 도구는 순수 JavaScript와 단일 <canvas> 요소를 사용하여 레이어, 키프레임, 타임라인 등 전문적인 기능을 구현했습니다. MP4, WebM, GIF 등 다양한 포맷으로 4K 해상도 내보내기가 가능합니다.
핵심 포인트
- 순수 JavaScript와 단일 <canvas> 요소로 전체 편집기 구축.
- 레이어, 키프레임, 타임라인 등 전문적인 모션 그래픽 기능 제공.
- MP4, WebM, GIF 등 다양한 포맷으로 4K 해상도 내보내기 지원.
- 백엔드는 Flask 서버와 ffmpeg를 활용하여 오디오 및 페이드 효과 처리.
15초짜리 애니메이션 클립이 필요할 때마다 저는 두 가지 선택지 중 하나에 놓였습니다. 한 달에 두 번밖에 사용하지 않을 모션 그래픽 스위트(motion graphics suite)를 구매하거나, 워터마크가 삽입되는 템플릿 사이트와 씨름하는 것이었습니다.
그래서 제가 직접 만들었습니다. 이름은 Riptide이며, 브라우저 탭에서 실행됩니다.
이것은 장난감이 아닙니다. 이 편집기로 만든 홍보용 쇼츠(Short)가 있습니다. 여기에 무엇을 할 수 있는지, 어떻게 구축되었는지, 그리고 저에게 가장 많은 것을 가르쳐준 버그에 대해 알려드리겠습니다.
기능 (What it does)
레이어와 키프레임(keyframes). 사각형, 타원, 다각형, 별, 호, 파도, 텍스트, 이미지 및 비디오. 위치(position), 크기(size), 회전(rotation), 스케일(scale), 불투명도(opacity), 색상(colour), 블러(blur), 스트로크(stroke) 등 모든 속성에 키프레임을 적용할 수 있습니다. 23가지 이징 커브(easing curves)와 더불어 Pop in, Rise, Typewriter, Draw on 같은 사용 준비가 된 프리셋이 있어 레이어에 드래그하여 사용할 수 있습니다.
실제 타임라인(timeline). 확대/축소 기능, 속성별 키프레임 행(per-property keyframe rows), 트림 바(trim bars), 재생 헤드에서 분할(split at the playhead), 마커(markers), 그리고 재생 헤드, 마커, 키프레임 및 클립 가장자리에 스냅되는 기능이 있습니다.
작동하는 오디오. 함께 재생되는 여러 트랙을 지원합니다. 클립을 드래그하고, 자르고, 분할하며, 클립 모서리의 작은 점들을 드래그하여 페이드 인/아웃(fade in and out) 효과를 줄 수 있습니다. 모든 트랙은 내보내기 시 믹싱되고, 페이드 및 볼륨이 포함됩니다.
예상되는 디자인 도구. Shift-클릭 또는 선택 상자를 이용한 다중 선택(multi-select), 정렬 및 분배(align and distribute), 캔버스 중앙 맞춤(centre on canvas), 그리고 이미지가 기본적으로 가지는 종횡비 잠금(aspect-ratio lock) 기능이 있어 실수로 사진을 왜곡하는 일이 없습니다.
내보내기(Export). MP4, WebM, GIF, PNG 시퀀스 또는 정지 이미지까지 4K 해상도에서 지원합니다.
구축 방식 (How it's built)
전체 편집기는 순수 자바스크립트(plain JavaScript)를 사용하여 단일 <canvas> 요소의 2D 컨텍스트에 그립니다. 프레임워크는 사용하지 않았고, WebGL도 사용하지 않았습니다.
모든 프레임은 하나의 함수로 처음부터 렌더링됩니다:
function renderScene(c, t, scale = 1) {
c.setTransform(scale, 0, 0, scale, 0, 0);
c.fillStyle = S.bg; c.fillRect(0, 0, S.w, S.h);
...
}
이 하나의 결정이 반복적으로 빛을 발했습니다. 편집기 미리보기, 전체 화면 뷰어, 내보내기 기능, 그리고 AI 에이전트가 보는 프레임들 모두 동일한 함수를 다른 컨텍스트와 스케일로 호출합니다. 만약 편집기에서 제대로 보인다면, 내보내기에서도 제대로 작동하는 것입니다.
백엔드는 프레임을 ffmpeg에 전달하는 것이 주 임무인 작은 Flask 서버입니다. 브라우저가 각 프레임을 렌더링하여 업로드하고, 그런 다음 서버는 오디오 트랙과 페이드 효과를 혼합하여 해당 프레임들을 ffmpeg에 전달합니다. PNG 시퀀스 내보내기(export)에는 심지어 ffmpeg가 필요하지 않습니다.
키보드 단축키는 하나의 테이블에 있습니다:
['Align', 'Centre on canvas', 'alt+c', () => centerOnCanvas('xy')],
['Edit', 'Select all layers', 'mod+a', () => selectAll()],
이 테이블이 단축키, 치트 시트(cheat sheet), 그리고 명령 팔레트를 구동하므로, 세 가지 요소가 서로 모순될 일이 없습니다.
사람들이 궁금해하는 부분: 실시간 애니메이션을 구현하는 AI
Riptide에는 내장된 MCP 서버가 있습니다. MCP를 사용하는 모든 에이전트는 열린 프로젝트에 연결하여 작업할 수 있습니다. 레이어 추가, 키프레임 설정, 프리셋 적용, 정렬, 오디오 추가, 그리고 자신의 작업을 확인하기 위한 프레임 렌더링 등이 가능합니다.
중요한 디자인 선택: 에이전트의 편집은 사용자의 편집과 정확히 같은 경로를 거칩니다. 캔버스에 즉시 나타나며, Cmd+Z로 되돌릴 수 있고, 파일에는 수정되었다는 표시가 남습니다. 이 에이전트는 나중에 가져와서(import) 사용할 파일을 생성하는 것이 아닙니다. 그것은 동일한 문서 내에서 작동하는 두 번째 손과 같습니다.
에이전트에게 "제목을 비트에 맞춰 나타나게 하고 모든 것을 중앙에 배치해라"라고 요청하고 그것이 화면에서 일어나는 것을 보는 것은 제가 만들어 본 것 중 가장 마법과 가까운 경험입니다.
전체 창을 망가뜨린 버그
이 프로젝트에서 가장 좋았던 버그는 단어 하나로 이루어져 있었습니다.
레이어(Layers) 패널과 미디어(Media) 패널 사이의 구분선을 드래그하면 전체 창이 정상적인 레이아웃과 뒤죽박죽된 레이아웃 사이에서 깜빡였습니다. 저는 먼저 명백한 용의자인 피드백 루프를 수정했습니다: 패널이 스크롤되고, 측정값이 이동하며, 다음 크기가 잘못 계산되는 현상이었습니다. 하지만 여전히 고쳐지지 않았습니다.
실제 원인: 드래그하는 동안 코드는 크기 조절 커서(resize cursor)를 표시하기 위해 <body>에 클래스를 추가했습니다. 수직 크기 조절을 위한 클래스는 row였습니다.
앱에는 이미 .row 클래스가 존재했습니다. 이 클래스는 사이드 패널의 작은 입력 필드를 스타일링합니다:
.row { display: flex; height: 26px; }
따라서 드래그하는 내내 전체 페이지가 높이 26픽셀의 플렉스(flex) 행처럼 변했습니다.
해결책은 <body>에 일반적인 클래스 이름을 붙이는 것을 멈추고 대신 네임스페이스 속성(namespaced attribute)을 사용하는 것이었습니다:
document.documentElement.dataset.resizing = axis; // 'x' 또는 'y'
html[data-resizing] { user-select: none; }
html[data-resizing=y] body::after { cursor: row-resize; }
제가 얻은 교훈은 다음과 같습니다: <html> 또는 <body>의 상태는 전역(global)적이기 때문에, 마치 전역 변수처럼 이름을 지정해야 합니다. 그리고 테스트가 크기를 측정할 때 드래그 중간에 화면을 전혀 보지 않는다면, 사용자가 페이지가 무너지는 것을 지켜보는 동안에도 통과될 수 있습니다.
유사한 것을 만들고 있다면 제가 해주고 싶은 조언
- 모든 것을 하나의 함수에서 렌더링하세요. 미리 보기(Preview), 내보내기(export) 및 썸네일은 자체적인 그리기 코드를 가져서는 안 됩니다.
- 에이전트가 사용자의 편집 경로를 사용하도록 만드세요. 실행 취소(Undo), 더티 상태(dirty state) 및 실시간 업데이트는 공짜로 따라오며, 두 개의 진실 공급원(source of truth)을 가질 일이 없습니다.
- 라이브 레이아웃이 아닌 드래그 시작 지점에서 측정하세요. 드래그 중간에 레이아웃을 다시 읽는 것은 스스로에게 피드백을 줄 수 있습니다.
- 화면을 보세요. 테스트 로그의 숫자는 사람이 보는 것과 같지 않습니다.
브라우저 기반 모션 에디터에서 무엇을 보고 싶으신가요? 그리고 만약 페이지 전체를 다운시킨 한 단어 CSS 버그를 겪었다면, 댓글로 들려주세요.
레포지토리는 GitHub의 Natuworkguy/Riptide입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기