화이트보드 애니메이션 (Whiteboard Animation)
VideoScribe / Doodly 스타일: 손이 그림과 글씨를 보드 위에 획 하나하나 그리면서 내레이션이 설명합니다. SVG 경로가 스스로 그려지는 애니메이션과 그리는 팁을 따라가는 마커 손을 결합하고, 내레이션 순서대로 요소가 보드에 채워지도록 시퀀싱합니다.
사용 시기
- '스크라이브' / 낙서 설명 영상: 손이 아트와 텍스트를 화이트보드에 그리는 방식.
- 스케치나 로고가 실시간으로 선 하나하나 그려지는 애니메이션.
- 음성 해설에 맞춰 손글씨가 스스로 쓰여지는 효과.
- 정적 일러스트레이션을 획별로 안내되는 공개 효과로 변환.
핵심 메커니즘: SVG 선 그리기 (Stroke Draw-On)
경로의 stroke-dashoffset을 전체 길이에서 0까지 애니메이션화하여 경로가 '스스로 그려지도록' 만듭니다. 대시 간격이 줄어들면서 시작부터 끝까지 선이 노출됩니다.
const path = document.querySelector("#stroke");
const len = path.getTotalLength();
path.style.strokeDasharray = len; // 경로 전체 길이와 같은 하나의 대시
path.style.strokeDashoffset = len; // 완전히 밀려나 보이지 않음
path.getBoundingClientRect(); // 트랜지션 전 리플로우 강제
path.style.transition = "stroke-dashoffset 1.2s linear";
path.style.strokeDashoffset = "0"; // 시작 → 끝으로 그림
<path>에 pathLength="1"을 사용하면 정규화됩니다: stroke-dasharray:1; stroke-dashoffset:1은 실제 길이에 관계없이 모든 경로를 그리며, 단일 이즈 값(0→1)이 그리기 진행 상태가 됩니다. linear (또는 매우 부드러운 이즈)는 안정적인 손놀림처럼 보입니다. 긴 획에 ease-in-out을 사용하면 마커가 불규칙하게 움직여 보이므로 피하십시오.
마커 손이 팁을 따라가기
손이 그려지는 정확한 지점에 위치해야 착시가 성립합니다. 현재 그리기 진행 상태에서 경로를 샘플링하고 매 프레임마다 손 이미지를 그곳에 배치합니다. 손 이미지의 펜촉이 경로 지점에 정렬되어야 합니다(이미지의 모서리가 아닌 펜촉이 지점에 오도록 오프셋).
const hand = document.querySelector("#hand"); // <img>/<g>, 팁이 왼쪽 상단 "펜촉"에 위치
const NIB = { x: 6, y: 4 }; // 손 이미지 내 펜촉 오프셋
function drawOn(path, hand, dur = 1200) {
const len = path.getTotalLength(), t0 = performance.now();
path.style.strokeDasharray = len;
(function frame(now) {
const k = Math.min(1, (now - t0) / dur); // 0→1 진행 상태
path.style.strokeDashoffset = len * (1 - k); // 시작→팁까지 노출
const p = path.getPointAtLength(len * k); // SVG 좌표계에서 현재 팁 위치
hand.setAttribute("transform", `translate(${p.x - NIB.x} ${p.y - NIB.y})`);
if (k < 1) requestAnimationFrame(frame);
})(t0);
}
getPointAtLength(len * k)는 핵심 함수입니다: strokeDashoffset을 구동하는 동일한 진행 상태에서 경로상의 좌표를 반환하므로 펜촉이 그려지는 선에 고정됩니다. 개별 획 사이에서는 손을 들어올립니다(페이드 아웃 후 다음 시작점으로 이동) 빈 보드 위를 미끄러지지 않도록 합니다.
공개 순서 — 보드 채우기
사람이 장면을 그리는 순서대로, 내레이션 순서에 따라 요소를 배열합니다: 각 일러스트레이션과 손글씨 블록이 그려지고, 잠시 유지된 후 다음이 시작됩니다. 하나의 그림 내에서 획을 시간차를 두고 시작합니다. 각 요소가 완성된 후 손을 다음으로 옮기기 전에 잠시 멈춥니다. 하나의 그림 = 음성 해설의 한 호흡입니다. 두 가지를 동시에 그리지 마십시오 — 시선(그리고 하나의 손)은 하나의 팁만 따라갈 수 있습니다.
| 단계 | 메커니즘 |
|---|---|
| 획 그리기 | stroke-dashoffset len→0, 손은 getPointAtLength(len*k) 위치 |
| 다중 획 그림 | 획들을 시간차를 두고 그림; 손은 각 새 획의 시작점으로 이동 |
| 손글씨 | 글자/단어별 획을 읽는 순서대로 그림, 손이 따라감 |
| 유지 | 요소 완료 후 손을 보드 밖에서 일시 정지 (0.4–1.0초) |
| 다음 요소 | 손을 다음 시작점으로 이동/페이드 인; 그리기 시작 |
| 지우기 / 클리어 | 다음 섹션을 위해 보드를 비우기 위해 와이프 또는 역방향 그리기 |
아트를 드로어블 경로로 변환하기
드로 온(draw-on)은 그리기 순서대로 배열된 단일 스트로크의 열린 경로가 필요합니다 — 채워진 복합 도형이 아닙니다.
- 선 아트를 열린 스트로크(아웃라인, 채우기 아님)로 제작 또는 트레이싱합니다(Illustrator/Inkscape). 채워진 블롭에는 애니메이션할 '획'이 없습니다. 채우기를 중심선 획으로 변환하거나 외곽선 경로를 그린 후 외곽선이 완성된 후에 채웁니다.
- 순서가 중요합니다:
<path>요소는 SVG에서 손이 그리는 순서대로 나타나야 합니다 — 문서 순서대로 애니메이션합니다. - 길고 연속적인 선이 가장 자연스러워 보입니다. 실제 펜이 들어올려지는 곳에서만 끊습니다.
- 단색 영역의 경우: 먼저 외곽선 획을 그리고, 그 뒤에 채우기를 페이드인/확장하여(
opacity또는 클립 공개) '나중에 색칠된' 느낌을 줍니다. - 손글씨: 획/손글씨 폰트를 사용하고, 글리프를 경로로 변환한 다음 각 글리프의 경로를 획 순서대로 그립니다. 또는 마스크를 사용하여 단어를 가리고 왼쪽에서 오른쪽으로 공개 효과를 적용하고 손이 마스크 가장자리를 따라가도록 합니다.
마커 손 에셋
펜촉이 알려진 지점에 있는, 마커를 쥔 손의 PNG/SVG입니다. 펜촉 오프셋을 한 번 측정하여(NIB 위 참조) 재사용합니다. 일관성을 위해 전체 작품에 하나의 손 에셋을 사용합니다. 왼손이 필요한 경우에만 미러링합니다(scaleX(-1)). 손 아래에 희미한 그림자를 추가하여 깊이감을 줍니다. 손이 '떨어져 있을 때' 유지 구간에서는 그림자를 제거합니다.
지우기 / 와이프 전환
섹션 사이에 보드를 지워 복잡해지지 않도록 합니다:
- 와이프:
clip-path(또는 흰색 사각형)을 채워진 그룹에 애니메이션하여 지워지게 하고, 손은 선택적으로 '지우는' 동작을 합니다. - 역방향 그리기:
strokeDashoffset0→len을 실행하여 그리기를 취소하고, 손이 뒤따라갑니다. - 페이드 후 다시 그리기: 이전 그룹을 교차 페이드 아웃하고 다음 그룹의 그리기를 시작합니다.
하나의 명확한 문법(예: 항상 왼쪽에서 오른쪽으로 와이프)을 유지하여 전환이 의도적으로 느껴지도록 합니다.
내레이션에 맞춰 속도 조절하기
그리기 지속 시간은 고정 타이머가 아닌 음성 해설에 맞춰야 합니다. VO를 약 2.3단어/초로 추정하여 각 그림의 총 그리기 시간을 해당 그림이 설명하는 대사의 길이와 대략적으로 맞추고, 짧은 유지 시간을 추가합니다. VO가 언급하는 레이블은 그 단어가 말해지는 순간에 정확히 그려져야 합니다. 강조 라인에서는 손을 느리게 하고, 중요하지 않은 연결 획은 빠르게 합니다. 내레이션이 계속되는 동안 완성된 보드 위에 손이 멈춰 있지 않도록 합니다 — 보드 밖으로 옮기거나 다음 요소를 시작합니다.
출력 체크리스트
- 모든 획은 그리기 순서대로 배열된 단일 열린 경로입니다. 어떤 것도 완전히 완성된 상태로 갑자기 나타나지 않습니다.
- 손 펜촉은 각 획 동안 그리기 팁 위에 정확히 위치합니다(떠 있거나 지연되지 않음).
- 손은 개별 획/요소 사이에서 깔끔하게 들어 올려집니다. 빈 보드 위를 미끄러지지 않습니다.
- 요소는 내레이션 순서대로 하나씩 그려집니다. 각 요소는 다음이 시작되기 전에 유지됩니다.
- 손글씨는 읽는 순서대로 그려집니다. 채우기는 외곽선 이후에 나타납니다.
- 지우기/와이프 문법은 일관되게 유지됩니다. 보드가 너무 복잡해지지 않습니다.
- 그리기 타이밍은 해당 그림이 설명하는 VO 라인을 따릅니다.
prefers-reduced-motion: 그리기 애니메이션 없이 완성된 보드(모든 획이 offset 0, 손은 숨김)를 표시합니다.
제공 및 검증 (독립형 HTML)
패키징된 도우미 (
scripts/):scripts/seek-shot.sh anim.html 0 1.5 3은?t=N하네스를 고정하고 각 순간을 스크린샷합니다.scripts/contact-sheet.sh sheet.png frame-*.png는 한눈에 검토할 수 있도록 타일링합니다.scripts/README.md를 참조하세요.
자체 포함된 화이트보드 장면(그리기 일러스트, 손글씨, 짧은 스크라이브 비트)의 경우 제공물은 브라우저에서 직접 열리는 하나의 HTML 파일입니다 — 빌드 단계나 렌더 파이프라인이 필요 없습니다. 웹 모션에는 단일 파일이 적합합니다. 하나의 파일로 충분할 때 번들러를 사용할 필요가 없습니다. (베이크된 VO가 포함된 전체 내레이션 스크라이브 비디오의 경우 Remotion 컴포지션으로 빌드하고 remotion still로 검증합니다 — explainer-video 스킬 참조).
출력 계약:
- 하나의
.html파일: SVG 인라인(그리기 순서대로 경로,pathLength="1"또는 측정된 길이), 손 에셋 인라인 또는 data-URI, 드로 온 드라이버가 인라인<script>하나에 포함. - 모든 그리기는 하나의 마스터 타임라인(GSAP
tl또는 하나의 rAF 클록)에서 이루어지므로 전체 시퀀스가 단일 플레이헤드를 가지며 탐색 가능 —strokeDashoffset과 손 위치 모두 동일한 진행 값으로 구동. - 아래의 탐색 하네스를 포함하여 어떤 순간이든 스크린샷을 위해 고정할 수 있음.
t에서 고정하면 경로가 얼마나 그려졌는지 정확히 보여줍니다 — 검증이 이에 의존합니다.
탐색 하네스 — 스크린샷을 위해 정확한 순간 고정. ?t=N은 마스터 타임라인을 N초로 이동하고 일시 정지하여 스크린샷이 중간 그리기 상태의 결정적인 프레임에 찍히도록 합니다.
<script>
// ... 마스터 타임라인을 `tl`로 구축 (drawOn 호출을 타임라인에 배치) ...
const t = new URLSearchParams(location.search).get("t");
if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); } // t초에서 고정
// ?t 없음 → 정상 재생
window.__ready = true; // 헤드리스 대기를 위한 준비 신호
console.log("duration", tl.duration());
</script>
검증 루프 — 렌더 → 고정 → 스크린샷 → 확인:
- 타임라인의 여러 지점에서 파일을 엽니다 — 시작, 중간 그리기 프레임, 끝:
…/scribe.html?t=0,?t=<dur/2>,?t=<dur>. 종료 시간을 위해 콘솔에서tl.duration()을 읽습니다. 획이 그려지는 중간의t를 하나 이상 선택하십시오 — 거기서 손-팁 착시가 입증되거나 깨집니다. - 각 고정 프레임을 헤드리스로 스크린샷합니다:
npx playwright screenshot --wait-for-timeout=500 "file://$PWD/scribe.html?t=1.2" frame-mid.png - 각 스틸을 검사합니다 — 충실도 확인: 획 중간
t에서 손 펜촉이 그려진 부분의 선두 팁에 정확히 위치하는지(앞서지 않고, 뒤처지지 않고, 떠 있지 않음); 경로가 팁까지만 그려져 있는지;?t=<dur>에서 모든 획이 깔끔하게 완료되어 틈, 오버슈트, 남은 대시가 없는지. 아티팩트 확인: 획 사이에 손이 빈 보드 위를 미끄러지는지, 펜촉 오프셋이 잘못되어 팁이 선에서 떠 있는지, 획이 순서에 어긋나게 그려지는지, 외곽선보다 먼저 채움색이 나타나는지, 잘리거나 캔버스 밖으로 그려지는지, 폰트 로드 전에 FOUC가 발생하는지. - 반복:
NIB, 획 순서, 지속 시간을 조정하고 각 고정 프레임이 손이 그리는 중간처럼 보일 때까지 다시 스크린샷합니다.
완료 전 확인사항:
- 브라우저에서 독립적으로 실행 — 콘솔 오류, 누락된 CDN/에셋 없음.
- 하나의 마스터 타임라인;
?t=N이 올바르게 고정되고strokeDashoffset과 손 위치가 하나의 진행 값을 공유. - 시작 / 중간 획 / 끝에서 스크린샷 — 손 펜촉이 전체적으로 팁 위에 있고, 획이 끝에서 깔끔하게 완료되며, 그리기 순서가 올바름.
- 획 사이에 손이 들어 올려짐; 빈 보드 위를 미끄러지지 않음.
prefers-reduced-motion은 그리기 애니메이션 없이 완성된 보드(모든 획이 그려짐, 손은 숨김)를 표시.
내레이션과 함께 프레임 결정적 내보내기가 필요한 경우, 동일한 드로 온(strokeDashoffset과 손 변환을 useCurrentFrame()/fps로 구동)을 Remotion 컴포지션으로 포팅하고 MP4/GIF로 렌더링합니다 — 렌더-스틸 → 인코딩 루프에 대해서는 explainer-video 스킬을 참조하세요.
참조 파일
references/draw-on-recipes.md— 실행 가능한 드로 온 메커니즘:getTotalLength/getPointAtLength손-경로 따라가기, GSAPDrawSVGPlugin및MotionPathPlugin변형, 다중 획 시간차, 글리프별 손글씨, 외곽선 후 채우기, 지우기/와이프 및 역방향 그리기 전환, Remotion 프레임 구동 포트.references/whiteboard-pipeline.md— 아트를 그릴 수 있는 단일 획 SVG로 변환, 그리기 순서 제작, 손글씨/획 폰트, 마커 손 에셋 및 펜촉 보정, VO 라인에 그리기 시간 맞추기, 스토리보드-보드 빌드 시퀀스.