graphic-gif
CSS 애니메이션 또는 AI 이미지-투-비디오 API를 사용하여 애니메이션 루핑 GIF를 생성합니다. 출력: animation.gif.
정적 PNG나 PDF를 출력하는 다른 모든 graphic- 스킬과 달리, 이 스킬은 애니메이션 .gif를 출력합니다. Playwright와 Web Animations API(옵션 A, 기본값)를 통해 CSS @keyframes 애니메이션을 프레임 단위로 캡처하거나, Kling을 통한 AI 이미지-투-비디오 파이프라인(옵션 B)을 사용합니다.
중요 규칙 (모든 생성 전에 읽을 것)
- 기본값은 css-animated입니다. 명시적으로 요청하지 않는 한
ai-generated를 사용하지 마세요. - 캔버스는 800×800px 정사각형입니다. 모든
clamp()값은 800px 기준으로 계산됩니다(1vw = 8px). - 단일 자체 포함 HTML. 모든 CSS는
<style>에 인라인으로 포함합니다. 글꼴 CDN<link>만 유일한 외부 의존성입니다. - HTML을 채팅에 절대 덤프하지 마세요. 파일로 저장하고 요약만 표시합니다.
- 프레임 캡처는 Web Animations API seeking을 사용합니다.
setTimeout루프나animation-delay트릭을 사용하지 마세요. - 정확한 프레임 수:
Math.floor(duration_seconds * fps)프레임.t=duration_ms시점의 프레임은 절대 캡처하지 마세요 —t=0과 중복되어 루프 지점에서 눈에 띄는 끊김이 발생합니다. - 플레이스홀더 박스는 사용하지 마세요. CSS로 생성된 시각 요소만 사용합니다. "이미지가 여기에 들어갑니다"와 같은 요소는 없어야 합니다.
- 간단한 팔레트 = 작은 파일 크기. 다음을 사용하세요:
clean-slate,terminal,electric-burst,brutalist. - 스태거를 위해 animation-delay를 사용하지 마세요. 스태거를
@keyframes백분율에 직접 포함시키세요 — 프레임 seeking이 타이밍을 처리합니다. - CSS를 작성하기 전에 디자인 방향을 확정하세요. 톤, 시그니처 요소, 모션 스타일, 잊지 못할 디테일 — 모든 것을 첫 코드 라인 이전에 결정합니다.
1단계: 입력 받기
필수: prompt (내용 설명 AND 모션 브리핑)
선택 사항 (기본값 있음):
| 매개변수 | 기본값 | 옵션 |
|---|---|---|
| animation_type | css-animated | css-animated / ai-generated |
| duration | 3.0 | 초 |
| fps | 12 | 초당 프레임 수 |
| loop | true | true / false |
| style | clean-slate | clean-slate / terminal / electric-burst / brutalist |
| dimensions | 800x800 | 너비x높이 (픽셀) |
| optimization | balanced | quality / balanced / filesize |
프롬프트가 없거나 모션 설명이 누락된 경우, 정확히 다음과 같이 질문하세요:
"GIF에 무엇을 표시할까요? 콘텐츠와 모션을 모두 설명해주세요 (예: '통계 카운트업: 구매자의 73%는 구매 전 3개 이상의 콘텐츠를 읽습니다. 타자기 효과, 한 글자씩. 스타일: terminal. 3초, 12fps.')
주요 설정 (모두 선택 사항, 기본값 표시):
- animation_type: css-animated (기본값) 또는 ai-generated
- duration: 3.0초
- fps: 12
- loop: true
- style: clean-slate (옵션: clean-slate / terminal / electric-burst / brutalist)
- dimensions: 800x800
- optimization: balanced (옵션: quality / balanced / filesize)"
필요한 모든 정보가 있으면 → 바로 2단계로 건너뜁니다.
2단계: 내부 아키텍처 (사용자에게 절대 표시하지 않음)
css-animated의 경우:
- 다음 중 애니메이션 유형을 선택합니다:
fade-in,slide-in,typewriter,counter,pulse,loop-scroll references/animation-library.md를 읽고 — 선택한 유형의 전체 HTML/CSS 명세를 찾습니다.references/style-presets.md를 읽고 — 선택한 스타일의 CSS 토큰 블록을 로드합니다.- 프레임 수 계산:
Math.floor(duration_seconds * fps)— 이 숫자를 기록합니다. - 디자인 방향 확정:
| 결정 | 도출 근거 |
|---|---|
| 톤 | 대상 청중을 위한 감성적 레지스터 (기계적 / 따뜻함 / 전기적 / 전문적) |
| 시그니처 요소 | 일관되게 사용되는 하나의 시각 장치 (커서 깜빡임, 유령 숫자, 스캔라인 오버레이, 액센트 테두리) |
| 모션 스타일 | 이 유형에 대한 이즈 커브 철학 (스프링 / 선형 / 스텝 / ease-in-out) |
| 잊지 못할 디테일 | 이 GIF를 본 사람이 기억할 단 하나의 요소 |
ai-generated의 경우:
- 기본 정지 프레임 HTML 생성 (포스터 스타일 레이아웃, 캔버스용)
- 스크린샷을 사용하여 PNG로 내보내기
- Kling API 호출:
POST https://api.klingai.com/v1/videos/image2video—image_url과 모션을 설명하는prompt포함 - 작업 완료까지 폴링
- 비디오 다운로드 → ffmpeg로 GIF 변환:
# 최상의 색상 품질을 위한 2패스 팔레트 ffmpeg -i input.mp4 -vf "fps=12,scale=800:800:flags=lanczos,palettegen=stats_mode=diff" palette.png ffmpeg -i input.mp4 -i palette.png -vf "fps=12,scale=800:800:flags=lanczos,paletteuse=dither=bayer:bayer_scale=5" output.gif
3단계: HTML 생성 (css-animated 경로)
생성 전 references/animation-library.md와 references/style-presets.md를 읽으세요.
캔버스 기본 — 모든 GIF에 필수:
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
body {
width: 800px;
height: 800px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
}
.canvas {
width: 800px;
height: 800px;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
애니메이션 규칙:
- 모든 애니메이션 요소에
animation-fill-mode: forwards(또는both) 적용 - 유형별 타이밍 함수:
typewriter→steps(N, end)(N = 정확한 문자 수)counter→linearfade-in→cubic-bezier(0.22, 1, 0.36, 1)(ease-out)slide-in→cubic-bezier(0.34, 1.56, 0.64, 1)(스프링 오버슈트)pulse→ease-in-out(단,animation-iteration-count: infinite)loop-scroll→linear(단,animation-iteration-count: infinite)
- 원샷 애니메이션(fade-in, slide-in, typewriter, counter):
animation-iteration-count: 1— 루핑은 GIF 레벨에서 처리 animation-delay사용 금지 — 스태거는@keyframes백분율에 포함
타자기 N 계산: 공백, 구두점, 숫자를 포함한 모든 문자를 셉니다:
- "73% of buyers" = 14자 →
steps(14, end) - "Hello, World!" = 13자 →
steps(13, end)
카운터 CSS @property (counter 유형에 필수):
@property --num {
syntax: '<integer>';
inherits: false;
initial-value: 0;
}
.counter {
animation: countUp var(--duration) linear forwards;
counter-reset: num var(--num);
}
.counter::after { content: counter(num); }
@keyframes countUp {
from { --num: 0; }
to { --num: var(--target); }
}
Loop-scroll: 콘텐츠를 반드시 복제해야 함:
<!-- 원본 아이템 4개 + 복제 아이템 4개 -->
<div class="ticker-track" style="--item-count: 4;">
[item1][item2][item3][item4][item1][item2][item3][item4]
</div>
translateX(0 → -50%) with linear infinite.
디자인 품질 규칙 (2단계의 결정 사항에서):
- 이름이 지정된 시그니처 요소가 CSS/HTML에 반드시 존재해야 함 (단순히 설명만으로는 안 됨)
- 타이포그래피: 디스플레이 텍스트와 보조 텍스트 간 최소 2:1 두께 대비 (예: 700 vs 400)
- 배경: 어두운 스타일의 경우 순수 흰색
#fff사용 금지 — 프리셋의 정확한--bg값 사용 - 터미널 스타일:
repeating-linear-gradient를 사용한 스캔라인 오버레이::after를opacity: 0.03으로 추가 - 브루탈리스트 스타일: 주요 요소에
4px solid #000또는4px solid var(--accent)두꺼운 테두리 - 잊지 못할 디테일: 추가 요소가 필요하면 지금 추가하세요
4단계: 자체 QA (5단계 전에 모든 실패 수정)
캔버스:
-
body와.canvas가 정확히 800×800px (또는 지정된 크기) -
body와.canvas모두overflow: hidden - 캔버스 경계를 넘는 요소 없음
애니메이션:
-
animation-delay가 전혀 없음 — 스태거는@keyframes백분율로 처리 - 모든 애니메이션이
t=0에서 시작 (Web Animations API가 거기서 seek) - 모든 애니메이션 요소에
animation-fill-mode: forwards또는both - 원샷 애니메이션:
animation-iteration-count: 1 - 무한 애니메이션(pulse, loop-scroll):
animation-iteration-count: infinite
유형별 확인:
- 타자기:
steps(N, end)의 N = 텍스트 문자열의 정확한 문자 수 - 카운터:
@property --num이syntax: '<integer>'와initial-value: 0으로 선언됨 - 카운터:
counter-reset: num var(--num)및::after { content: counter(num) } - Loop-scroll: 콘텐츠가 HTML에서 정확히 한 번 복제됨
디자인:
- 플레이스홀더 박스 없음
-
references/style-presets.md의 스타일 프리셋 토큰이 적용됨 — 임의의 헥스 색상 없음 - 2단계에서 명명된 시그니처 요소가 HTML/CSS에 실제로 존재함
- 2단계의 잊지 못할 디테일이 실제로 구현됨
- 선택한 스타일의 글꼴을 위한 글꼴 CDN
<link>가 존재함
5단계: 내보내기
프롬프트에서 슬러그 결정 (케밥-케이스, 30자 이하). 출력 디렉토리 생성:
mkdir -p [slug]
HTML 저장:
[slug]/animation.html
빠른 시각적 확인을 위해 브라우저에서 열기:
open [slug]/animation.html
내보내기 스크립트 실행 ([skill-root]를 이 스킬의 실제 경로로 대체):
bash [skill-root]/scripts/export-gif.sh \
[slug]/animation.html \
[slug]/animation.gif \
--duration [duration] \
--fps [fps] \
[--no-loop if loop=false] \
--optimization [optimization] \
--width [W] \
--height [H]
스크립트 동작:
- 임시 디렉토리에
gifenc,sharp(또는jimp),playwright설치 - 캐시되지 않은 경우 Chromium 다운로드
capture-and-encode.mjs실행 — 애니메이션 일시 중지, 각 프레임 seek, 스크린샷, GIF 조립gifsicle최적화 패스 실행 (가능한 경우)- 파일 크기 보고 및 결과 열기
내보내기 스크립트를 [skill-root]/scripts/export-gif.sh에서 찾을 수 없는 경우, 스킬이 scripts/ 폴더와 함께 설치되었는지 확인하세요.
6단계: 출력 요약
성공적인 내보내기 후 표시:
## GIF: [1줄 설명]
날짜: [YYYY-MM-DD] | 스타일: [style] | 애니메이션: [type] | [duration]초 @ [fps]fps
크기: [WxH] | 프레임: [N] | 루프: [true/false]
파일
소스: [slug]/animation.html
출력: [slug]/animation.gif
크기: [X] KB
체크리스트
- [ ] 미리보기가 시작/끝 지점에서 깔끔하게 루프됨 (끊김 없음)
- [ ] 텍스트가 의도한 표시 크기에서 읽기 쉬움
- [ ] 파일 크기 적절: 이메일 <500KB / 소셜 미디어 <3MB
AI 생성 경로 (옵션 B)
animation_type: ai-generated가 명시적으로 지정된 경우에만 사용합니다.
요구 사항:
- 환경에 Kling API 키:
KLING_API_KEY(일당 66회 무료 크레딧, 무료 티어에 신용카드 필요 없음) - 비디오→GIF 변환을 위해 로컬에
ffmpeg설치 필요
워크플로우:
- 프롬프트에 맞는 기본 정지 프레임 HTML 생성 (지정된 크기의 포스터/그래픽 스타일)
- 정지 프레임을 PNG로 내보내기:
# Playwright를 통한 빠른 스크린샷 node -e " const { chromium } = require('playwright'); (async () => { const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: W, height: H } }); await page.goto('file://[slug]/animation.html'); await page.screenshot({ path: '[slug]/base-frame.png' }); await browser.close(); })(); " - Kling 이미지-투-비디오 엔드포인트에 업로드
- 결과물을 ffmpeg 2패스 팔레트로 GIF 변환
- gifsicle 최적화 적용
Kling을 사용할 수 없는 경우: css-animated로 대체하고 사용자에게 다음과 같이 메모: "AI 생성에는 Kling API 키(KLING_API_KEY)가 필요합니다. css-animated로 대체합니다. AI 생성을 활성화하려면 키를 설정하세요."
프롬프트 팁 (사용자가 가이드를 요청할 때 표시)
"콘텐츠뿐만 아니라 모션도 설명하세요. '통계 표시'보다 '통계가 하나씩 카운트업'이 더 좋습니다."
"파일 크기를 위해 간단하게 유지하세요. 1–3개의 애니메이션 요소와 단색 배경."
"루프를 생각하세요. 애니메이션이 끝에서 시작으로 눈에 띄지 않게 이어져야 합니다."
"애니메이션 유형을 명시적으로 지정하세요.
typewriter와counter가 소셜 미디어에 가장 효과적입니다."✅ 좋은 예: "애니메이션 GIF 생성, css-animated, 타자기 효과. 텍스트: 'B2B 구매자의 73%는 영업에 연락하기 전 3개 이상의 콘텐츠를 읽습니다.' 각 문자가 한 번에 하나씩 타이핑됩니다. 스타일: terminal. 3초, 12fps, loop=true."
❌ 나쁜 예: "마케팅 팁 애니메이션 GIF 만들어줘"