{ "name": "awesome-ai-motion", "version": "0.1.0", "description": "AI 에이전트가 바로 쓰는 모션 기법 도감. 효과마다 정의·파라미터·구현·프롬프트·출처, 핵심 효과는 3초 클립까지.", "descriptionEn": "A motion technique field guide your AI agent can use: definitions, parameters, implementation and prompts for every effect, with 3-second reference clips for the core set.", "families": { "principles": { "ko": "기본기", "en": "PRINCIPLES", "desc": "디즈니 12원칙, 이징, 타이밍과 간격. 모든 움직임의 문법", "descEn": "Disney's 12 principles, easing, timing and spacing. The grammar of all motion" }, "entrance": { "ko": "등장·퇴장", "en": "ENTRANCE & EXIT", "desc": "요소가 화면에 들어오고 나가는 방식", "descEn": "How elements come in and leave" }, "emphasis": { "ko": "강조·주의", "en": "EMPHASIS", "desc": "이미 보이는 것 중 하나로 시선을 끌어오는 방식", "descEn": "Pulling the eye to something already on screen" }, "type": { "ko": "타이포", "en": "TYPOGRAPHY", "desc": "글자가 등장하고 바뀌고 강조되는 방식", "descEn": "How text appears, changes and gets emphasis" }, "transitions": { "ko": "전환·컷", "en": "TRANSITIONS", "desc": "장면과 장면을 잇는 컷과 화면 전환", "descEn": "Cuts and transitions that join scenes" }, "camera": { "ko": "가상 카메라", "en": "CAMERA", "desc": "화면 전체를 움직여 시선과 공간을 옮기는 법", "descEn": "Moving the whole frame to move the eye and the space" }, "data": { "ko": "데이터", "en": "DATA", "desc": "숫자와 차트를 움직여 크기·변화·비교를 보이는 법", "descEn": "Animating numbers and charts to show size, change and comparison" }, "ui": { "ko": "UI 시연", "en": "UI DEMO", "desc": "화면 조작과 제품 사용을 영상으로 보여 주는 법", "descEn": "Showing screen interaction and product use on video" }, "explainer": { "ko": "원리 도해", "en": "EXPLAINER", "desc": "보이지 않는 원리와 과정을 그림으로 풀어 보이는 법", "descEn": "Drawing invisible mechanisms and processes" }, "shape": { "ko": "도형·패스", "en": "SHAPE & PATH", "desc": "선 그리기, 모프, 경로 이동, 셰이프 레이어 기법", "descEn": "Line drawing, morphing, path motion, shape layers" }, "texture": { "ko": "질감·스타일", "en": "TEXTURE & STYLE", "desc": "글리치, 그레인, 빛, 인쇄 질감 같은 화면 처리", "descEn": "Glitch, grain, light, print texture" }, "generative": { "ko": "입자·생성", "en": "GENERATIVE", "desc": "입자, 노이즈, 반복 규칙으로 만드는 움직임", "descEn": "Particles, noise and rule-based repetition" }, "depth": { "ko": "3D·깊이", "en": "3D & DEPTH", "desc": "원근, 회전, 레이어 깊이로 만드는 공간감", "descEn": "Perspective, rotation and layered depth" }, "loop": { "ko": "반복·앰비언트", "en": "LOOP & AMBIENT", "desc": "로더, 숨쉬기, 배경 루프처럼 계속 도는 움직임", "descEn": "Loaders, breathing, ambient loops" }, "caption": { "ko": "자막·하단 자막", "en": "CAPTIONS", "desc": "자막, 하단 자막, 인용과 출처 표기", "descEn": "Captions, lower thirds, quotes and credits" } }, "purposes": [ "주목 끌기", "설명", "비교", "순서·흐름", "강조", "전환", "데이터 증명", "피드백", "분위기", "브랜딩" ], "media": [ "설명 영상", "숏폼", "스크롤덱", "발표", "제품 시연", "데이터 스토리", "웹 UI" ], "levels": [ "기본", "중급", "고급" ], "counts": { "effects": 637, "rendered": 128, "recipes": 11 }, "effects": [ { "slug": "overlapping-action", "family": "principles", "ko": "오버랩", "en": "Overlapping Action", "aka": [ "시간차 동작", "드래그", "Drag", "Offset timing", "Overlapping action", "겹치는 동작", "겹침 동작", "Time Delayed Follower" ], "oneLiner": "한 물체의 부위들이 같은 방향으로 움직이되 출발과 정지 시각이 조금씩 어긋나는 움직임", "conveys": "물체에 무게와 유연함이 있다는 느낌. 한 덩어리로 딱딱하게 움직이는 것보다 살아 있고 자연스럽게 읽힌다", "purposes": [ "분위기", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "캐릭터·아이콘·카드 묶음처럼 여러 부분이 붙은 대상을 옮길 때", "목록이나 카드 무리가 한꺼번에 이동할 때 부위별로 살짝 늦게 따라오게 할 때" ], "goodExample": "카드 묶음이 오른쪽으로 옮겨질 때 제목 줄이 먼저, 본문 줄이 0.06초 늦게 도착해 한 장처럼 흐른다", "badExample": "시간차를 0.3초 이상 벌려 부위가 따로 노는 것처럼 보이거나, 모든 버튼·글자마다 걸어 화면이 출렁인다", "avoid": [ "부위 간 시간차 0.12초 초과 금지(따로 노는 것처럼 보임)", "정보 읽기가 우선인 표·본문에는 쓰지 않는다" ], "params": [ { "name": "부위 간 시간차", "default": "0.07s", "range": "0.04~0.10s", "note": "앞 부위가 먼저 출발" }, { "name": "부위별 지속 증가", "default": "+0.06s", "range": "0.03~0.08s", "note": "뒤 부위일수록 늦게 멈춤" }, { "name": "기본 지속", "default": "1.1s", "range": "0.6~1.4s", "note": "이동 거리에 비례" }, { "name": "이징", "default": "power3.inOut", "range": "power2~power4", "note": "출발·정지가 모두 보이는 이동" } ], "ease": "power3.inOut", "snippet": "const parts = ['.head', '.body', '.t1', '.t2', '.t3'];\nparts.forEach((s, i) => {\n tl.to(s, { x: 820, duration: 1.1 + i * 0.06, ease: 'power3.inOut' }, 0.25 + i * 0.07);\n});", "pairsWith": [ "follow-through", "stagger", "easing-curves" ], "engines": { "hyperframes": "한 paused 타임라인 안에서 부위마다 position 인자를 0.07초씩 밀어 쓴다. 부위는 transform x/y만 움직인다", "reelforge": "오브젝트 씬의 이동 비트에 부위 분리 마크업(data-part)을 두고 시간차만 파라미터로 노출한다", "scrolline": "scrub 타임라인이라 시간차가 진행률 비율로 바뀐다. 0.07초 대신 진행률 0.02~0.03 간격으로 옮긴다" }, "prompts": { "claude": "GSAP 타임라인으로 <대상>(머리·몸통·꼬리처럼 부위가 나뉜 요소)을 오른쪽으로 820px 옮기는데 오버랩을 넣어줘. 앞 부위가 먼저 출발하고 뒤 부위는 0.07초씩 늦게 출발하게, 각 부위 duration은 1.1초에서 뒤로 갈수록 0.06초씩 늘려 늦게 멈추게 해. 이징은 power3.inOut, 도착 뒤 0.6초 정지. transform만 쓰고 paused 타임라인 하나로 seek 가능하게 만들어.", "codex": "<파일>의 이동 장면에 오버랩(overlapping action)을 적용해. 요소를 부위 배열로 나눠 i번째 부위 tween을 position 0.25+i*0.07, duration 1.1+i*0.06, ease power3.inOut으로 건다. Math.random·setTimeout 없이 타임라인만 쓰고, 0.4초·1.0초·2.7초 시점을 캡처해 부위 간 간격이 벌어졌다가 닫히는지 확인해." }, "sources": [ { "title": "Disney 12 principles, Thomas & Johnston, The Illusion of Life (1981)", "url": "https://en.wikipedia.org/wiki/Twelve_basic_principles_of_animation", "license": "개념 인용" }, { "title": "motion dictionary 1-principles.md#3.1 함께 쓰지만 구분해야 할 하위 개념", "url": "", "license": "own" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/work-with-expressions/expression-examples/expression-examples.html", "license": "unknown" }, { "title": "airbnb/lottie-web", "url": "https://github.com/airbnb/lottie-web", "license": "MIT" }, { "title": "iart-ai/generative-illustration-skills", "url": "https://github.com/iart-ai/generative-illustration-skills", "license": "unknown" }, { "title": "heygen-com/hyperframes", "url": "https://github.com/heygen-com/hyperframes/blob/HEAD/skills/hyperframes-animation/adapters/lottie.md", "license": "Apache-2.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/overlapping-action/index.html", "mp4": "effects/overlapping-action/clip.mp4", "gif": "effects/overlapping-action/preview.gif", "poster": "effects/overlapping-action/poster.jpg" }, "no": "001", "oneLinerEn": "Parts of an object move in the same direction with slightly offset start and stop times.", "conveysEn": "Conveys weight and flexibility, making the object feel alive and natural.", "useWhenEn": [ "Move objects with connected parts, such as characters, icons, or groups of cards.", "Let individual parts lag slightly as a list or group of cards moves together." ], "promptsEn": { "claude": "Use a GSAP timeline to move , an element with distinct parts such as a head, body, and tail, 820px to the right with overlapping action. Start the leading part first and delay each following part by 0.07 seconds. Set each part's duration to 1.1 seconds, increasing it by 0.06 seconds for each successive part so it stops later. Use power3.inOut and hold for 0.6 seconds after arrival. Animate transforms only and use a single paused timeline that supports seeking.", "codex": "Apply overlapping action to the movement scene in . Divide the element into an array of parts and tween part i at position 0.25+i*0.07 with duration 1.1+i*0.06 and ease power3.inOut. Use only the timeline, without Math.random or setTimeout. Capture at 0.4, 1.0, and 2.7 seconds to verify that the spacing between parts opens up and then closes." }, "variants": [ "위치 추종", "크기 추종", "회전 추종", "일러스트 부품별 위상차", "컷아웃 콜라주", "식물 부유", "그림 이야기", "위치와 회전과 스케일의 시간차 도착" ] }, { "slug": "anticipation", "family": "principles", "ko": "예비동작", "en": "Anticipation", "aka": [ "준비 동작", "Wind-up", "예비 동작", "anticipation-windup-chain", "nudge-curve", "Anticipatory back motion", "반대 방향 예비 동작", "Anticipation Pose" ], "oneLiner": "주 동작 전에 반대 방향으로 짧게 움직여 다음 움직임을 예고하는 기법", "conveys": "출발할 방향과 힘의 축적. 갑작스러운 이동을 따라보기 쉽게 한다", "purposes": [ "주목 끌기", "설명", "비교" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "캐릭터나 아이콘의 발사 방향을 예고할 때", "큰 이동 전에 시선을 준비시킬 때" ], "goodExample": "주홍 점이 0.25초 동안 왼쪽 54px 물러나 납작해진 뒤 오른쪽으로 발사된다", "badExample": "예비동작이 주 동작보다 길어서 사용자의 반응을 지연한다", "avoid": [ "즉시 응답해야 하는 반복 입력에는 넣지 않는다", "예비 이동을 주 이동보다 크게 하지 않는다" ], "params": [ { "name": "예비 거리", "default": "54px", "range": "24~70px", "note": "주 이동과 반대 방향" }, { "name": "예비 지속", "default": "0.25s", "range": "0.15~0.30s", "note": "짧지만 눈에 보이는 준비" }, { "name": "가로 변형", "default": "1.30", "range": "1.15~1.40", "note": "세로 배율은 가로 배율의 역수" }, { "name": "발사 지속", "default": "0.65s", "range": "0.4~0.9s", "note": "준비 후 빠르게 출발" } ], "ease": "power2.inOut 준비 / power3.out 발사", "snippet": "tl.to('#plain',{x:730,duration:.65,ease:'power3.out'},.6);\ntl.to('#ready',{x:-54,scaleX:1.3,scaleY:1/1.3,duration:.25,ease:'power2.inOut'},.6);\ntl.to('#ready',{x:730,scaleX:1,scaleY:1,duration:.65,ease:'power3.out'},.85);", "pairsWith": [ "squash-stretch", "easing-curves", "follow-through" ], "engines": { "hyperframes": "예비 tween과 발사 tween을 같은 paused 타임라인에 연속으로 배치한다", "reelforge": "이동 비트 앞에 반대 방향 54px 준비 비트를 0.25초 넣는다", "scrolline": "전체 이동 진행률 앞부분에 짧은 역방향 변위를 배정한다" }, "prompts": { "claude": "GSAP으로 <대상>에 예비동작을 넣어줘. 0.60초부터 0.25초 동안 왼쪽 54px 이동하고 scaleX 1.3, scaleY 1/1.3으로 납작하게 해. 0.85초부터 0.65초 동안 x 730px으로 발사하며 원형으로 돌아오고, 3.00초까지 도착 상태를 유지해.", "codex": "<파일>의 아래 비교 줄에 예비동작을 적용해. 0.60초부터 0.25초간 x -54px, scaleX 1.3, scaleY 1/1.3, ease power2.inOut으로 준비하고 0.85초부터 0.65초간 x 730px, scale 1, ease power3.out으로 발사해. 0.73초에서 역방향 압축이 보이고 1.73초와 2.90초에서 같은 도착 상태인지 캡처해 확인해." }, "sources": [ { "title": "Twelve basic principles of animation, Anticipation", "url": "https://en.wikipedia.org/wiki/Twelve_basic_principles_of_animation", "license": "개념 인용" }, { "title": "motion dictionary 1-principles.md#3. 디즈니 12원칙 전체", "url": "", "license": "own" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Eases/", "license": "GSAP Standard License" }, { "title": "motiondesign.school", "url": "https://motiondesign.school/blog/animation-principles-in-logo-animation/", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/anticipation/index.html", "mp4": "effects/anticipation/clip.mp4", "gif": "effects/anticipation/preview.gif", "poster": "effects/anticipation/poster.jpg" }, "no": "002", "oneLinerEn": "A brief movement in the opposite direction prepares the viewer for the main action.", "conveysEn": "Signals the direction of travel and a buildup of force, making sudden motion easier to follow.", "useWhenEn": [ "Signal the launch direction of a character or icon.", "Prepare the viewer's attention before a large movement." ], "promptsEn": { "claude": "Add anticipation to with GSAP. Starting at 0.60 seconds, move 54px left over 0.25 seconds and squash it with scaleX 1.3 and scaleY 1/1.3. Starting at 0.85 seconds, launch it to x 730px over 0.65 seconds while restoring its circular shape. Hold the arrival state until 3.00 seconds.", "codex": "Apply anticipation to the lower comparison row in . Starting at 0.60 seconds, prepare over 0.25 seconds with x -54px, scaleX 1.3, scaleY 1/1.3, and ease power2.inOut. Starting at 0.85 seconds, launch over 0.65 seconds with x 730px, scale 1, and ease power3.out. Capture at 0.73 seconds to verify the backward squash, and at 1.73 and 2.90 seconds to verify identical arrival states." }, "variants": [ "누르기 준비", "발사 준비", "회전 준비", "수축 후 팽창", "뒤로 당긴 뒤 이동", "회전 예비", "스케일 예비" ] }, { "slug": "easing-curves", "family": "principles", "ko": "이징", "en": "Easing", "aka": [ "속도 곡선", "Ease", "Easing curves", "이징 곡선", "Easing Curves", "speed-graph-power-family", "keyframe-velocity-to-cubic-bezier", "easy-ease-linear-hold-usage" ], "oneLiner": "같은 거리와 시간을 유지하면서 진행률에 따른 속도를 바꾸는 움직임", "conveys": "출발과 정지의 무게감. ease-out은 빠르게 반응한 뒤 부드럽게 멈춘다", "purposes": [ "비교", "설명", "피드백" ], "media": [ "설명 영상", "웹 UI", "발표" ], "level": "기본", "useWhen": [ "동일한 이동의 속도감을 비교할 때", "UI가 빠르게 반응하고 부드럽게 멈추게 할 때" ], "goodExample": "670px 이동을 1.5초로 통일하고 linear, power3.in, power3.out을 나란히 비교한다", "badExample": "지속 시간과 거리를 함께 바꿔 이징만의 차이를 구별할 수 없게 한다", "avoid": [ "비교 줄마다 거리와 시간을 바꾸지 않는다", "모든 움직임에 강한 가속을 적용하지 않는다" ], "params": [ { "name": "이동 거리", "default": "670px", "range": "300~800px", "note": "비교 줄에 동일하게 적용" }, { "name": "지속 시간", "default": "1.50s", "range": "0.6~1.8s", "note": "동시 출발과 동시 도착" }, { "name": "시작 시각", "default": "0.30s", "range": "0.2~0.4s", "note": "초기 상태를 보여 준다" }, { "name": "이징", "default": "power3.out", "range": "none / power3.in / power3.out", "note": "속도 곡선만 바꾼다" } ], "ease": "none / power3.in / power3.out", "snippet": "[['#linear','none'],['#in','power3.in'],['#out','power3.out']].forEach(([s,ease]) => {\n tl.to(s,{x:670,duration:1.5,ease},.3);\n});\ntl.to('.graph,.arrived',{opacity:1,duration:.25},1.9);", "pairsWith": [ "timing-spacing", "anticipation", "spring-overshoot" ], "engines": { "hyperframes": "한 paused 타임라인에서 세 tween의 시작과 지속 시간을 같게 두고 ease만 바꾼다", "reelforge": "세 비교 트랙의 거리와 지속 시간을 고정하고 이징 값을 노출한다", "scrolline": "같은 스크롤 진행률에 이징 함수를 적용해 위치를 계산한다" }, "prompts": { "claude": "GSAP으로 <대상>을 세 줄에 배치하고 오른쪽 670px 이동을 비교해줘. 0.30초에 함께 출발해 1.50초 동안 none, power3.in, power3.out을 각각 적용하고 마지막 줄만 주홍으로 해. 1.90초에 작은 이징 그래프를 표시하고 3.00초까지 멈춰 있어.", "codex": "<파일>의 scene에 세 이징 비교 줄을 넣어. 모든 점은 0.30초부터 1.50초 동안 transform x로 670px 이동하고 ease는 none, power3.in, power3.out이다. 0.73초와 1.23초 캡처에서 점 위치가 다르고 2.23초와 2.90초에서는 도착 위치와 그래프가 같은지 확인해." }, "sources": [ { "title": "GSAP Eases", "url": "https://gsap.com/docs/v3/Eases/", "license": "공식 문서 참조" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/extended-keyframe/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/easing-curves/index.html", "mp4": "effects/easing-curves/clip.mp4", "gif": "effects/easing-curves/preview.gif", "poster": "effects/easing-curves/poster.jpg" }, "no": "003", "oneLinerEn": "Easing changes velocity over the course of an animation while keeping its distance and duration constant.", "conveysEn": "Adds weight to starts and stops; ease-out responds quickly and then settles smoothly.", "useWhenEn": [ "Compare the perceived speed of otherwise identical movements.", "Make UI motion respond quickly and stop smoothly." ], "promptsEn": { "claude": "Arrange in three rows with GSAP to compare a 670px movement to the right. Start all three at 0.30 seconds and animate for 1.50 seconds using none, power3.in, and power3.out respectively. Make only the last row vermilion. Show small easing graphs at 1.90 seconds and hold until 3.00 seconds.", "codex": "Add three easing comparison rows to the scene in . Move every dot 670px using transform x, starting at 0.30 seconds over 1.50 seconds, with eases none, power3.in, and power3.out. Capture at 0.73 and 1.23 seconds to verify different dot positions, and at 2.23 and 2.90 seconds to verify identical final positions and graphs." }, "variants": [ "linear", "sine", "power1~4", "expo", "circ", "back", "bounce", "elastic", "steps", "power 계열" ] }, { "slug": "spring-overshoot", "family": "principles", "ko": "스프링 오버슛", "en": "Spring & Overshoot", "aka": [ "오버슈트", "Back easing", "목표 초과", "Elastic spring slide", "스프링 슬라이드 전환", "Spring Text Pop", "스프링 텍스트 팝", "Impact Number Reveal" ], "oneLiner": "목표 크기를 조금 넘었다가 되돌아와 안착하는 움직임", "conveys": "관성과 복원력이 있는 도착을 보여준다", "purposes": [ "주목 끌기", "피드백", "설명" ], "media": [ "설명 영상", "제품 시연", "웹 UI" ], "level": "기본", "useWhen": [ "작은 객체가 나타나거나 선택 상태로 안착할 때", "목표 초과와 복원을 그래프로 설명할 때" ], "goodExample": "먹 블록이 scale 0에서 1.08까지 커졌다가 1.00으로 안착하고 그래프의 초과 구간만 주홍이 된다", "badExample": "크기를 1.4까지 키워 주변 문장을 덮거나 여러 번 흔들려 읽기를 방해한다", "avoid": [ "본문 전체에 반복 적용하지 않는다", "초과 크기가 주변 요소와 겹치지 않게 여백을 둔다" ], "params": [ { "name": "도착 지속", "default": "1.4s", "range": "0.8~1.6s", "note": "초과와 복귀를 모두 포함한다" }, { "name": "Back 계수", "default": "1.5", "range": "1.0~2.0", "note": "1.5일 때 최대 scale 1.08이다" }, { "name": "크기 범위", "default": "0→1.00", "range": "0~1.00", "note": "중심을 기준으로 크기를 바꾼다" }, { "name": "시작 시각", "default": "0.30s", "range": "0.20~0.40s", "note": "완료 뒤 3초까지 정지한다" } ], "ease": "back.out(1.5)", "snippet": "const state = {t:0};\nconst spring = t => 1 + 2.5*(t-1)**3 + 1.5*(t-1)**2;\ntl.to(state, {t:1, duration:1.4, ease:'none', onUpdate:() => {\n gsap.set('#card', {scale:spring(state.t)});\n update(); // 누적 길이로 그래프 strokeDashoffset과 값 표시 동기화\n}}, 0.3);", "pairsWith": [ "anticipation", "squash-stretch", "easing-curves" ], "engines": { "hyperframes": "단일 프록시 진행률로 블록 scale과 그래프를 갱신해 seek를 동기화한다", "reelforge": "등장 비트의 scale에 back.out(1.5)를 적용하고 1.4초 뒤 값을 1에 고정한다", "scrolline": "스크롤 진행률을 back.out(1.5)에 넣고 목표 크기보다 8% 큰 영역의 여백을 확보한다" }, "prompts": { "claude": "<대상>을 먹 블록으로 표현하고 0.3초부터 1.4초 동안 scale 0에서 1로 back.out(1.5) 도착을 만들어줘. 최대 크기는 1.08이며 옆 시간 그래프와 크기 숫자를 같은 프록시 값으로 갱신해. 그래프의 1.00 초과 구간만 주홍으로 그리고 2.05초부터 3초까지 완성 상태를 유지해.", "codex": "<파일>의 .scene에 먹 블록과 scale 시간 그래프를 배치해. paused GSAP 타임라인의 0.3초부터 1.4초 동안 t를 0→1로 구동하고 scale=1+2.5*(t-1)^3+1.5*(t-1)^2를 적용해. SVG pathLength 1의 strokeDashoffset을 누적 경로 길이로 갱신하되 소수 값을 반올림하지 마. 0.73초에 부분 그래프, 1.14초에 최대 1.08, 2.9초에 1.00 안착과 주홍 초과 구간을 캡처해 확인해." }, "sources": [ { "title": "GSAP Easing 공식 문서", "url": "https://gsap.com/docs/v3/Eases/", "license": "문서 참조" }, { "title": "Adobe", "url": "https://helpx.adobe.com/premiere/desktop/add-video-effects/types-of-effects/transitions.html", "license": "unknown" }, { "title": "pixel-point/animate-text", "url": "https://github.com/pixel-point/animate-text", "license": "unknown" }, { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "jschr/textillate", "url": "https://github.com/jschr/textillate", "license": "MIT" }, { "title": "motion-canvas/motion-canvas", "url": "https://github.com/motion-canvas/motion-canvas", "license": "MIT" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/spring-pop/registry-item.json", "license": "Apache-2.0" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/spring-overshoot/index.html", "mp4": "effects/spring-overshoot/clip.mp4", "gif": "effects/spring-overshoot/preview.gif", "poster": "effects/spring-overshoot/poster.jpg" }, "no": "004", "oneLinerEn": "An object briefly exceeds its target scale before returning and settling.", "conveysEn": "Shows inertia and restoring force as the object settles.", "useWhenEn": [ "Reveal a small object or settle it into a selected state.", "Explain overshoot and recovery with a graph." ], "promptsEn": { "claude": "Represent as an ink-black block and animate scale from 0 to 1 with back.out(1.5), starting at 0.3 seconds over 1.4 seconds. The maximum scale is 1.08. Update the adjacent time graph and scale readout from the same proxy value. Draw only the graph segment above 1.00 in vermilion, and hold the completed state from 2.05 to 3 seconds.", "codex": "Place an ink-black block and a scale-over-time graph in .scene in . In a paused GSAP timeline, drive t from 0 to 1 starting at 0.3 seconds over 1.4 seconds, and apply scale=1+2.5*(t-1)^3+1.5*(t-1)^2. Update strokeDashoffset on an SVG with pathLength 1 using cumulative path length, without rounding fractional values. Capture at 0.73 seconds for the partial graph, 1.14 seconds for the maximum 1.08, and 2.9 seconds for the settled 1.00 scale and vermilion overshoot segment." }, "variants": [ "Elastic spring slide", "spring-scale-in", "퍼센트 성과", "순위", "배수 강조", "충격 대형 숫자 등장", "animate-text/spring-scale-in", "단일 오버슈트", "다중 탄성 왕복", "무오버슈트 안착" ] }, { "slug": "squash-stretch", "family": "principles", "ko": "스쿼시 앤 스트레치", "en": "Squash & Stretch", "aka": [ "압축과 신장", "부피 유지 변형", "Text Squash Stretch", "텍스트 눌림과 늘어남", "Squash and stretch", "눌림과 늘어남", "스쿼시와 스트레치", "Squash and Stretch" ], "oneLiner": "속도가 붙으면 늘어나고 충돌하면 납작해지되 두 축의 배율 곱을 일정하게 유지하는 변형", "conveys": "탄성, 무게, 충돌의 힘", "purposes": [ "설명", "강조", "피드백" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "공이나 유연한 캐릭터의 충돌을 보여 줄 때", "작은 반발에 탄성을 더할 때" ], "goodExample": "340px 낙하 중 세로 배율 1.55로 늘어나고 착지 순간 0.55로 눌리며 가로는 역수로 변한다", "badExample": "두 축을 함께 늘려 공의 부피가 커지거나 바닥을 뚫고 내려간다", "avoid": [ "단단한 금속 물체에 큰 변형을 주지 않는다", "세로와 가로 배율을 독립적으로 tween하지 않는다" ], "params": [ { "name": "낙하 거리", "default": "340px", "range": "180~400px", "note": "바닥과 공의 하단을 맞춘다" }, { "name": "늘어남 배율", "default": "1.55", "range": "1.2~1.7", "note": "세로 배율, 가로는 역수" }, { "name": "압축 배율", "default": "0.55", "range": "0.5~0.8", "note": "첫 충돌의 세로 배율" }, { "name": "압축 지속", "default": "0.10s", "range": "0.07~0.15s", "note": "충돌 직후 짧게 눌린다" }, { "name": "반발 높이", "default": "210px", "range": "100~250px", "note": "첫 낙하보다 낮게 튄다" } ], "ease": "power2.in 낙하 / power2.out 반발", "snippet": "const shape = {s:1};\nconst deform = () => gsap.set('#ball',{scaleX:1/shape.s,scaleY:shape.s});\ntl.to('#fall',{y:340,duration:.85,ease:'power2.in'},.3);\ntl.to(shape,{s:1.55,duration:.55,ease:'power2.inOut',onUpdate:deform},.3);\ntl.to(shape,{s:.55,duration:.1,ease:'power2.out',onUpdate:deform},1.15);\ntl.to('#fall',{y:130,duration:.4,ease:'power2.out'},1.36);\ntl.to(shape,{s:1.35,duration:.15,ease:'power2.out',onUpdate:deform},1.36);", "pairsWith": [ "anticipation", "arc-motion", "spring-overshoot" ], "engines": { "hyperframes": "세로 배율 프록시 하나를 tween하고 onUpdate에서 가로 역수를 설정한다. 공의 변형 원점은 하단이다", "reelforge": "낙하, 충돌, 반발 비트마다 세로 배율을 지정하고 가로를 역수로 계산한다", "scrolline": "진행률 구간별 낙하와 압축을 계산하고 역수 배율로 부피를 유지한다" }, "prompts": { "claude": "GSAP으로 <대상> 공을 0.30초부터 0.85초 동안 340px 떨어뜨려줘. 세로 배율은 0.55초 동안 1.55로 늘리고 1.15초 충돌부터 0.10초 동안 0.55로 눌러. 가로 배율은 항상 세로 배율의 역수로 설정하고 하단을 변형 원점으로 해. 1.36초부터 210px 반발한 뒤 2.46초까지 바닥에서 원형으로 돌아오고 3.00초까지 정지해.", "codex": "<파일>의 낙하 장면에 세로 배율 프록시를 넣고 onUpdate에서 scaleX=1/s, scaleY=s로 설정해. 0.30초부터 340px 낙하, 1.15초에 s 0.55 압축, 1.36초에 210px 반발을 적용해. 0.73초, 1.23초, 1.73초, 2.90초 캡처에서 늘어남, 바닥 압축, 반발, 최종 원형을 확인하고 배율 곱이 1인지 검증해." }, "sources": [ { "title": "Twelve basic principles of animation, Squash and stretch", "url": "https://en.wikipedia.org/wiki/Twelve_basic_principles_of_animation", "license": "개념 인용" }, { "title": "codrops/LetterEffects", "url": "https://github.com/codrops/LetterEffects", "license": "unknown" }, { "title": "codrops/OnScrollTypographyAnimations", "url": "https://github.com/codrops/OnScrollTypographyAnimations", "license": "MIT" }, { "title": "codrops/OnScrollLetterAnimations", "url": "https://github.com/codrops/OnScrollLetterAnimations", "license": "MIT" }, { "title": "codrops/TextBlockTransitions", "url": "https://github.com/codrops/TextBlockTransitions", "license": "MIT" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/hw-box-label/registry-item.json", "license": "Apache-2.0" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Eases/CustomBounce/", "license": "GSAP Standard License" }, { "title": "motiondesign.school", "url": "https://motiondesign.school/blog/animation-principles-in-logo-animation/", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/squash-stretch/index.html", "mp4": "effects/squash-stretch/clip.mp4", "gif": "effects/squash-stretch/preview.gif", "poster": "effects/squash-stretch/poster.jpg" }, "no": "005", "oneLinerEn": "An object stretches as it gains speed and squashes on impact while keeping the product of its axis scales constant.", "conveysEn": "Conveys elasticity, weight, and the force of impact.", "useWhenEn": [ "Show the impact of a ball or flexible character.", "Add elasticity to a small bounce." ], "promptsEn": { "claude": "Use GSAP to drop the ball 340px starting at 0.30 seconds over 0.85 seconds. Stretch its vertical scale to 1.55 over 0.55 seconds, then squash it to 0.55 over 0.10 seconds starting at the 1.15-second impact. Always set horizontal scale to the reciprocal of vertical scale and use the bottom as the transform origin. Starting at 1.36 seconds, bounce it 210px upward, then return it to a circular shape on the floor by 2.46 seconds. Hold until 3.00 seconds.", "codex": "Add a vertical-scale proxy to the falling scene in and set scaleX=1/s and scaleY=s in onUpdate. Start the 340px drop at 0.30 seconds, squash to s 0.55 at 1.15 seconds, and bounce 210px upward at 1.36 seconds. Capture at 0.73, 1.23, 1.73, and 2.90 seconds to check the stretch, floor squash, bounce, and final circle. Verify that the product of the scales is 1." }, "variants": [ "세로 늘림", "가로 압축", "줄 전체 압축", "스크롤 신장", "착지 압축", "도약 신장", "아이콘 팝", "이동 신장", "누름", "도약" ] }, { "slug": "stagger", "family": "principles", "ko": "스태거", "en": "Stagger", "aka": [ "순차 등장", "시간차 등장", "Grid card assembly", "격자 카드 조립", "Spatial stagger wave", "공간 스태거 파동", "Grid Ripple", "격자 파문" ], "oneLiner": "여러 요소를 일정한 시간차로 차례로 등장시키는 움직임", "conveys": "요소의 순서와 하나의 묶음이라는 관계를 보여준다", "purposes": [ "순서·흐름", "설명" ], "media": [ "설명 영상", "발표", "웹 UI" ], "level": "기본", "useWhen": [ "격자나 목록의 읽는 순서를 보여줄 때", "여러 요소의 동시 등장을 분산할 때" ], "goodExample": "4열 3행의 블록 12개가 0.06초 간격으로 24px 떠오르며 나타난다", "badExample": "요소마다 0.4초를 기다리게 해 목록을 읽기까지 오래 걸린다", "avoid": [ "큰 목록은 총 등장 시간을 1.5초 이내로 제한한다", "읽는 순서와 다른 방향의 스태거를 피한다" ], "params": [ { "name": "요소 간 간격", "default": "0.06s", "range": "0.04~0.10s", "note": "행 우선으로 12개를 순회한다" }, { "name": "등장 지속", "default": "0.55s", "range": "0.35~0.70s", "note": "각 요소에 같은 지속 시간을 적용한다" }, { "name": "세로 이동", "default": "24px", "range": "16~40px", "note": "아래에서 제자리로 이동한다" }, { "name": "시작 시각", "default": "0.30s", "range": "0.20~0.40s", "note": "준비 후 첫 요소가 출발한다" } ], "ease": "power3.out", "snippet": "const tl = Motion.timeline();\ntl.fromTo('.block', {y:24, opacity:0},\n {y:0, opacity:1, duration:0.55, stagger:0.06, ease:'power3.out'}, 0.3);\ntl.to('#note', {opacity:1, duration:0.2}, 1.65);\nMotion.ready();", "pairsWith": [ "motion-hierarchy", "char-stagger", "overlapping-action" ], "engines": { "hyperframes": "한 paused 타임라인에 DOM 순서대로 stagger 0.06초를 적용한다", "reelforge": "격자 요소를 행 우선 배열로 두고 등장 시작 시각을 0.06초씩 늦춘다", "scrolline": "12개 요소의 시작 진행률을 일정하게 벌려 아래에서 위로 드러낸다" }, "prompts": { "claude": "<대상>을 4열 3행의 12개 먹 블록으로 배치해 스태거를 만들어줘. 0.3초에 시작하고 각 요소는 y 24px에서 0px, opacity 0에서 1로 0.55초 동안 power3.out으로 움직이며 간격은 0.06초로 해. 각 칸 아래 순번을 고정하고 마지막 칸만 주홍으로 표시하며 1.85초부터 3초까지 완성 상태를 유지해.", "codex": "<파일>의 .scene에 4열 3행 블록 12개와 고정 순번을 배치해. GSAP paused 타임라인의 0.3초에 y 24→0, opacity 0→1, duration 0.55, stagger 0.06, ease power3.out을 적용해. 0.73초와 1.23초를 캡처해 행 우선 등장과 마지막 주홍 칸을 확인하고 2.9초에 잘림 없이 정지하는지 검증해." }, "sources": [ { "title": "GSAP stagger 공식 문서", "url": "https://gsap.com/resources/getting-started/Staggers/", "license": "문서 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/grid-card-assemble/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/logo-wall/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/trust-strip/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/mk-placeholder-grid/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/heygen-avatar-promo-card/registry-item.json", "license": "Apache-2.0" }, { "title": "juliangarnier/anime", "url": "https://animejs.com/documentation/utilities/stagger", "license": "MIT" }, { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-staggered-grid", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/stagger/index.html", "mp4": "effects/stagger/clip.mp4", "gif": "effects/stagger/preview.gif", "poster": "effects/stagger/poster.jpg" }, "no": "006", "oneLinerEn": "Multiple elements appear in sequence with a consistent delay between them.", "conveysEn": "Communicates element order and their relationship as a group.", "useWhenEn": [ "Show the reading order of a grid or list.", "Spread out the entrance of multiple elements." ], "promptsEn": { "claude": "Arrange as 12 ink-black blocks in 4 columns and 3 rows and add a stagger. Start at 0.3 seconds. Animate each element from y 24px to 0px and opacity 0 to 1 over 0.55 seconds with power3.out and a 0.06-second stagger. Keep sequence numbers fixed below each cell, make only the last cell vermilion, and hold the completed state from 1.85 to 3 seconds.", "codex": "Place 12 blocks in 4 columns and 3 rows with fixed sequence numbers in .scene in . At 0.3 seconds in a paused GSAP timeline, apply y 24 to 0, opacity 0 to 1, duration 0.55, stagger 0.06, and ease power3.out. Capture at 0.73 and 1.23 seconds to check the row-major entrance order and final vermilion cell. Verify at 2.9 seconds that everything is stationary without clipping." }, "variants": [ "격자 카드 조립", "목록 순차 등장", "중앙 방사", "가장자리 수렴", "가로 파동", "세로 파동", "비선형 지연", "Background Ripple Effect", "RippleGrid", "Interactive Grid Pattern" ] }, { "slug": "arc-motion", "family": "principles", "ko": "아크", "en": "Arcs", "aka": [ "곡선 이동", "포물선 궤적", "Arc layout transfer", "곡선 레이아웃 이동", "Projectile arc", "포물선 발사", "Directional acceleration", "방향 가속 궤적" ], "oneLiner": "물체가 직선 대신 휘어진 궤적을 따라 이동하는 기법", "conveys": "회전과 반발에서 나오는 자연스러운 흐름", "purposes": [ "설명", "비교", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "기본", "useWhen": [ "공의 반발이나 던지기 궤적을 설명할 때", "직선 이동보다 유연한 공간 이동을 만들 때" ], "goodExample": "점이 770px 이동하면서 높이 230px의 포물선을 그리고 지나간 자리에 점선이 남는다", "badExample": "높이를 지나치게 키워 점이 화면 밖으로 나가거나 궤적의 방향을 읽을 수 없다", "avoid": [ "정확한 직선 추적을 설명할 때 사용하지 않는다", "점선 궤적을 움직이는 점보다 강하게 강조하지 않는다" ], "params": [ { "name": "수평 거리", "default": "770px", "range": "300~850px", "note": "양 끝점의 수평 간격" }, { "name": "최고 높이", "default": "230px", "range": "80~260px", "note": "시작과 도착 기준선에서의 높이" }, { "name": "지속 시간", "default": "1.65s", "range": "1.0~2.0s", "note": "곡선 전체의 이동 시간" }, { "name": "궤적 점 간격", "default": "진행률 0.025", "range": "0.02~0.05", "note": "미리 계산한 점을 통과 시각에 표시" } ], "ease": "none, 포물선 위치 수식", "snippet": "const progress = {p:0};\ntl.to('#straight',{x:770,duration:1.65,ease:'none'},.3);\ntl.to(progress,{p:1,duration:1.65,ease:'none',onUpdate:()=>\n gsap.set('#arc',{x:770*progress.p,y:-4*230*progress.p*(1-progress.p)})\n},.3);", "pairsWith": [ "squash-stretch", "anticipation", "timing-spacing" ], "engines": { "hyperframes": "진행률 프록시 하나로 x와 포물선 y를 계산한다. MotionPath 플러그인은 필요 없다", "reelforge": "수평 거리와 최고 높이를 파라미터로 두고 진행률별 위치를 계산한다", "scrolline": "스크롤 진행률 p를 x=D*p, y=-4*H*p*(1-p)에 직접 대입한다" }, "prompts": { "claude": "GSAP으로 <대상> 점을 0.30초부터 1.65초 동안 오른쪽 770px 옮기며 최고 높이 230px의 포물선을 그려줘. 진행률 p에 따라 x=770*p, y=-4*230*p*(1-p)를 계산하고 0.025 간격의 회색 궤적 점을 통과한 뒤 남겨. 위에는 같은 시간의 회색 직선 비교를 두고 2.35초부터 3.00초까지 도착 상태를 유지해.", "codex": "<파일>의 아래 이동 점에 프록시 p를 0에서 1로 tween하는 아크를 넣어. 시작 0.30초, 지속 1.65초, ease none으로 x 770*p와 y -4*230*p*(1-p)를 적용하고 궤적 점 41개를 순차 표시해. 0.73초와 1.23초에서 상승과 정점 근처를 확인하고 2.23초와 2.90초에서 완성 점선과 도착 위치가 같은지 캡처해 확인해." }, "sources": [ { "title": "Twelve basic principles of animation, Arc", "url": "https://en.wikipedia.org/wiki/Twelve_basic_principles_of_animation", "license": "개념 인용" }, { "title": "motiondivision/motion", "url": "https://motion.dev/docs/react-layout-animations", "license": "MIT" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Plugins/MotionPathPlugin/", "license": "GSAP Standard License" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Plugins/Physics2DPlugin/", "license": "GSAP Standard License" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/arc-motion-path/registry-item.json", "license": "Apache-2.0" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/animate-in-after-effects/animation-keyframes/keyframe-interpolation.html", "license": "unknown" }, { "title": "motion dictionary 1-principles.md#3. 디즈니 12원칙 전체", "url": "", "license": "own" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/offset-path-traveler/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/arc-motion/index.html", "mp4": "effects/arc-motion/clip.mp4", "gif": "effects/arc-motion/preview.gif", "poster": "effects/arc-motion/poster.jpg" }, "no": "007", "oneLinerEn": "An object follows a curved trajectory instead of a straight line.", "conveysEn": "Conveys the natural flow of rotation and bouncing motion.", "useWhenEn": [ "Explain the trajectory of a bouncing or thrown ball.", "Create more fluid movement through space than a straight-line translation." ], "promptsEn": { "claude": "Use GSAP to move the dot 770px to the right starting at 0.30 seconds over 1.65 seconds, following a parabola with a peak height of 230px. For progress p, calculate x=770*p and y=-4*230*p*(1-p). Leave gray trajectory dots behind after passing them, spaced at 0.025 progress intervals. Above it, show a gray straight-line comparison with the same timing. Hold the arrival state from 2.35 to 3.00 seconds.", "codex": "Add an arc to the lower moving dot in by tweening proxy p from 0 to 1. Start at 0.30 seconds with duration 1.65 seconds and ease none. Apply x 770*p and y -4*230*p*(1-p), and reveal 41 trajectory dots sequentially. Capture at 0.73 and 1.23 seconds to check the ascent and near-apex position. Capture at 2.23 and 2.90 seconds to verify identical completed dotted paths and arrival positions." }, "variants": [ "재배치 곡선", "상향 호", "하향 호", "한 발", "분수", "폭죽", "역재생", "바람", "가로 중력", "마찰 동반" ] }, { "slug": "follow-through", "family": "principles", "ko": "팔로스루", "en": "Follow-through", "aka": [ "후속 동작", "관성 잔동작", "Follow through", "Follow Through", "후행 정착", "Follow Through Settle", "후속 움직임 정착", "Follow Through and Overlapping Action" ], "oneLiner": "몸통이 멈춘 뒤 붙어 있는 끝부분이 관성으로 더 움직였다가 늦게 정지하는 기법", "conveys": "대상의 유연함과 관성. 정지 순간에도 힘이 남아 있음을 보여 준다", "purposes": [ "설명", "강조", "분위기" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "몸통에 붙은 깃발이나 꼬리의 관성을 표현할 때", "갑작스러운 정지에 무게와 유연함을 더할 때" ], "goodExample": "먹 막대가 1.05초에 멈춘 뒤 위의 주홍 판만 38도 더 기울었다가 진폭을 줄이며 2.23초에 멈춘다", "badExample": "몸통까지 계속 흔들려 무엇이 먼저 멈췄는지 구별하기 어렵다", "avoid": [ "끝부분의 연결점이 몸통에서 떨어지지 않게 한다", "흔들림 진폭을 계속 같은 크기로 반복하지 않는다" ], "params": [ { "name": "몸통 이동", "default": "650px", "range": "300~750px", "note": "몸통은 한 번 이동하고 정지" }, { "name": "몸통 지속", "default": "0.75s", "range": "0.5~1.1s", "note": "none으로 이동 후 급정지" }, { "name": "이동 중 기울기", "default": "-24°", "range": "-12~-30°", "note": "진행 방향 반대로 따라온다" }, { "name": "정지 후 최대각", "default": "38°", "range": "20~45°", "note": "관성으로 진행 방향을 넘는다" }, { "name": "감쇠 각도", "default": "-20°, 11°, -6°, 3°, 0°", "range": "2~3회 진동", "note": "매 반주기마다 진폭을 줄인다" } ], "ease": "none 몸통 / power2.out 관성 / power1.inOut 감쇠", "snippet": "tl.to('#body',{x:650,duration:.75,ease:'none'},.3);\ntl.to('#flag',{rotation:-24,duration:.25,ease:'power2.out'},.3);\ntl.to('#flag',{rotation:38,duration:.18,ease:'power2.out'},1.05);\n[[-20,.22],[11,.20],[-6,.20],[3,.18],[0,.20]].forEach(([rotation,duration],i)=>{\n const start=[1.23,1.45,1.65,1.85,2.03][i];\n tl.to('#flag',{rotation,duration,ease:'power1.inOut'},start);\n});", "pairsWith": [ "overlapping-action", "anticipation", "spring-overshoot" ], "engines": { "hyperframes": "판을 몸통의 자식으로 두고 연결점에 transform-origin을 지정한다. 몸통 tween 뒤에 판 회전만 이어 간다", "reelforge": "몸통 이동 비트와 끝부분 감쇠 회전 비트를 분리해 끝부분만 늦게 정지시킨다", "scrolline": "몸통의 정지 진행률 이후 구간에 감쇠 회전 키프레임을 배치한다" }, "prompts": { "claude": "GSAP으로 <대상>의 몸통과 붙어 있는 얇은 판에 팔로스루를 넣어줘. 몸통은 0.30초부터 0.75초 동안 오른쪽 650px 이동한 뒤 급정지하고, 판은 이동 중 -24도로 기울여. 1.05초부터 판만 0.18초 동안 38도로 더 나간 뒤 -20, 11, -6, 3, 0도로 각각 0.22, 0.20, 0.20, 0.18, 0.20초 동안 감쇠시켜. 연결점은 고정하고 2.45초부터 3.00초까지 완성 상태를 유지해.", "codex": "<파일>에서 얇은 판을 몸통의 자식으로 두고 transform-origin을 하단 연결점으로 지정해. 몸통은 시작 0.30초, duration 0.75초, x 650px, ease none으로 급정지시키고 판은 1.05초부터 38도 관성 회전 후 -20, 11, -6, 3, 0도로 감쇠시켜 2.23초에 멈춰. 0.73초와 1.23초 캡처에서 판의 기울기가 반대 방향인지, 1.23초 이후 몸통 위치는 고정되는지, 2.50초와 2.90초에서는 모두 멈추는지 확인해." }, "sources": [ { "title": "Twelve basic principles of animation, Follow through and overlapping action", "url": "https://en.wikipedia.org/wiki/Twelve_basic_principles_of_animation", "license": "개념 인용" }, { "title": "motion dictionary 1-principles.md#3.1 함께 쓰지만 구분해야 할 하위 개념", "url": "", "license": "own" }, { "title": "motiondesign.school", "url": "https://motiondesign.school/blog/animation-principles-in-logo-animation/", "license": "unknown" }, { "title": "airbnb/lottie-web", "url": "https://github.com/airbnb/lottie-web", "license": "MIT" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" }, { "title": "nature-of-code/noc-book-2", "url": "https://natureofcode.com/physics-libraries/", "license": "unknown" }, { "title": "mrdoob/three.js", "url": "https://threejs.org/examples/#physics_ammo_rope", "license": "MIT" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/follow-through/index.html", "mp4": "effects/follow-through/clip.mp4", "gif": "effects/follow-through/preview.gif", "poster": "effects/follow-through/poster.jpg" }, "no": "008", "oneLinerEn": "An attached extremity continues moving through inertia after the main body stops, then settles later.", "conveysEn": "Conveys flexibility and inertia, showing that force remains after the main body stops.", "useWhenEn": [ "Show inertia in a flag or tail attached to a body.", "Add weight and flexibility to an abrupt stop." ], "promptsEn": { "claude": "Add follow-through to the body and attached thin panel of with GSAP. Move the body 650px right starting at 0.30 seconds over 0.75 seconds, then stop abruptly. Tilt the panel to -24 degrees during movement. Starting at 1.05 seconds, rotate only the panel farther to 38 degrees over 0.18 seconds, then damp it through -20, 11, -6, 3, and 0 degrees over 0.22, 0.20, 0.20, 0.18, and 0.20 seconds respectively. Keep the attachment point fixed and hold the completed state from 2.45 to 3.00 seconds.", "codex": "In , make the thin panel a child of the body and set transform-origin to its bottom attachment point. Move the body with start 0.30 seconds, duration 0.75 seconds, x 650px, and ease none, then stop abruptly. Starting at 1.05 seconds, rotate the panel to 38 degrees through inertia, then damp it through -20, 11, -6, 3, and 0 degrees, stopping at 2.23 seconds. Capture at 0.73 and 1.23 seconds to check opposite panel tilts. Verify that the body position stays fixed after 1.23 seconds and that everything is stationary at 2.50 and 2.90 seconds." }, "variants": [ "꼬리 정착", "장식 회전", "끝부분 흔들림", "매달린 물체", "스프링 사슬", "Lanyard", "탄성 줄", "다중 스프링", "꼬리 사슬" ] }, { "slug": "motion-hierarchy", "family": "principles", "ko": "모션 위계", "en": "Motion Hierarchy", "aka": [ "등장 위계", "정보 위계", "Reading order", "Motion hierarchy", "Negative Space Hold", "빈 공간 결론 홀드", "Coherence-driven background settling", "핵심 외 움직임 정착" ], "oneLiner": "중요한 정보부터 크기와 등장 시간을 달리해 읽는 순서를 만드는 움직임", "conveys": "제목이 먼저 주목받고 부제와 세부가 뒤따르는 정보 구조를 보여준다", "purposes": [ "순서·흐름", "설명", "주목 끌기" ], "media": [ "설명 영상", "발표", "스크롤덱", "웹 UI" ], "level": "기본", "useWhen": [ "제목과 근거를 단계적으로 읽게 할 때", "한 화면의 정보 중요도가 서로 다를 때" ], "goodExample": "84px 제목은 0.3초에 0.7초 동안, 34px 부제는 1.05초에 0.4초 동안, 21px 세부는 1.5초에 0.22초 동안 등장한다", "badExample": "제목과 본문이 동시에 같은 크기와 속도로 움직여 먼저 읽을 정보가 없다", "avoid": [ "낮은 위계의 세부가 제목보다 크게 움직이지 않게 한다", "등장 지연으로 읽는 시간이 2초 이상 길어지지 않게 한다" ], "params": [ { "name": "제목 시작·지속", "default": "0.30s / 0.70s", "range": "0.20~0.40s / 0.50~0.80s", "note": "84px 제목을 32px 아래에서 띄운다" }, { "name": "부제 시작·지속", "default": "1.05s / 0.40s", "range": "0.90~1.20s / 0.30~0.50s", "note": "34px 부제를 18px 아래에서 띄운다" }, { "name": "세부 시작·지속", "default": "1.50s / 0.22s", "range": "1.30~1.70s / 0.18~0.30s", "note": "21px 세부를 10px 아래에서 띄운다" }, { "name": "완성 홀드", "default": "0.73s", "range": "0.50~1.00s", "note": "순번 지시선까지 표시한 뒤 정지한다" } ], "ease": "power3.out / power2.out", "snippet": "const tl = Motion.timeline();\ntl.fromTo('#title', {y:32, opacity:0},\n {y:0, opacity:1, duration:0.70, ease:'power3.out'}, 0.30);\ntl.fromTo('#sub', {y:18, opacity:0},\n {y:0, opacity:1, duration:0.40, ease:'power3.out'}, 1.05);\ntl.fromTo('#detail', {y:10, opacity:0},\n {y:0, opacity:1, duration:0.22, ease:'power2.out'}, 1.50);\ntl.to('.lead', {opacity:1, duration:0.18, stagger:0.07}, 1.95);\nMotion.ready();", "pairsWith": [ "stagger", "progressive-disclosure", "mask-reveal" ], "engines": { "hyperframes": "제목·부제·세부를 한 타임라인의 0.30·1.05·1.50초에 배치한다", "reelforge": "텍스트 위계별 등장 비트를 나누고 아래 단계일수록 이동 거리와 지속을 줄인다", "scrolline": "제목·부제·세부의 시작 진행률을 순서대로 벌리고 마지막 읽기 구간을 둔다" }, "prompts": { "claude": "<대상>을 제목 84px, 부제 34px, 세부 21px의 세 단계로 배치해 모션 위계를 만들어줘. 제목은 0.3초에 y 32→0으로 0.7초, 부제는 1.05초에 y 18→0으로 0.4초, 세부는 1.5초에 y 10→0으로 0.22초 동안 투명에서 불투명으로 나타나게 해. 제목과 부제는 power3.out, 세부는 power2.out을 쓰고 오른쪽 1·2·3 지시선을 1.95초부터 표시하며 2.27초부터 3초까지 정지해.", "codex": "<파일>의 .scene에 84px 제목, 34px 부제, 21px 세부를 배치해. 단일 paused 타임라인에서 시작 0.30/1.05/1.50초, 지속 0.70/0.40/0.22초, y 이동 32/18/10px, opacity 0→1을 적용해. 0.73초에는 제목만, 1.23초에는 제목과 부제, 1.73초에는 세 단계가 모두 보이는지 캡처하고 2.9초에 1·2·3 지시선과 완성 홀드를 확인해." }, "sources": [ { "title": "Material Design, Understanding motion", "url": "https://m2.material.io/design/motion/understanding-motion.html", "license": "개념 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/beat-timeline/registry-item.json", "license": "Apache-2.0" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "GSAP Standard License" }, { "title": "motion dictionary 1-principles.md#6. 모션 위계·코레오그래피", "url": "", "license": "own" }, { "title": "Richard E. Mayer / Wiley", "url": "https://onlinelibrary.wiley.com/doi/10.1111/jcal.12197", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/motion-hierarchy/index.html", "mp4": "effects/motion-hierarchy/clip.mp4", "gif": "effects/motion-hierarchy/preview.gif", "poster": "effects/motion-hierarchy/poster.jpg" }, "no": "009", "oneLinerEn": "Differences in scale and entrance timing establish a reading order that prioritizes important information.", "conveysEn": "Communicates an information hierarchy in which the title draws attention first, followed by the subtitle and details.", "useWhenEn": [ "Guide viewers through a title and its supporting information in stages.", "Present information with different levels of importance on one screen." ], "promptsEn": { "claude": "Create motion hierarchy for with three levels: an 84px title, 34px subtitle, and 21px detail text. Reveal the title at 0.3 seconds over 0.7 seconds with y 32 to 0, the subtitle at 1.05 seconds over 0.4 seconds with y 18 to 0, and the details at 1.5 seconds over 0.22 seconds with y 10 to 0, each fading from transparent to opaque. Use power3.out for the title and subtitle and power2.out for the details. Show right-side leader lines labeled 1, 2, and 3 starting at 1.95 seconds, and hold from 2.27 to 3 seconds.", "codex": "Place an 84px title, 34px subtitle, and 21px detail text in .scene in . Use a single paused timeline with starts 0.30/1.05/1.50 seconds, durations 0.70/0.40/0.22 seconds, y offsets 32/18/10px, and opacity 0 to 1. Capture at 0.73 seconds to verify only the title is visible, at 1.23 seconds for the title and subtitle, and at 1.73 seconds for all three levels. At 2.9 seconds, check the leader lines labeled 1, 2, and 3 and the completed hold." }, "variants": [ "부모 먼저", "자식 먼저", "속성별 타이밍", "침묵 구간", "엔드카드", "단일 로고", "배경 정지", "보조 요소 퇴장", "비핵심 디밍" ] }, { "slug": "timing-spacing", "family": "principles", "ko": "타이밍과 간격", "en": "Timing & Spacing", "aka": [ "양파 껍질", "Onion skin", "프레임 간격", "Timing and spacing", "시간과 간격", "Weighted Deceleration", "무게별 감속", "weighted-deceleration-mass" ], "oneLiner": "같은 이동 시간 안에서 프레임 사이 위치 간격으로 속도 변화를 드러내는 움직임", "conveys": "위치 간격이 넓으면 빠르고 촘촘하면 느리다는 관계를 보여준다", "purposes": [ "비교", "설명" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "등속 이동과 감속 이동을 같은 조건으로 비교할 때", "이징의 속도 변화를 잔상으로 설명할 때" ], "goodExample": "920px를 1.4초 동안 이동하며 30fps 위치를 남기면 linear는 균등하고 power3.out은 도착점에서 촘촘해진다", "badExample": "이동 거리와 지속 시간이 다른 두 줄을 비교해 간격 차이의 원인을 알 수 없다", "avoid": [ "잔상을 현재 점보다 진하게 표시하지 않는다", "프레임 간격을 불규칙하게 샘플링하지 않는다" ], "params": [ { "name": "이동 거리", "default": "920px", "range": "600~940px", "note": "비교 줄의 거리를 동일하게 둔다" }, { "name": "이동 지속", "default": "1.4s", "range": "1.0~1.8s", "note": "두 줄을 같은 시각에 시작하고 끝낸다" }, { "name": "잔상 샘플링", "default": "30fps", "range": "24~60fps", "note": "매 1/30초의 위치를 고정 잔상으로 남긴다" }, { "name": "감속 이징", "default": "power3.out", "range": "power2.out~power4.out", "note": "도착점 가까이 잔상 간격이 좁아진다" } ], "ease": "none / power3.out", "snippet": "const ease = gsap.parseEase('power3.out');\nfor (let i=0; i<=42; i++) {\n const dot = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n dot.setAttribute('cx', 20+920*ease(i/42)); dot.setAttribute('cy', 35);\n dot.setAttribute('r', 4.5); dot.setAttribute('class', 'trail');\n document.querySelector('#easeTrail').appendChild(dot);\n tl.set(dot, {opacity:0.40}, 0.3+i/30);\n}\ntl.to('#linearDot', {x:920, duration:1.4, ease:'none'}, 0.3);\ntl.to('#easeDot', {x:920, duration:1.4, ease:'power3.out'}, 0.3);", "pairsWith": [ "easing-curves", "arc-motion", "stagger" ], "engines": { "hyperframes": "잔상을 미리 계산하고 1/30초 간격의 timeline set으로 드러내 seek를 재현한다", "reelforge": "등속·감속 두 트랙을 같은 지속과 거리로 두고 프레임 위치를 겹쳐 표시한다", "scrolline": "진행률을 42등분해 각 위치의 잔상을 표시하고 linear와 감속 줄을 비교한다" }, "prompts": { "claude": "<대상>을 위쪽 회색 linear 점과 아래쪽 주홍 power3.out 점으로 비교해줘. 두 점 모두 0.3초에 출발해 920px를 1.4초 동안 이동하고 매 1/30초 위치에 반지름 4.5px의 먹 잔상을 남겨. 잔상은 제자리에 유지하고 2.05초부터 3초까지 정지해 끝에서 좁아지는 간격을 읽게 해.", "codex": "<파일>의 .scene에 같은 길이의 위·아래 트랙을 만들고 0.3초부터 x 0→920을 1.4초 동안 실행해. 위는 none, 아래는 power3.out이며 i=0~42의 잔상을 i/30초마다 tl.set으로 표시해. 0.73초에 이동 속도 차이, 1.73초에 균등 간격과 도착점에 모인 잔상, 2.9초에 완성 홀드를 캡처해 검증해." }, "sources": [ { "title": "Disney 12 principles, Timing", "url": "https://en.wikipedia.org/wiki/Twelve_basic_principles_of_animation", "license": "개념 인용" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "GSAP Standard License" } ], "runtime": "gsap", "render": "planned", "files": { "html": "effects/timing-spacing/index.html" }, "no": "010", "oneLinerEn": "Spacing between successive frame positions reveals changes in velocity within the same movement duration.", "conveysEn": "Shows that wider spacing means faster motion and tighter spacing means slower motion.", "useWhenEn": [ "Compare constant-speed and decelerating movement under identical conditions.", "Explain easing-related velocity changes with motion trails." ], "promptsEn": { "claude": "Compare as an upper gray linear dot and a lower vermilion power3.out dot. Start both at 0.3 seconds and move them 920px over 1.4 seconds. Leave ink-black trail dots with radius 4.5px at positions sampled every 1/30 second. Keep the trail dots in place and hold from 2.05 to 3 seconds so the tightening spacing near the endpoint is readable.", "codex": "Create upper and lower tracks of equal length in .scene in and animate x from 0 to 920 starting at 0.3 seconds over 1.4 seconds. Use none for the upper track and power3.out for the lower track. Reveal trail dots i=0 through 42 with tl.set at i/30-second intervals. Capture at 0.73 seconds to check the speed difference, at 1.73 seconds to check uniform spacing versus dots clustered near the endpoint, and at 2.9 seconds to verify the completed hold." }, "variants": [ "미디어 연속 이동", "정지 홀드", "하드 스톱", "비트 정지", "퍼센트 키프레임", "배열 키프레임", "누적 지연", "홀드", "재생 속도 램프", "슬로 모션" ] }, { "slug": "motion-blend", "family": "principles", "ko": "모션 블렌드", "en": "Motion Blending", "aka": [ "Rive Timeline Blend", "리브 타임라인 블렌드", "Animation blend" ], "oneLiner": "두 동작을 가중치로 섞어 한 동작에서 다음 동작으로 끊김 없이 넘어가는 전환", "conveys": "자세나 상태가 갑자기 튀지 않고 이어진다는 느낌", "purposes": [ "설명", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "고급", "useWhen": [ "캐릭터가 걷기에서 정지로 넘어갈 때", "두 포즈 사이에서 튀는 프레임 없이 연결해야 할 때" ], "goodExample": "걷는 포즈 A의 가중치 1→0, 정지 포즈 B의 가중치 0→1을 0.3초 동안 선형으로 교차시킨다", "badExample": "블렌드 없이 프레임이 튀거나, 블렌드를 1초 이상 끌어 두 동작이 뭉개져 보인다", "avoid": [ "블렌드 길이는 0.2~0.5초", "두 동작의 끝점(위치·각)이 크게 다르면 먼저 정렬한다", "가중치 합이 1이 되게 한다" ], "params": [ { "name": "블렌드 길이", "default": "0.3s", "range": "0.2~0.5s", "note": "교차 구간" }, { "name": "가중치", "default": "0→1", "range": "0~1", "note": "A는 1→0, 합은 항상 1" }, { "name": "이징", "default": "none", "range": "none~power1.inOut", "note": "선형이 기본" }, { "name": "시작 시각", "default": "동작 A 종료 0.1s 전", "range": "-0.2~0s", "note": "겹침으로 이어 붙임" } ], "ease": "none", "snippet": "const w = { v: 0 };\ntl.to(w, { v: 1, duration: 0.3, ease: 'none', onUpdate: () => {\n gsap.set('.figure', { x: 300 * (1 - w.v) + 0 * w.v, rotation: 8 * (1 - w.v) + 0 * w.v });\n} }, 0.9);", "pairsWith": [ "state-tween", "follow-through", "timing-spacing" ], "engines": { "hyperframes": "가중치 객체를 tween하고 onUpdate에서 결정론으로 계산한다. onUpdate가 seek에서도 불리는지 캡처로 확인하고, 안 되면 속성을 직접 tween한다", "reelforge": "브리프에 동작 A·B의 이름, 블렌드 길이, 가중치 곡선을 싣는다", "scrolline": "scrub에서는 진행률 자체를 가중치로 쓴다. w = progress이면 역방향도 자동으로 맞는다" }, "prompts": { "claude": "GSAP으로 <대상>의 <동작 A>에서 <동작 B>로 모션 블렌드를 넣어줘. 0.9초부터 0.3초 동안 가중치 v를 0에서 1로 선형으로 올리고 각 속성을 A*(1-v)+B*v로 계산해. 이징은 none, 가중치 합은 항상 1. paused 타임라인에서 seek해도 같은 값이 나오게 해.", "codex": "<파일>의 <대상>에 motion-blend를 적용해. w={v:0}를 0.9s부터 0.3s 동안 v 1까지 ease none으로 tween하고 onUpdate에서 x, rotation을 A*(1-v)+B*v로 set. 0.9초·1.05초·1.2초에 캡처해 값이 A, 중간, B로 바뀌는지와 seek 후에도 같은 값인지 확인해." }, "sources": [ { "title": "rive.app", "url": "https://rive.app/docs/editor/state-machine/states", "license": "unknown" }, { "title": "airbnb/lottie-web", "url": "https://github.com/airbnb/lottie-web", "license": "MIT" }, { "title": "rive-app/rive-runtime", "url": "https://github.com/rive-app/rive-runtime", "license": "MIT" } ], "runtime": "gsap", "variants": [ "걷기에서 정지", "포즈 교대", "속도 블렌드" ], "oneLinerEn": "Two motions are mixed by weight so one action flows into the next.", "conveysEn": "Poses connect without a jump, so the change feels continuous.", "useWhenEn": [ "When a character goes from walking to standing", "When two poses must join without a popping frame" ], "promptsEn": { "claude": "Add a motion blend to from to with GSAP. From 0.9s over 0.3s raise a weight v from 0 to 1 linearly and compute each property as A*(1-v)+B*v. Ease none, weights always sum to 1. Seeking on the paused timeline must give identical values.", "codex": "Apply motion-blend to in . Tween w={v:0} to v 1 over 0.3s from 0.9s with ease none and in onUpdate set x and rotation to A*(1-v)+B*v. Capture at 0.9s, 1.05s and 1.2s to check the values move from A to the midpoint to B and stay identical after a seek." }, "render": "planned", "files": {}, "no": "011" }, { "slug": "smear-frame", "family": "principles", "ko": "스미어 프레임", "en": "Smear Frame", "aka": [ "Smear Frame Motion", "스미어 프레임 운동" ], "oneLiner": "빠른 이동 중 대상이 이동 방향으로 길게 늘어나거나 여러 윤곽으로 퍼졌다 돌아오는 표현", "conveys": "속도와 타격의 강도. 프레임 사이가 비지 않고 이어져 보인다", "purposes": [ "강조", "설명" ], "media": [ "숏폼", "설명 영상" ], "level": "고급", "useWhen": [ "빠른 스와이프·휘두름·튀어나옴을 한두 프레임으로 강조할 때", "공이나 아이콘이 화면을 가로지르는 순간을 힘 있게 보여 줄 때" ], "goodExample": "공이 x 900px을 0.12초에 지나갈 때 scaleX 1.8로 늘어났다가 도착 직후 1로 복귀한다", "badExample": "늘어난 상태로 0.3초 이상 머물러 왜곡으로 보이거나, 느린 이동에도 스미어를 건다", "avoid": [ "스미어는 50~100ms 안에 끝낸다", "느린 이동(0.5초 이상)에는 쓰지 않는다", "늘림은 이동 방향 축으로만" ], "params": [ { "name": "스미어 길이", "default": "50~100ms", "range": "40~120ms", "note": "가장 빠른 구간" }, { "name": "신장 배율", "default": "1.8", "range": "1.4~2.2", "note": "이동 방향 축 scale" }, { "name": "이동 거리", "default": "900px", "range": "400~1200px", "note": "1920x1080 기준" }, { "name": "이징", "default": "power4.in", "range": "power3~power4.in", "note": "가속 뒤 즉시 정지" } ], "ease": "power4.in", "snippet": "tl.to('.ball', { x: 900, duration: 0.12, ease: 'power4.in' }, 0.4)\n .to('.ball', { scaleX: 1.8, transformOrigin: '100% 50%', duration: 0.06 }, 0.4)\n .to('.ball', { scaleX: 1, duration: 0.1, ease: 'power2.out' }, 0.52);", "pairsWith": [ "squash-stretch", "motion-blur", "motion-trails" ], "engines": { "hyperframes": "프레임 30fps에서 0.12초는 약 4프레임이다. 스미어 tween을 그 프레임 안에 넣고 seek 캡처로 늘어난 프레임을 확인한다", "reelforge": "브리프에 이동 거리·신장 배율·스미어 길이를 넣고 추가로 방향 축(x/y)을 지정한다", "scrolline": "scrub에서는 속도가 0에 가까워 스미어가 안 보인다. 진행률 변화량 비례로 scaleX를 걸거나 생략한다" }, "prompts": { "claude": "GSAP으로 <대상>이 x 900px을 0.12초에 지나갈 때 스미어를 넣어줘. 0.4초에 power4.in으로 이동시키고, 같은 시각에 scaleX를 1.8까지 0.06초에 늘려(transformOrigin 100% 50%) 0.52초부터 0.1초 동안 1로 되돌려. 이동 방향 축만 늘려. paused 타임라인 하나로 만들어.", "codex": "<파일>의 <대상>에 smear-frame을 적용해. x 900 이동(0.12s, power4.in, position 0.4)과 scaleX 1.8(0.06s), 복귀 scaleX 1(0.1s, position 0.52). 0.44초·0.5초·0.7초를 캡처해 늘어난 프레임에서 폭이 1.8배 안팎인지, 0.7초에 원래 비율인지 확인해." }, "sources": [ { "title": "schoolofmotion.com", "url": "https://schoolofmotion.com/courses/animation-bootcamp", "license": "unknown" }, { "title": "airbnb/lottie-web", "url": "https://github.com/airbnb/lottie-web", "license": "MIT" } ], "runtime": "svg", "variants": [ "신장 스미어", "다중 윤곽 스미어" ], "oneLinerEn": "During a fast move, the object stretches along its direction of travel and snaps back.", "conveysEn": "Speed and impact. Frames read as connected rather than as a jump.", "useWhenEn": [ "When a swipe, swing or pop-out should hit in one or two frames", "When a ball or icon crosses the screen with force" ], "promptsEn": { "claude": "Add a smear frame to as it moves 900px in x over 0.12s. At 0.4s move with power4.in, and in the same moment stretch scaleX to 1.8 over 0.06s with transformOrigin 100% 50%, then return to 1 over 0.1s from 0.52s. Stretch only along the travel axis. One paused timeline.", "codex": "Apply smear-frame to in . Tween x 900 (0.12s, power4.in, position 0.4), scaleX 1.8 (0.06s), and restore scaleX 1 (0.1s, position 0.52). Capture at 0.44s, 0.5s and 0.7s to check width is about 1.8x on the stretched frame and back to normal at 0.7s." }, "render": "planned", "files": {}, "no": "012" }, { "slug": "action-sequence", "family": "principles", "ko": "순차 동작", "en": "Action Sequence", "aka": [ "Sequential action chain", "순차 동작 연결", "Succession", "Sequence" ], "oneLiner": "한 동작이 끝난 뒤 다음 동작이 시작해 단계를 차례로 보여 주는 연결", "conveys": "과정의 선후 관계가 정확히 읽힌다. 한 번에 하나씩 이해하게 한다", "purposes": [ "순서·흐름", "설명" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "절차나 파이프라인 단계를 순서대로 보여 줄 때", "원인 다음 결과가 이어지는 설명" ], "goodExample": "입력 상자 등장 0.7초, 0.12초 쉼, 화살표 그리기 0.7초, 쉼, 결과 상자 등장 0.7초 순으로 이어진다", "badExample": "단계가 겹치거나 간격이 없어 어디서 끝나고 시작하는지 모르겠다", "avoid": [ "단계 간 간격 0.08~0.2초", "단계 수 5개 이하(넘으면 나누기)", "각 단계는 하나의 의미만" ], "params": [ { "name": "단계 길이", "default": "0.7s", "range": "0.5~1.0s", "note": "한 단계 tween" }, { "name": "단계 간격", "default": "0.12s", "range": "0.08~0.2s", "note": "끝과 다음 시작 사이" }, { "name": "단계 수", "default": "3", "range": "2~5", "note": "한 장면 기준" }, { "name": "이징", "default": "power2.out", "range": "power2~power3.out", "note": "도착이 분명" } ], "ease": "power2.out", "snippet": "const steps = ['.s1', '.s2', '.s3'];\nsteps.forEach((s, i) => tl.from(s, { opacity: 0, y: 24, duration: 0.7, ease: 'power2.out' }, 0.3 + i * (0.7 + 0.12)));", "pairsWith": [ "temporal-overlap", "segmented-explanation", "stagger" ], "engines": { "hyperframes": "단계 시작 시각을 i*(길이+간격)으로 계산해 넣으면 편집 후에도 순서가 유지된다. tl.from의 즉시 render 옵션에 주의", "reelforge": "브리프에 단계 배열, 단계 길이, 간격, 각 단계 해설 한 줄을 싣는다", "scrolline": "scrub에서는 단계를 진행률 구간 n등분에 매핑하고 사이에 짧은 정지 구간을 둔다" }, "prompts": { "claude": "GSAP으로 <대상> 단계 .s1, .s2, .s3를 순서대로 보여줘. 0.3초에 시작해 각 단계는 opacity 0, y +24px에서 0.7초 power2.out으로 나타나고 다음 단계는 앞 단계 끝나고 0.12초 뒤에 시작해. 마지막 뒤 0.6초 정지. paused 타임라인 하나로 만들어.", "codex": "<파일>의 <대상>에 action-sequence를 적용해. steps.forEach로 from({opacity 0, y 24}, 0.7s, power2.out)를 position 0.3+i*0.82에 건다. 0.5초·1.3초·2.2초를 캡처해 항상 한 단계만 움직이는지, 앞 단계가 남아 있는지 확인해." }, "sources": [ { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/composition.py", "license": "MIT" }, { "title": "motion dictionary 1-principles.md#5. 타이밍·간격·리듬 기본기", "url": "", "license": "own" }, { "title": "motion-canvas/motion-canvas", "url": "https://motioncanvas.io/docs/flow/", "license": "MIT" } ], "runtime": "gsap", "variants": [ "무간격 연결", "설명 휴지 연결" ], "oneLinerEn": "One action finishes and the next begins, showing steps in order.", "conveysEn": "The order of a process reads exactly. Viewers take one step at a time.", "useWhenEn": [ "When showing procedure or pipeline steps in order", "When a cause is followed by its result" ], "promptsEn": { "claude": "Reveal the steps .s1, .s2 and .s3 of in order with GSAP. Start at 0.3s. Each step animates from opacity 0, y +24px over 0.7s with power2.out, and the next starts 0.12s after the previous ends. Hold 0.6s after the last. One paused timeline.", "codex": "Apply action-sequence to in . With steps.forEach add from({opacity 0, y 24}, 0.7s, power2.out) at position 0.3 + i*0.82. Capture at 0.5s, 1.3s and 2.2s to check only one step moves at a time and earlier steps remain." }, "render": "planned", "files": {}, "no": "013" }, { "slug": "additive-motion", "family": "principles", "ko": "가산 모션", "en": "Additive Motion", "aka": [ "Group Rig Sway", "그룹 동반 흔들림", "null-parent-rig", "Additive layered motion", "가산 레이어 모션", "Secondary Action", "보조 동작" ], "oneLiner": "큰 경로를 따라 움직이는 대상에 작은 흔들림이나 회전이 동시에 더해진다", "conveys": "요소들이 하나의 그룹처럼 움직인다는 것을 알게 해 준다. 주 동작 위에 미세한 살아 있는 느낌이 얹힌다", "purposes": [ "분위기", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "아이콘이나 카드가 이동하면서 살짝 흔들리도록 해 무게를 줄 때", "여러 요소를 하나의 부모 리그로 묶어 함께 움직이게 할 때" ], "goodExample": "부모가 2초에 걸쳐 x 900px 이동하는 동안 자식이 주기 0.4초, 위치 진폭 6px, 회전 2도로 흔들린다", "badExample": "이동과 흔들림을 하나의 tween에 섞어 값을 덮어쓰거나, 진폭을 30px로 키워 술 취한 것처럼 보인다", "avoid": [ "주 경로와 보조 흔들림을 다른 요소(부모와 자식)에 나눠 건다", "보조 진폭은 주 이동 거리의 2% 이하" ], "params": [ { "name": "주경로 시간", "default": "2s", "range": "1.5~3s", "note": "부모의 이동" }, { "name": "보조 주기", "default": "0.4s", "range": "0.3~0.6s", "note": "자식 진동" }, { "name": "위치 진폭", "default": "6px", "range": "3~10px", "note": "y 방향" }, { "name": "회전", "default": "2도", "range": "1~3도", "note": "사인 곡선" } ], "ease": "sine.inOut", "snippet": "tl.to('.rig', { x: 900, duration: 2, ease: 'power2.inOut' }, 0.3); // 부모: 주경로\ntl.to('.item', { y: 6, rotation: 2, duration: 0.2, ease: 'sine.inOut', yoyo: true, repeat: 9 }, 0.3); // 자식: 0.4s 주기, 10 반주기", "pairsWith": [ "overlapping-action", "follow-through", "group-motion" ], "engines": { "hyperframes": "부모와 자식을 다른 tween으로 나누고 repeat 횟수를 명시해 timeline 길이를 고정한다. repeat: -1은 쓰지 않는다", "reelforge": "씬 워커 브리프에 부모 경로, 자식 진폭과 주기를 별도 파라미터 두 묶음으로 싣는다", "scrolline": "진행률에 주 경로를 연결하고, 흔들림은 진행률 0.05 폭 사인으로 만든다. 진폭은 픽셀 그대로 두고 주기만 조정한다" }, "prompts": { "claude": "<대상> 카드에 가산 모션을 넣어줘. 부모 .rig가 2초 동안 x 900px를 power2.inOut으로 이동하고, 자식 .item은 같은 구간에서 y 6px와 rotation 2도를 sine.inOut으로 0.2초 반주기 yoyo, 10회 왕복해. 둘은 별도 tween이라 서로 값을 덮어쓰지 않게 해.", "codex": "<파일>에 additive motion을 구현해. .rig x 900 / 2s / power2.inOut, .item y 6 rotation 2 / 0.2s / sine.inOut / yoyo repeat 9. 0.5초, 1.3초, 2.3초를 캡처해 카드가 큰 경로를 따라가는 동안 흔들림이 유지되는지, 도착 후 흔들림이 0으로 돌아오는지 확인해." }, "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/01-anchor-transform.md#null-parent-rig", "license": "Apache-2.0" }, { "title": "juliangarnier/anime", "url": "https://animejs.com/documentation/animation/tween-parameters/composition", "license": "MIT" }, { "title": "motion dictionary 1-principles.md#3. 디즈니 12원칙 전체", "url": "", "license": "own" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "GSAP Standard License" } ], "runtime": "gsap", "variants": [ "이동과 흔들림", "회전과 감쇠", "주경로와 소음" ], "oneLinerEn": "A small wobble or rotation is layered on top of an element following a larger path.", "conveysEn": "Makes elements read as one group and adds a subtle liveliness on top of the main motion.", "useWhenEn": [ "Give an icon or card weight by letting it sway while it travels.", "Bind several elements to one parent rig so they move together." ], "promptsEn": { "claude": "Add additive motion to the card in . The parent .rig moves x 900px over 2 seconds with power2.inOut, while the child .item adds y 6px and 2 degrees of rotation with sine.inOut, yoyo half-periods of 0.2s, 10 times. Use separate tweens so neither overwrites the other.", "codex": "Implement additive motion in : .rig x 900 / 2s / power2.inOut, .item y 6 and rotation 2 / 0.2s / sine.inOut / yoyo repeat 9. Capture at 0.5s, 1.3s and 2.3s and verify the wobble persists along the main path and returns to 0 after arrival." }, "render": "planned", "files": {}, "no": "014" }, { "slug": "beat-sync", "family": "principles", "ko": "비트 싱크", "en": "Beat Synchronization", "aka": [ "Beat landing", "박자에 착지", "Audio synchronized motion", "오디오 동기 모션", "Beat synchronization" ], "oneLiner": "요소의 도착, 크기 변화, 화면 컷이 음악의 특정 박자에 정확히 맞아떨어진다", "conveys": "음악과 화면의 사건이 하나로 일치해 리듬감과 완성도가 생긴다. 박자에서 어긋나면 바로 어색하게 느껴진다", "purposes": [ "순서·흐름", "주목 끌기" ], "media": [ "숏폼", "설명 영상", "제품 시연" ], "level": "기본", "useWhen": [ "음악 위에 카드나 문구가 차례로 등장하는 영상을 만들 때", "컷 전환을 박자에 맞춰 리듬을 살릴 때" ], "goodExample": "100BPM에서 박자 간격 600ms, 이동 400ms짜리 카드가 박 시각 0.6초, 1.2초, 1.8초에 정확히 도착한다. 도착 오차는 1프레임 이내다", "badExample": "이동이 시작하는 시각을 박에 맞춰 도착이 박보다 400ms 늦거나, 박자를 눈으로 대충 맞춰 매번 오차가 다르다", "avoid": [ "시작이 아니라 도착을 박에 맞춘다", "BPM은 분석 도구로 실측한다(감으로 입력 금지)" ], "params": [ { "name": "BPM", "default": "100", "range": "80~140", "note": "박 간격 60/BPM초" }, { "name": "박자 간격", "default": "600ms", "range": "428~750ms", "note": "BPM에서 계산" }, { "name": "이동 시간", "default": "400ms", "range": "250~500ms", "note": "박 간격보다 짧게" }, { "name": "허용 오차", "default": "1프레임", "range": "30fps 기준 33ms", "note": "이내" } ], "ease": "power3.out", "snippet": "const beat = 60 / 100; // 0.6s\n[1, 2, 3, 4].forEach((n, i) => {\n tl.from(`.card${i}`, { y: 60, opacity: 0, duration: 0.4, ease: 'power3.out' }, n * beat - 0.4); // 도착 = n*beat\n});", "pairsWith": [ "kinetic-beats", "audio-reactive-pulse", "stagger" ], "engines": { "hyperframes": "beats 분석 결과의 박 시각을 배열로 두고 tween 시작 = 박 시각 - duration으로 계산한다. paused 타임라인이라 seek해도 위치가 같다", "reelforge": "씬 워커 브리프에 BPM, 박 시각 배열, 도착 기준 요소를 싣는다. 컷 전환은 박 시각에 정확히 둔다", "scrolline": "음악 없이 스크롤이 진행되므로 박 대신 진행률 등분(0.25 간격)으로 도착 지점을 정한다" }, "prompts": { "claude": "<대상> 카드 4장이 100BPM 음악의 박에 맞춰 도착하게 GSAP 타임라인을 짜줘. 박 간격은 0.6초이고 카드 i의 도착 시각은 (i+1)*0.6초야. 각 카드는 y 60px 아래에서 0.4초, power3.out으로 올라오게 하고, 시작 시각은 도착 시각에서 0.4초를 뺀 값으로 계산해줘.", "codex": "<파일>의 등장 애니메이션을 비트 싱크로 고쳐줘. 100BPM, 박 간격 0.6초, 카드별 tween은 position = 도착 - 0.4, duration 0.4, ease power3.out. 0.6초, 1.2초, 1.8초 시점에 캡처해 각 카드가 정확히 최종 위치에 있는지, 그 직전 33ms 시점에는 아직 이동 중인지 확인해." }, "sources": [ { "title": "local/HyperFrames-skills", "url": "claude-skill:music-to-video/references/motion-primitive-catalog.md", "license": "unknown" }, { "title": "theatre-js/theatre", "url": "https://www.theatrejs.com/docs/latest/manual/audio", "license": "Apache-2.0 / AGPL-3.0 (구성요소별)" }, { "title": "motion dictionary 4-explainer-learning.md#C. 템포·리듬·편집과 비트 동기", "url": "", "license": "own" }, { "title": "theatre-js/theatre", "url": "https://www.theatrejs.com/docs/latest/concepts", "license": "Apache-2.0 / AGPL-3.0 (구성요소별)" } ], "runtime": "gsap", "variants": [ "도착 동기", "충돌 동기", "컷 동기", "팝 동기" ], "oneLinerEn": "Arrivals, scale changes and cuts land exactly on specific beats of the music.", "conveysEn": "Screen events and sound become one, giving rhythm and polish. Any drift from the beat feels wrong immediately.", "useWhenEn": [ "Cards or lines appear in sequence over music.", "Time cuts to the beat to strengthen rhythm." ], "promptsEn": { "claude": "Build a GSAP timeline where 4 cards of land on the beat of a 100 BPM track. The beat interval is 0.6 seconds and card i arrives at (i+1)*0.6s. Each card rises from 60px below over 0.4 seconds with power3.out, and its start time is arrival minus 0.4s.", "codex": "Convert the entrance animation in to beat sync: 100 BPM, 0.6s beat interval, per-card tween at position = arrival - 0.4, duration 0.4, ease power3.out. Capture at 0.6s, 1.2s and 1.8s to verify each card is exactly at its final position, and 33ms earlier to verify it is still moving." }, "render": "planned", "files": {}, "no": "015" }, { "slug": "bounce-landing", "family": "principles", "ko": "바운스 착지", "en": "Bounce Landing", "aka": [ "Bounce Drop", "낙하 바운스", "bounce-drop-physics", "drop-and-settle", "Bounce landing", "Collision Bounce", "충돌 바운스", "Ballistic bounce", "Bounce", "튀기기", "CustomBounce", "사용자 충돌" ], "oneLiner": "물체가 아래로 가속해 떨어진 뒤 점점 낮게 튀며 멈춘다", "conveys": "중력과 충돌을 느끼게 한다. 물체의 무게와 바닥이라는 기준면이 생긴다", "purposes": [ "피드백", "주목 끌기" ], "media": [ "웹 UI", "숏폼", "설명 영상" ], "level": "기본", "useWhen": [ "아이콘이나 카드가 위에서 떨어져 자리를 잡을 때", "완료나 도착을 경쾌하게 알릴 때" ], "goodExample": "120px 높이에서 떨어져 1초 동안 반발계수 0.55로 3번 튀고 멈춘다. 착지 순간 그림자가 진해진다", "badExample": "반발계수 0.9로 10번 넘게 튀어 멈추지 않고, 진지한 데이터 화면에서 장난스럽게 튄다", "avoid": [ "반동 4회 초과 금지", "금융이나 오류 같은 진지한 맥락에는 쓰지 않는다" ], "params": [ { "name": "낙하 높이", "default": "120px", "range": "80~200px", "note": "시작 y" }, { "name": "총 시간", "default": "1000ms", "range": "700~1400ms", "note": "bounce.out" }, { "name": "반발계수", "default": "0.55", "range": "0.4~0.65", "note": "반동 높이 감쇠" }, { "name": "반동", "default": "3회", "range": "2~4회", "note": "" } ], "ease": "bounce.out", "snippet": "tl.fromTo('.ball', { y: -120 }, { y: 0, duration: 1, ease: 'bounce.out' }, 0.3);\ntl.fromTo('.shadow', { scale: 0.6, opacity: 0.2 }, { scale: 1, opacity: 0.5, duration: 1, ease: 'bounce.out' }, 0.3);", "pairsWith": [ "squash-stretch", "spring-overshoot", "physical-exit" ], "engines": { "hyperframes": "bounce.out을 단일 tween에 쓴다. 그림자는 같은 시간에 별도 tween으로 건다. paused 타임라인이라 seek 결정론", "reelforge": "씬 브리프에 낙하 높이, 총 시간, 반발계수를 싣고 그림자 유무를 선택으로 둔다", "scrolline": "scrub에서는 반동 3회가 손 움직임에 묻힌다. 진행률 0.15 구간에 몰아서 재생한다" }, "prompts": { "claude": "<대상> 아이콘이 120px 위에서 떨어져 1초 동안 bounce.out으로 3번 튀고 멈추게 해줘. 착지 그림자는 같은 시간에 scale 0.6에서 1, opacity 0.2에서 0.5로 함께 변하게 해. 시작은 0.3초.", "codex": "<파일>의 등장 애니메이션을 바운스 착지로 바꿔줘. y -120에서 0, duration 1s, ease bounce.out, 그림자 scale 0.6에서 1. 0.5초, 0.8초, 1.3초 시점을 캡처해 바닥에 닿는 순간 그림자가 진해지는지, 반동 높이가 점점 낮아지는지 확인해." }, "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/02-easing-graph.md#bounce-drop-physics", "license": "Apache-2.0" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Eases/", "license": "GSAP Standard License" }, { "title": "motionscript.com", "url": "https://motionscript.com/articles/bounce-and-overshoot.html", "license": "unknown" }, { "title": "motion dictionary 1-principles.md#4.1 곡선의 읽는 법", "url": "", "license": "own" }, { "title": "local/embedded-captions", "url": "claude-skill:embedded-captions/references/motion-vocabulary.md", "license": "unknown" } ], "runtime": "gsap", "variants": [ "중력 낙하", "충돌 반동", "높이 감쇠" ], "oneLinerEn": "An object accelerates down, then bounces lower each time until it rests.", "conveysEn": "Conveys gravity and impact, and gives the object weight and a ground plane.", "useWhenEn": [ "An icon or card drops from above and settles.", "Announce arrival or completion with a lively touch." ], "promptsEn": { "claude": "Make the icon in drop from 120px above and bounce three times over 1 second with bounce.out. Animate the contact shadow at the same time, scale 0.6 to 1 and opacity 0.2 to 0.5. Start at 0.3 seconds.", "codex": "Change the entrance in to a bounce landing: y -120 to 0, duration 1s, ease bounce.out, shadow scale 0.6 to 1. Capture at 0.5s, 0.8s and 1.3s and verify the shadow darkens at contact and each rebound is lower than the last." }, "render": "planned", "files": {}, "no": "016" }, { "slug": "concurrent-motion", "family": "principles", "ko": "동시 동작", "en": "Concurrent Motion", "aka": [ "Concurrent action composition", "동시 동작 합성", "AnimationGroup" ], "oneLiner": "여러 대상이나 속성이 같은 시간대에 함께 움직이는 합성", "conveys": "동시에 일어나는 변화의 관계. 함께 바뀌는 것을 한 번에 읽는다", "purposes": [ "설명", "비교" ], "media": [ "설명 영상", "데이터 스토리", "발표" ], "level": "기본", "useWhen": [ "두 값이 동시에 반대로 움직이는 관계를 보여 줄 때", "위치와 색이 하나의 사건으로 함께 바뀔 때" ], "goodExample": "왼쪽 막대는 줄고 오른쪽 막대는 늘어나는 것이 정확히 같은 0.9초 동안 진행된다", "badExample": "각 요소의 시작 시각이 조금씩 달라 같은 사건으로 보이지 않는다", "avoid": [ "동시 대상 4개 이하(넘으면 그룹화)", "길이와 이징을 같게 하거나 의도적으로 다르게", "도착 시각을 맞춘다" ], "params": [ { "name": "총 길이", "default": "0.9s", "range": "0.6~1.2s", "note": "모든 대상 공통" }, { "name": "시작 차이", "default": "0ms", "range": "0~40ms", "note": "동시로 보이는 한도" }, { "name": "이징", "default": "power2.inOut", "range": "power1~power3.inOut", "note": "공통 적용" }, { "name": "홀드", "default": "0.6s", "range": "0.4~1.0s", "note": "도착 뒤 정지" } ], "ease": "power2.inOut", "snippet": "tl.to('.left', { height: 120, duration: 0.9, ease: 'power2.inOut' }, 0.3)\n .to('.right', { height: 360, duration: 0.9, ease: 'power2.inOut' }, 0.3)\n .to('.gauge', { rotation: 60, duration: 0.9, ease: 'power2.inOut' }, 0.3);", "pairsWith": [ "group-motion", "temporal-overlap", "state-tween" ], "engines": { "hyperframes": "같은 position 값 0.3을 여러 tween에 준다. 라벨을 쓰면 시작 시각 관리가 쉽다", "reelforge": "브리프에 동시 대상 목록과 공통 길이·이징을 싣는다", "scrolline": "scrub에서는 모든 대상이 같은 진행률을 공유한다. 서로 다른 ease를 쓰면 도착 시각이 어긋나므로 통일" }, "prompts": { "claude": "GSAP으로 <대상>의 .left, .right, .gauge가 정확히 같은 시간에 움직이게 해줘. 0.3초 시작, 0.9초 길이, power2.inOut 공통이고 .left는 높이 120px, .right는 360px, .gauge는 rotation 60도가 돼. 시작 시각이 어긋나지 않게 같은 position을 써. paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 concurrent-motion을 적용해. 세 tween 모두 position 0.3, duration 0.9, ease power2.inOut. 0.3초·0.75초·1.2초를 캡처해 세 대상의 진행률(현재값-시작값)/(목표값-시작값)이 서로 같은지 확인해." }, "sources": [ { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/composition.py", "license": "MIT" }, { "title": "motion-canvas/motion-canvas", "url": "https://motioncanvas.io/docs/flow/", "license": "MIT" } ], "runtime": "gsap", "variants": [ "완전 동시", "부분 겹침" ], "oneLinerEn": "Several targets or properties move in the same window of time.", "conveysEn": "Relationships between simultaneous changes. Things that change together are read at once.", "useWhenEn": [ "When two values move in opposite directions at the same time", "When position and color change as one event" ], "promptsEn": { "claude": "Make .left, .right and .gauge of move at exactly the same time with GSAP. Start 0.3s, duration 0.9s, ease power2.inOut for all; .left height 120px, .right 360px, .gauge rotation 60 degrees. Use the same position so starts do not drift. Paused timeline.", "codex": "Apply concurrent-motion to in . All three tweens at position 0.3, duration 0.9, ease power2.inOut. Capture at 0.3s, 0.75s and 1.2s and check the progress (current-start)/(target-start) is equal for all three." }, "render": "planned", "files": {}, "no": "017" }, { "slug": "continuous-motion", "family": "principles", "ko": "연속 이동", "en": "Continuous Motion", "aka": [ "Continuous Tangent Motion", "접선 속도 승계", "rolling-tension-chain", "scene-boundary-tangent-handoff", "Continue Motion", "속도 유지 연장", "loopOut continue" ], "oneLiner": "여러 정거장이나 장면을 지나면서 요소가 중간에 멈추지 않고 같은 흐름을 이어 간다", "conveys": "분리된 단계가 하나의 연속된 행동으로 읽힌다. 정지가 없으니 진행 방향에 대한 기대가 유지된다", "purposes": [ "순서·흐름", "전환" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "중급", "useWhen": [ "단계 A, B, C를 지나는 마커가 매번 멈추지 않고 이어 달려야 할 때", "장면 경계에서 속도를 이어 받게 할 때" ], "goodExample": "마커가 600ms 구간 세 개를 지나며 경계에서 속도가 같아 멈추지 않고 이어진다. 홀드 0ms", "badExample": "구간마다 power3.inOut을 걸어 매번 정지하고 출발해 끊긴다. 경계 속도가 달라 이어 붙인 자국이 보인다", "avoid": [ "구간별 ease-in-out 금지(경계에서 정지함)", "경계 속도를 계산해 맞춘다" ], "params": [ { "name": "구간 길이", "default": "600ms", "range": "400~900ms", "note": "동일 길이면 속도 계산이 쉽다" }, { "name": "경계 속도", "default": "동일", "range": "없음", "note": "전 구간 끝 속도 = 다음 구간 시작 속도" }, { "name": "정지 홀드", "default": "0ms", "range": "0~150ms", "note": "길수록 끊김" }, { "name": "구간 수", "default": "3", "range": "2~5", "note": "" } ], "ease": "none", "snippet": "// 세 정거장 x = 300, 900, 1500 을 하나의 연속 경로로 잇는다\ntl.to('.marker', { keyframes: [{ x: 300 }, { x: 900 }, { x: 1500 }], duration: 1.8, ease: 'none' }, 0.2);\n// 필요하면 ease 'sine.inOut'을 전체 1.8s 에만 건다: 경계마다 멈추지 않음", "pairsWith": [ "easing-curves", "speed-ramp", "match-on-action" ], "engines": { "hyperframes": "keyframes 하나로 전 구간을 잇고 ease는 전체에만 건다. 구간별 tween을 쓸 때는 경계 속도를 손으로 맞춘다", "reelforge": "씬 브리프에 정거장 좌표 목록과 전체 시간만 싣는다. 각 씬은 시작 속도를 받아 이어 받도록 한다", "scrolline": "진행률에 하나의 연속 경로를 매핑한다. scrub은 자연히 연속이므로 구간 경계 속도만 확인한다" }, "prompts": { "claude": "<대상> 마커가 정거장 세 곳(x 300, 900, 1500)을 정지 없이 지나가게 해줘. GSAP keyframes 하나로 전체 1.8초, ease none으로 잇고 전체 시작과 끝에만 짧은 감속(sine.inOut 0.3초)을 넣어. 구간별로 ease를 걸지 마.", "codex": "<파일>의 마커 이동을 연속 이동으로 바꿔줘. keyframes x 300 → 900 → 1500, duration 1.8초, ease none. 0.8초와 1.4초(경계 근처) 시점의 x 변화량이 앞뒤 프레임에서 같은지 캡처와 값 비교로 확인해 정지 구간이 없음을 검증해." }, "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/02-easing-graph.md#rolling-tension-chain", "license": "Apache-2.0" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/work-with-expressions/expression-language-reference/expression-language-reference.html", "license": "unknown" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/02-easing-graph.md#scene-boundary-tangent-handoff", "license": "Apache-2.0" }, { "title": "airbnb/lottie-web", "url": "https://github.com/airbnb/lottie-web", "license": "MIT" } ], "runtime": "gsap", "variants": [ "구간 접선 연결", "마지막 속도 연장" ], "oneLinerEn": "An element passes through several stops or scenes without pausing, keeping one flow.", "conveysEn": "Separate steps read as one continuous action. Without stops, the expectation of direction holds.", "useWhenEn": [ "A marker passing stations A, B and C must run through without stopping.", "Hand off velocity across a scene boundary." ], "promptsEn": { "claude": "Make the marker in pass three stops (x 300, 900, 1500) without stopping. Use a single GSAP keyframes tween of 1.8 seconds with ease none, adding a short ease only at the very start and end (sine.inOut, 0.3s). Do not set an ease per segment.", "codex": "Convert the marker movement in to continuous motion: keyframes x 300, 900, 1500, duration 1.8s, ease none. Capture near the boundaries at 0.8s and 1.4s and verify by value comparison that the per-frame x delta is the same before and after, proving there is no stop." }, "render": "planned", "files": {}, "no": "018" }, { "slug": "group-motion", "family": "principles", "ko": "그룹 이동", "en": "Group Motion", "aka": [ "Common-fate grouping", "동시 운동 그룹화", "Common fate / Group motion", "공통 운명" ], "oneLiner": "같은 그룹의 마크들이 같은 방향과 속도로 동시에 움직여 한 묶음으로 보이게 하는 표현", "conveys": "떨어져 있는 항목도 함께 움직이면 같은 무리로 읽힌다. 공통 운명의 원리", "purposes": [ "설명", "비교" ], "media": [ "데이터 스토리", "설명 영상", "웹 UI" ], "level": "기본", "useWhen": [ "산점도에서 어떤 점들이 같은 군집인지 보여 줄 때", "카드 여러 장을 하나의 카테고리로 묶어 옮길 때", "팀이나 세그먼트별로 점을 이동시킬 때" ], "goodExample": "파란 점 12개가 서로 다른 위치에서 동시에 x +240px 이동하고, 회색 점은 그대로여서 파란 점이 한 무리로 보인다", "badExample": "그룹 안에서 점마다 시작 시각이 다르거나 속도가 달라 한 묶음으로 읽히지 않는다", "avoid": [ "그룹 내 지연 0ms 유지", "그룹 간 지연은 0.1~0.3초", "동시에 움직이는 그룹은 3개 이하" ], "params": [ { "name": "길이", "default": "0.7s", "range": "0.5~1.0s", "note": "그룹 전체 이동 시간" }, { "name": "그룹 내 지연", "default": "0ms", "range": "0~20ms", "note": "같은 그룹은 동일 시작" }, { "name": "그룹 간 지연", "default": "160ms", "range": "100~300ms", "note": "그룹 A 뒤에 그룹 B" }, { "name": "이징", "default": "power2.inOut", "range": "power2~power3.inOut", "note": "그룹 전체에 동일 적용" } ], "ease": "power2.inOut", "snippet": "tl.to('.grp-a', { x: 240, duration: 0.7, ease: 'power2.inOut' }, 0.3)\n .to('.grp-b', { x: -180, duration: 0.7, ease: 'power2.inOut' }, 0.3 + 0.16);", "pairsWith": [ "stagger", "temporal-overlap", "object-constant-update" ], "engines": { "hyperframes": "그룹 클래스를 셀렉터로 한 번에 tween해 같은 시작·같은 이징을 보장한다. stagger 옵션은 넣지 않는다", "reelforge": "브리프에 그룹 이름별 이동량·방향·그룹 간 지연을 표로 싣고, 그룹 색은 고정한다", "scrolline": "scrub에서는 그룹 간 지연을 진행률 0.08 정도의 오프셋으로 바꾼다. 그룹 안은 동일 진행률" }, "prompts": { "claude": "GSAP으로 <대상> 점 그룹을 묶어서 움직여줘. .grp-a 점 전체를 0.3초에 x +240px, 0.7초, power2.inOut으로 동시에 옮기고 .grp-b는 0.16초 뒤에 x -180px로 같은 방식으로 옮겨. 그룹 안에서는 stagger 없이 시작 시각을 똑같이 해. paused 타임라인 하나로 만들어.", "codex": "<파일>의 <대상>에 group-motion을 적용해. 셀렉터 .grp-a는 position 0.3, x 240, 0.7s, power2.inOut, .grp-b는 position 0.46, x -180. stagger는 쓰지 않는다. 0.5초와 1.0초를 캡처해 같은 그룹 점들의 x 변화량이 모두 동일한지 확인해." }, "sources": [ { "title": "Heer & Robertson 2007", "url": "https://sites.stat.columbia.edu/gelman/communication/HeerRobertson2007.pdf", "license": "unknown" }, { "title": "motion dictionary 1-principles.md#6. 모션 위계·코레오그래피", "url": "", "license": "own" }, { "title": "d3/d3-transition", "url": "https://d3js.org/d3-transition", "license": "ISC" } ], "runtime": "svg", "variants": [ "평행 이동", "동기 크기 변화", "동기 색 변화" ], "oneLinerEn": "Marks in one group move together in the same direction and speed so they read as a unit.", "conveysEn": "Items that are far apart still read as one set when they move together (common fate).", "useWhenEn": [ "When showing which points belong to the same cluster in a scatter plot", "When moving several cards as one category", "When shifting dots by team or segment" ], "promptsEn": { "claude": "Move the dot groups as units with GSAP. Move all .grp-a dots x +240px at 0.3s over 0.7s with power2.inOut at the same moment, then .grp-b x -180px 0.16s later the same way. No stagger inside a group. One paused timeline.", "codex": "Apply group-motion to in . Selector .grp-a at position 0.3, x 240, 0.7s, power2.inOut; .grp-b at position 0.46, x -180. No stagger. Capture at 0.5s and 1.0s and check every dot in the same group has an identical x delta." }, "render": "planned", "files": {}, "no": "019" }, { "slug": "inertial-glide", "family": "principles", "ko": "관성 이동", "en": "Inertial Glide", "aka": [ "Inertial glide", "관성 미끄러짐", "Numeric property physics", "속성 물리 변화" ], "oneLiner": "놓인 물체가 이동 방향을 유지하다가 서서히 감속하며 멈춘다", "conveys": "속도와 마찰을 느끼게 한다. 밀어서 놓은 것 같은 자연스러운 정지가 된다", "purposes": [ "피드백", "분위기" ], "media": [ "웹 UI", "제품 시연", "숏폼" ], "level": "중급", "useWhen": [ "카드를 던지듯 밀어 놓았을 때 미끄러지다 멈추게 할 때", "스와이프 뒤 목록이 관성으로 흘러갈 때" ], "goodExample": "초기 속도 600px/s에서 시정수 700ms로 지수 감쇠하며 최대 2000ms 동안 미끄러지다 멈춘다", "badExample": "감쇠가 없어 등속으로 가다 갑자기 멈추거나, 초기 속도가 3000px/s라 화면 밖으로 나간다", "avoid": [ "정지 조건은 속도 5px/s 미만", "이동 거리는 화면 안에 들어오도록 초기 속도를 정한다" ], "params": [ { "name": "초기 속도", "default": "600px/s", "range": "300~1000px/s", "note": "방향 포함" }, { "name": "감쇠 시정수", "default": "700ms", "range": "400~1000ms", "note": "v = v0 * exp(-t/tau)" }, { "name": "최대 재생", "default": "2000ms", "range": "1500~2500ms", "note": "" }, { "name": "총 이동", "default": "약 420px", "range": "v0*tau", "note": "600*0.7" } ], "ease": "expo.out", "snippet": "const v0 = 600, tau = 0.7, dist = v0 * tau; // 420px\ntl.to('.card', { x: dist, duration: 2, ease: 'expo.out' }, 0.3);\n// 정확한 지수 감쇠: x(t) = v0*tau*(1 - exp(-t/tau)), expo.out은 근사", "pairsWith": [ "retarget-motion", "follow-through", "easing-curves" ], "engines": { "hyperframes": "expo.out으로 지수 감쇠를 근사하거나 onUpdate에서 x(t)를 공식으로 계산한다. 공식이면 seek 결정론이 보장된다", "reelforge": "브리프에 초기 속도, 시정수, 최대 재생 시간을 싣는다. 이동 거리는 v0*tau로 미리 계산한다", "scrolline": "진행률을 시간으로 보고 x(t)를 공식으로 계산한다. 스크롤을 멈추면 관성으로 더 가지 않고 진행률에 고정된다" }, "prompts": { "claude": "<대상> 카드에 관성 이동을 넣어줘. 초기 속도 600px/s, 시정수 0.7초로 지수 감쇠하게 하고 총 2초 동안 x 420px 이동해 멈추게 해. GSAP에서 ease expo.out으로 근사하고 시작은 0.3초.", "codex": "<파일>에 inertial glide를 구현해. x(t) = 600*0.7*(1 - exp(-t/0.7)), 2초, onUpdate로 계산. 0.5초, 1.0초, 2.0초에 캡처해 위치 증가량이 점점 줄어드는지, 2초에 약 402px 이상이면서 420px를 넘지 않는지 확인해." }, "sources": [ { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Plugins/InertiaPlugin/", "license": "GSAP Standard License" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Plugins/PhysicsPropsPlugin/", "license": "GSAP Standard License" }, { "title": "motiondivision/motion", "url": "https://motion.dev/docs/react-transitions", "license": "MIT" }, { "title": "motiondivision/motionone", "url": "https://github.com/motiondivision/motionone/blob/main/README.md", "license": "MIT" }, { "title": "Popmotion/popmotion", "url": "https://github.com/Popmotion/popmotion", "license": "MIT" } ], "runtime": "gsap", "variants": [ "위치 관성", "회전 관성", "크기 관성" ], "oneLinerEn": "A released object keeps its direction of travel and gradually decelerates to a stop.", "conveysEn": "Conveys speed and friction, like something pushed and let go, with a natural stop.", "useWhenEn": [ "Let a card slide and stop after being pushed.", "Let a list coast after a swipe." ], "promptsEn": { "claude": "Add inertial glide to the card in . Use an initial velocity of 600px/s and a time constant of 0.7 seconds for exponential decay, traveling about 420px over 2 seconds and stopping. Approximate with ease expo.out in GSAP and start at 0.3 seconds.", "codex": "Implement inertial glide in : x(t) = 600*0.7*(1 - exp(-t/0.7)), 2 seconds, computed in onUpdate. Capture at 0.5s, 1.0s and 2.0s and verify the position increments shrink and the value at 2s is about 402px and never exceeds 420px." }, "render": "planned", "files": {}, "no": "020" }, { "slug": "retarget-motion", "family": "principles", "ko": "진행 중 목표 변경", "en": "Motion Retargeting", "aka": [ "Adaptive reverse easing", "중간 반전의 가감속", "Interrupt-and-retarget transition", "진행 중 목표 재설정", "Interruptibility / Reversibility", "중단·반전 가능성" ], "oneLiner": "이동 도중 목표가 바뀌어도 현재 위치에서 새 방향으로 자연스럽게 가감속한다", "conveys": "취소와 복귀, 데이터 갱신이 갑작스럽지 않게 보인다. 사용자의 의도 변경에 화면이 부드럽게 반응한다", "purposes": [ "피드백", "순서·흐름" ], "media": [ "웹 UI", "제품 시연", "데이터 스토리" ], "level": "고급", "useWhen": [ "이동 중인 요소가 중간에 목적지가 바뀌는 인터랙션을 시연할 때", "실시간 데이터가 갱신될 때 막대가 되돌아가지 않고 새 값으로 이어 갈 때" ], "goodExample": "800ms 이동의 40% 지점(320ms)에서 목표가 바뀌고, 현재 위치에서 400ms 동안 새 목표로 이어진다. 위치 점프가 없다", "badExample": "목표가 바뀌면 시작점으로 순간 이동한 뒤 새 tween을 시작해 점프한다. 속도가 0으로 떨어졌다 다시 출발한다", "avoid": [ "현재 위치를 새 시작값으로 쓴다(시작점 복귀 금지)", "현재 속도를 이어 받을 수 있으면 이어 받는다" ], "params": [ { "name": "첫 이동", "default": "800ms", "range": "600~1000ms", "note": "원래 목표 x 700" }, { "name": "변경 시점", "default": "40%", "range": "30~60%", "note": "320ms" }, { "name": "재이동", "default": "400ms", "range": "300~600ms", "note": "새 목표 x 200" }, { "name": "이징", "default": "power2.out", "range": "", "note": "재이동은 감속만" } ], "ease": "power2.out", "snippet": "tl.to('.dot', { x: 700, duration: 0.8, ease: 'power2.out' }, 0.2);\ntl.call(() => {}, null, 0.52);\ntl.to('.dot', { x: 200, duration: 0.4, ease: 'power2.out', overwrite: 'auto' }, 0.52); // 현재 위치에서 재시작", "pairsWith": [ "easing-curves", "inertial-glide", "state-tween" ], "engines": { "hyperframes": "overwrite auto가 paused 타임라인에서 seek 뒤에도 같은 결과를 내는지 미리 검증한다. 필요하면 변경 시점의 위치를 값으로 계산해 fromTo로 쓴다", "reelforge": "브리프에 변경 시점과 새 목표를 파라미터로 싣는다. 씬은 변경 이벤트를 데이터 이벤트와 동기시킨다", "scrolline": "스크롤 방향이 바뀌는 순간이 곧 목표 변경이다. progress 자체를 새 목표로 보간하면 자연스럽게 이어진다" }, "prompts": { "claude": "<대상> 점이 x 700으로 0.8초 동안 power2.out으로 이동하다가 0.52초에 목표가 x 200으로 바뀌는 장면을 만들어줘. 현재 위치에서 0.4초, power2.out으로 새 목표로 이어 가게 하고 시작점으로 튀지 않게 해.", "codex": "<파일>에 retarget motion을 구현해. 첫 tween x 700 / 0.8s / power2.out, 0.52초에 새 tween x 200 / 0.4s / power2.out, 현재 위치에서 시작. 0.5초, 0.52초, 0.53초를 캡처해 0.52초 전후 x 값이 연속인지(점프 없음), 그 뒤 x가 200으로 수렴하는지 확인해." }, "sources": [ { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/GSAP/Tween/", "license": "GSAP Standard License" }, { "title": "d3/d3-transition", "url": "https://d3js.org/d3-transition", "license": "ISC" }, { "title": "motion dictionary 1-principles.md#6. 모션 위계·코레오그래피", "url": "", "license": "own" }, { "title": "bost.ocks.org", "url": "https://bost.ocks.org/mike/transition/", "license": "unknown" } ], "runtime": "gsap", "variants": [ "중간 반전", "새 목표 추종", "데이터 갱신" ], "oneLinerEn": "When the target changes mid-move, the element eases from its current position toward the new direction.", "conveysEn": "Cancellations, reversals and data updates look smooth. The screen responds gracefully to a change of intent.", "useWhenEn": [ "Demonstrate an interaction where the destination changes mid-move.", "Let live data updates continue from the current value instead of resetting." ], "promptsEn": { "claude": "In , make a dot move to x 700 over 0.8 seconds with power2.out, then at 0.52 seconds retarget it to x 200. Continue from its current position over 0.4 seconds with power2.out and no jump back to the start.", "codex": "Implement retarget motion in : first tween x 700 / 0.8s / power2.out, then at 0.52s a new tween x 200 / 0.4s / power2.out starting from the current position. Capture at 0.5s, 0.52s and 0.53s and verify x is continuous across 0.52s with no jump and converges to 200 afterward." }, "render": "planned", "files": {}, "no": "021" }, { "slug": "rotate", "family": "principles", "ko": "회전", "en": "Rotation", "aka": [], "oneLiner": "중심점 둘레로 방향을 바꾸는 기본 회전", "conveys": "방향과 자세가 바뀌었다는 것을 명확히 알린다", "purposes": [ "강조", "순서·흐름" ], "media": [ "웹 UI", "설명 영상", "발표" ], "level": "기본", "useWhen": [ "화살표·펼침 아이콘이 상태에 따라 돌 때", "카드나 다이어그램 노드를 90도 돌려 축을 바꿀 때" ], "goodExample": "화살표 아이콘이 300ms 동안 90도 돌아 아래를 가리킨다", "badExample": "회전 중심이 요소 밖이라 궤도를 그리며 흔들리거나, 텍스트를 여러 바퀴 돌린다", "avoid": [ "회전 중심을 명시", "텍스트는 회전 없이 아이콘만", "각도는 90 또는 180 배수" ], "params": [ { "name": "각도", "default": "90deg", "range": "45~180deg", "note": "한 방향 고정" }, { "name": "길이", "default": "300ms", "range": "200~500ms", "note": "회전 시간" }, { "name": "원점", "default": "50% 50%", "range": "요소별", "note": "아이콘 중앙" }, { "name": "이징", "default": "power2.out", "range": "power2~power3.out", "note": "도착에서 감속" } ], "ease": "power2.out", "snippet": "tl.to('.chevron', { rotation: 90, transformOrigin: '50% 50%', duration: 0.3, ease: 'power2.out' }, 0.4);", "pairsWith": [ "rotate-scale", "toggle-slide", "accordion-expand" ], "engines": { "hyperframes": "rotation은 transform이므로 paused seek에 안전하다. 각도 값은 누적하지 말고 절대값으로 지정", "reelforge": "브리프에 각도·원점·길이를 싣는다. 여러 개를 돌릴 때는 stagger 0.05초", "scrolline": "scrub에서는 각도를 진행률에 선형 매핑한다. 도착에서 ease-out으로 정지" }, "prompts": { "claude": "GSAP으로 <대상> 화살표 아이콘을 회전시켜줘. 0.4초부터 0.3초 동안 rotation 0에서 90도, transformOrigin 50% 50%, power2.out. 각도는 절대값으로 지정하고 텍스트는 돌리지 않아. paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 rotate를 적용해. to({rotation 90, transformOrigin '50% 50%'}, 0.3s, power2.out, position 0.4). 0.4초·0.55초·0.9초를 캡처해 0도, 중간각, 90도를 확인하고 중심이 이동하지 않았는지 좌표를 비교해." }, "sources": [ { "title": "motion dictionary 1-principles.md#8. 2D 속성 기본 동작", "url": "", "license": "own" } ], "runtime": "gsap", "variants": [], "oneLinerEn": "Turns an element around a center point to change its direction.", "conveysEn": "Clearly tells that direction or posture has changed.", "useWhenEn": [ "When an arrow or expand icon turns with state", "When rotating a card or diagram node 90 degrees to switch axis" ], "promptsEn": { "claude": "Rotate the arrow icon with GSAP. From 0.4s over 0.3s rotate 0 to 90 degrees around 50% 50% with power2.out. Use absolute angles and do not rotate text. Paused timeline.", "codex": "Apply rotate to in . to({rotation 90, transformOrigin '50% 50%'}, 0.3s, power2.out, position 0.4). Capture at 0.4s, 0.55s and 0.9s to check 0 degrees, a mid angle and 90 degrees, and compare coordinates to confirm the center did not move." }, "render": "planned", "files": {}, "no": "022" }, { "slug": "scale", "family": "principles", "ko": "스케일", "en": "Scale", "aka": [ "Scale / Zoom", "크기 변화", "ScaleX / ScaleY", "축별 크기" ], "oneLiner": "기준점에서 크기를 비례로 바꿔 펼쳐지는 방향과 크기 변화를 보여 주는 기본 동작", "conveys": "크기가 커지거나 작아지는 변화와 어디서 펼쳐지는지를 함께 알린다", "purposes": [ "강조", "전환" ], "media": [ "웹 UI", "설명 영상", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "카드·모달이 살짝 커지며 나타날 때", "버튼을 눌렀을 때 미세하게 줄었다 돌아올 때", "가로나 세로 한 축으로만 늘어나는 막대·바를 그릴 때" ], "goodExample": "모달이 scale 0.96에서 1로 300ms 동안 커지며 중앙에서 펼쳐진다", "badExample": "scale 0에서 시작해 글자가 깨져 보이거나 원점이 요소 밖이라 엉뚱한 방향으로 커진다", "avoid": [ "scale 0.9 미만 시작은 글자 요소에 금지", "transform-origin을 의도에 맞게 지정", "opacity와 같이 써서 튐 방지" ], "params": [ { "name": "시작 배율", "default": "0.96", "range": "0.9~0.98", "note": "1에 가까울수록 고급스럽다" }, { "name": "길이", "default": "300ms", "range": "200~450ms", "note": "이동 시간" }, { "name": "원점", "default": "50% 50%", "range": "방향에 따라 지정", "note": "막대는 50% 100%" }, { "name": "이징", "default": "power3.out", "range": "power2~power4.out", "note": "도착이 부드럽게" } ], "ease": "power3.out", "snippet": "tl.from('.modal', { scale: 0.96, opacity: 0, transformOrigin: '50% 50%', duration: 0.3, ease: 'power3.out' }, 0.3);", "pairsWith": [ "fade", "slide", "scale-pop" ], "engines": { "hyperframes": "transformOrigin을 tween에 명시해 seek마다 같은 원점이 되게 한다. 글자 흐림을 막으려면 종료 scale을 정확히 1로 둔다", "reelforge": "브리프에 시작 배율·원점·길이를 싣고 가로 또는 세로 확장은 scaleX/scaleY로 구분한다", "scrolline": "scrub에서는 0.96→1을 진행률 0~0.5에 매핑하고 opacity를 함께 올린다. 스프링 없이 ease-out" }, "prompts": { "claude": "GSAP으로 <대상>에 스케일 등장을 넣어줘. 0.3초에 scale 0.96, opacity 0에서 시작해 0.3초 동안 scale 1, opacity 1로 power3.out으로 커져. transformOrigin은 50% 50%, 막대라면 50% 100%. 종료 scale은 정확히 1로 두고 paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 scale을 적용해. from({scale 0.96, opacity 0, transformOrigin '50% 50%'}, 0.3s, power3.out, position 0.3). 0.3초·0.45초·0.8초를 캡처해 시작 배율 0.96, 중간, 종료 1.0과 글자 선명도를 확인해." }, "sources": [ { "title": "motion dictionary 1-principles.md#8. 2D 속성 기본 동작", "url": "", "license": "own" } ], "runtime": "gsap", "variants": [ "균일 확대", "가로 확장", "세로 확장" ], "oneLinerEn": "Changes size proportionally from an anchor so growth direction and amount read clearly.", "conveysEn": "Size change and where it unfolds from are told together.", "useWhenEn": [ "When a card or modal grows slightly as it appears", "When a button dips and returns on press", "When drawing bars that grow along one axis" ], "promptsEn": { "claude": "Add a scale entrance to with GSAP. At 0.3s start at scale 0.96, opacity 0 and grow to scale 1, opacity 1 over 0.3s with power3.out. transformOrigin 50% 50%, or 50% 100% for bars. End scale exactly 1 and use a paused timeline.", "codex": "Apply scale to in . from({scale 0.96, opacity 0, transformOrigin '50% 50%'}, 0.3s, power3.out, position 0.3). Capture at 0.3s, 0.45s and 0.8s to check start scale 0.96, the midpoint, end scale 1.0 and text sharpness." }, "render": "planned", "files": {}, "no": "023" }, { "slug": "state-tween", "family": "principles", "ko": "상태 보간", "en": "State Tween", "aka": [ "State-to-state property tween", "목표 상태 속성 보간", "MoveToTarget", "State transition", "상태 전이", "Compound transform", "복합 변환" ], "oneLiner": "위치·크기·회전·색이 저장된 다음 상태로 동시에 바뀌는 보간", "conveys": "한 대상의 상태가 연속적으로 바뀌었다는 것을 읽게 한다", "purposes": [ "설명", "순서·흐름" ], "media": [ "설명 영상", "웹 UI", "데이터 스토리" ], "level": "기본", "useWhen": [ "카드가 접힘 상태에서 펼침 상태로 바뀔 때", "아이콘이 대기에서 활성으로 바뀔 때", "다이어그램 노드가 위치와 색을 함께 바꿀 때" ], "goodExample": "노드가 x 0→420px, scale 1→1.3, rotation 0→15도, 색 파랑→노랑으로 0.8초 동안 함께 바뀐다", "badExample": "속성마다 길이와 이징이 달라 각자 따로 움직이는 것처럼 보이거나, 목표 상태가 불명확하다", "avoid": [ "속성마다 같은 길이·이징을 쓴다(의도적 차이 제외)", "시작과 목표 상태를 코드에 명시", "색은 알파와 함께 보간" ], "params": [ { "name": "길이", "default": "0.8s", "range": "0.5~1.2s", "note": "모든 속성 공통" }, { "name": "이징", "default": "power2.inOut", "range": "power1~power3.inOut", "note": "smooth" }, { "name": "목표 상태", "default": "x, scale, rotation, color", "range": "속성 2~4개", "note": "한 번에 너무 많이 바꾸지 않는다" }, { "name": "홀드", "default": "0.6s", "range": "0.4~1.0s", "note": "도착 뒤 정지" } ], "ease": "power2.inOut", "snippet": "tl.fromTo('.node', { x: 0, scale: 1, rotation: 0, backgroundColor: '#2f6bff' }, { x: 420, scale: 1.3, rotation: 15, backgroundColor: '#ffc83d', duration: 0.8, ease: 'power2.inOut' }, 0.3);", "pairsWith": [ "motion-blend", "color-transition", "concurrent-motion" ], "engines": { "hyperframes": "fromTo로 시작·목표 상태를 함께 적으면 seek 시 초기화가 확실하다. 색은 hex로 통일한다", "reelforge": "브리프에 상태 A/B를 속성 표로 싣고 길이는 한 값으로 공유한다", "scrolline": "scrub에서는 진행률 p가 A에서 B로의 보간 비율이다. 각 속성에 같은 ease-out을 적용" }, "prompts": { "claude": "GSAP으로 <대상>의 상태 보간을 넣어줘. 0.3초부터 0.8초 동안 x 0→420px, scale 1→1.3, rotation 0→15도, backgroundColor #2f6bff→#ffc83d를 power2.inOut으로 동시에 바꾸고, 시작 상태를 fromTo에 명시해. 도착 뒤 0.6초 정지. paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 state-tween을 적용해. fromTo 시작 {x 0,scale 1,rotation 0,bg #2f6bff}, 목표 {x 420,scale 1.3,rotation 15,bg #ffc83d}, 0.8s, power2.inOut, position 0.3. 0.3초·0.7초·1.1초를 캡처해 모든 속성이 같은 비율로 진행하는지 확인해." }, "sources": [ { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/transform.py", "license": "MIT" }, { "title": "motion dictionary 1-principles.md#6. 모션 위계·코레오그래피", "url": "", "license": "own" } ], "runtime": "gsap", "variants": [ "복합 속성 변화", "일부 속성 변화" ], "oneLinerEn": "Position, size, rotation and color change together toward a saved next state.", "conveysEn": "Shows one object's state changing continuously.", "useWhenEn": [ "When a card goes from collapsed to expanded", "When an icon goes from idle to active", "When a diagram node changes position and color together" ], "promptsEn": { "claude": "Add a state tween to with GSAP. From 0.3s over 0.8s change x 0 to 420px, scale 1 to 1.3, rotation 0 to 15 degrees and backgroundColor #2f6bff to #ffc83d together with power2.inOut, with the start state written in fromTo. Hold 0.6s after arrival. Paused timeline.", "codex": "Apply state-tween to in . fromTo start {x 0, scale 1, rotation 0, bg #2f6bff} to {x 420, scale 1.3, rotation 15, bg #ffc83d}, 0.8s, power2.inOut, position 0.3. Capture at 0.3s, 0.7s and 1.1s to check all properties progress at the same ratio." }, "render": "planned", "files": {}, "no": "024" }, { "slug": "stepped-motion", "family": "principles", "ko": "계단식 모션", "en": "Stepped Motion", "aka": [ "계단식 상태 변화", "hold-keyframe-stepped-values", "Stepped timing", "계단식 타이밍", "SteppedEase", "Steps", "Stop motion", "Snapped movement", "양자화 이동", "Hold Keyframe Cut", "홀드 키프레임 컷", "Stepped keyframe", "계단", "Intentional discontinuity", "의도한 불연속" ], "oneLiner": "값이나 자세가 중간 보간 없이 한 단계씩 순간적으로 바뀐다", "conveys": "디지털 단계와 저속 프레임의 질감을 전한다. 부드러운 애니메이션과 대비되어 기계적이고 또렷한 인상이 생긴다", "purposes": [ "분위기", "브랜딩" ], "media": [ "숏폼", "웹 UI", "설명 영상" ], "level": "기본", "useWhen": [ "스톱모션이나 8비트 느낌의 브랜드 톤을 낼 때", "단계별로 값이 바뀌는 카운터와 진행 표시를 만들 때" ], "goodExample": "8단계, 총 800ms, 단계 간격 100ms로 요소가 20px 격자 위를 한 칸씩 이동한다", "badExample": "단계 수를 60으로 늘려 부드러운 이동과 구분이 안 되거나, 단계 간격이 불규칙해 실수처럼 보인다", "avoid": [ "단계 간격은 일정하게 유지한다", "읽어야 하는 텍스트 이동에는 쓰지 않는다" ], "params": [ { "name": "단계 수", "default": "8", "range": "4~12", "note": "적을수록 거칠다" }, { "name": "총 시간", "default": "800ms", "range": "600~1200ms", "note": "100ms 간격" }, { "name": "격자", "default": "20px", "range": "10~40px", "note": "위치 스냅 단위" }, { "name": "이징", "default": "steps(8)", "range": "steps", "note": "GSAP 코어 ease" } ], "ease": "steps(8)", "snippet": "tl.to('.item', { x: 160, duration: 0.8, ease: 'steps(8)' }, 0.3); // 20px x 8 = 160px, 100ms마다 순간 이동\n// 각도 스냅: rotation: 90, ease: 'steps(6)'", "pairsWith": [ "stagger", "color-cycle", "palette-cycle" ], "engines": { "hyperframes": "ease를 steps(n)으로 지정하면 seek에도 같은 단계 값이 나온다. onUpdate에서 좌표를 반올림하지 않아도 된다", "reelforge": "브리프에 단계 수, 총 시간, 격자 크기를 싣는다. 같은 씬에 부드러운 요소를 섞으면 대비가 더 산다", "scrolline": "진행률을 단계 수로 양자화하는 것과 같다. 스크롤에서 경계가 딱딱하므로 단계 수를 6 이상으로 한다" }, "prompts": { "claude": "<대상> 요소를 부드러운 이동 대신 8단계 스냅으로 옮겨줘. x 0에서 160px까지 0.8초, GSAP ease는 steps(8)로 해서 100ms마다 20px씩 순간 이동하게 해. 시작은 0.3초야.", "codex": "<파일>의 이동을 stepped motion으로 바꿔. x 160, duration 0.8s, ease steps(8), 시작 0.3s. 0.35초, 0.45초, 0.55초 시점을 캡처해 x가 20px 단위로만 존재하는지, 중간값이 나오지 않는지 확인해." }, "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/02-easing-graph.md#hold-keyframe-stepped-values", "license": "Apache-2.0" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Eases/SteppedEase/", "license": "GSAP Standard License" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/GSAP/CorePlugins/Snap/", "license": "GSAP Standard License" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/animate-in-after-effects/animation-keyframes/keyframe-interpolation.html", "license": "unknown" }, { "title": "motion dictionary 1-principles.md#4.1 곡선의 읽는 법", "url": "", "license": "own" } ], "runtime": "gsap", "variants": [ "위치 스냅", "각도 스냅", "홀드 키프레임", "단계 상태 전환" ], "oneLinerEn": "Values or poses change instantly one step at a time without in-between interpolation.", "conveysEn": "Carries the texture of digital steps and low frame rates. Against smooth animation it reads mechanical and crisp.", "useWhenEn": [ "Set a stop-motion or 8-bit brand tone.", "Build counters or progress indicators that change in discrete steps." ], "promptsEn": { "claude": "Move the element in in 8 snapped steps instead of a smooth glide. Go from x 0 to 160px over 0.8 seconds with GSAP ease steps(8), so it jumps 20px every 100ms. Start at 0.3 seconds.", "codex": "Change the movement in to stepped motion: x 160, duration 0.8s, ease steps(8), start 0.3s. Capture at 0.35s, 0.45s and 0.55s and verify x only takes multiples of 20px and no intermediate values appear." }, "render": "planned", "files": {}, "no": "025" }, { "slug": "temporal-overlap", "family": "principles", "ko": "동작 중첩", "en": "Temporal Overlap", "aka": [ "Overlapping choreography", "겹치는 동작 순서", "Temporal overlap", "시간 겹침" ], "oneLiner": "첫 요소가 멈추기 전에 다음 요소의 움직임이 시작되어 동작이 물결처럼 이어지는 순서", "conveys": "서로 연결된 행동과 유려한 흐름. 딱딱한 순차 재생보다 자연스럽다", "purposes": [ "순서·흐름", "분위기" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "카드·아이콘 여러 개가 차례로 들어오되 끊기지 않게 하고 싶을 때", "한 요소의 도착이 다음 요소의 출발을 이끄는 연쇄를 표현할 때" ], "goodExample": "카드 3장이 각각 0.6초 동안 들어오되 다음 카드가 앞 카드 종료 0.15초 전에 출발한다", "badExample": "겹침이 커서 전부 동시에 움직이는 것처럼 보이거나, 겹침이 없어 순차 재생과 차이가 없다", "avoid": [ "겹침은 개별 길이의 15~35%", "3개 이상 연쇄일 때는 겹침을 일정하게", "읽어야 할 텍스트가 든 카드는 겹침을 줄인다" ], "params": [ { "name": "개별 길이", "default": "0.6s", "range": "0.4~0.9s", "note": "한 요소의 tween" }, { "name": "겹침", "default": "0.15s", "range": "0.1~0.25s", "note": "앞 요소 종료 전 시작 시간" }, { "name": "이동 거리", "default": "40px", "range": "24~80px", "note": "1920x1080 기준" }, { "name": "이징", "default": "power3.out", "range": "power2~power4.out", "note": "도착이 부드럽게" } ], "ease": "power3.out", "snippet": "['.c1', '.c2', '.c3'].forEach((s, i) => tl.from(s, { opacity: 0, y: 40, duration: 0.6, ease: 'power3.out' }, 0.3 + i * (0.6 - 0.15)));", "pairsWith": [ "overlapping-action", "action-sequence", "stagger" ], "engines": { "hyperframes": "position 인자를 i*(길이-겹침)으로 계산해 한 paused 타임라인에 배치한다. 겹침 값 하나로 리듬을 바꿀 수 있다", "reelforge": "브리프에 개별 길이·겹침·개수를 싣는다. 겹침을 0으로 두면 순차 재생과 같다", "scrolline": "scrub에서는 각 요소 진행률 구간을 서로 겹치게 정한다(예: 0~0.5, 0.35~0.85). ease-out 사용" }, "prompts": { "claude": "GSAP으로 <대상> 카드 .c1, .c2, .c3가 겹치는 순서로 들어오게 해줘. 각 카드는 opacity 0, y +40px에서 0.6초 power3.out으로 나타나고, 다음 카드는 앞 카드 종료 0.15초 전에 시작해. 시작은 0.3초. paused 타임라인 하나로 position 값을 계산해 넣어.", "codex": "<파일>의 <대상>에 temporal-overlap을 적용해. forEach에서 from({opacity 0, y 40}, 0.6s, power3.out)을 position 0.3+i*0.45에 건다. 0.5초·0.8초·1.4초를 캡처해 카드 i가 끝나기 전에 i+1이 움직이기 시작했는지 확인해." }, "sources": [ { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "GSAP Standard License" }, { "title": "motion dictionary 1-principles.md#5. 타이밍·간격·리듬 기본기", "url": "", "license": "own" }, { "title": "motiondivision/motion", "url": "https://motion.dev/docs/animate", "license": "MIT" }, { "title": "pmndrs/react-spring", "url": "https://www.react-spring.dev/docs/components/use-chain", "license": "MIT" }, { "title": "juliangarnier/anime", "url": "https://animejs.com/documentation/timeline", "license": "MIT" } ], "runtime": "gsap", "variants": [ "부분 중첩", "연쇄 중첩" ], "oneLinerEn": "The next element starts moving before the first has stopped, so motion flows in a wave.", "conveysEn": "Connected actions and fluid flow. Smoother than strict sequencing.", "useWhenEn": [ "When cards or icons enter in turn without a break", "When one element's arrival should trigger the next in a chain" ], "promptsEn": { "claude": "Have cards .c1, .c2 and .c3 of enter with overlap in GSAP. Each animates from opacity 0, y +40px over 0.6s with power3.out, and the next starts 0.15s before the previous ends. Start at 0.3s. Compute positions on one paused timeline.", "codex": "Apply temporal-overlap to in . In forEach add from({opacity 0, y 40}, 0.6s, power3.out) at position 0.3 + i*0.45. Capture at 0.5s, 0.8s and 1.4s to check that item i+1 starts moving before item i has finished." }, "render": "planned", "files": {}, "no": "026" }, { "slug": "symmetric-panel-open", "family": "entrance", "ko": "대칭 패널 펼침", "en": "Symmetric Panel Open", "aka": [ "Split Compare", "대칭 비교 등장", "split-tilt-cards" ], "oneLiner": "두 판이 양쪽에서 들어와 책을 펴듯 서로 반대 각도로 정면을 향한다.", "conveys": "두 항목을 같은 비중으로 비교한다.", "purposes": [ "비교", "전환" ], "media": [ "설명 영상", "스크롤덱", "웹 UI" ], "level": "중급", "useWhen": [ "같은 크기의 카드로 두 제품을 비교할 때", "전후 화면을 같은 시각에 공개할 때" ], "goodExample": "두 요금제 카드가 좌우 240px에서 들어와 0.6초에 정면을 향하고 배지는 0.3초 뒤 나타난다.", "badExample": "한쪽 카드만 크게 확대해 비교의 비중이 달라진다.", "avoid": [ "원근으로 카드 본문의 가독성을 낮추지 않는다." ], "params": [ { "name": "진입 시간", "default": "600ms", "range": "400~900ms", "note": "두 판의 도착 시각을 맞춘다." }, { "name": "진입 거리", "default": "240px", "range": "120~360px", "note": "좌우에 반대 부호를 적용한다." }, { "name": "Y 회전", "default": "15deg", "range": "8~20deg", "note": "정면 0도로 닫는다." }, { "name": "배지 지연", "default": "300ms", "range": "200~450ms", "note": "판 도착 뒤의 지연이다." }, { "name": "이징", "default": "power2.out", "range": "none | power2.out | power2.inOut", "note": "끝점을 넘지 않는 이징을 쓴다." } ], "ease": "power2.out", "snippet": "const tl = gsap.timeline({paused:true});\n['.left','.right'].forEach((s,i)=>{\n tl.fromTo(s,{x:i?240:-240,rotationY:i?-15:15},{x:0,rotationY:0,duration:0.6,ease:'power2.out'},0);\n});\ntl.fromTo('.badge',{scale:0,opacity:0},{scale:1,opacity:1,duration:0.2},0.9);", "pairsWith": [ "split-compare", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인 하나에 대칭 패널 펼침 상태를 넣고 seek(t)로 0.6초 구간을 재현한다. 현재 시간에서 도형을 계산해 프레임 누적을 피한다.", "reelforge": "씬 워커 브리프에 진입 시간 600ms, 진입 거리 240px, Y 회전 15deg, 배지 지연 300ms, 이징 power2.out를 싣고 입력 자료와 초기 도형 좌표를 고정한다. 두 요금제 카드가 좌우 240px에서 들어와 0.6초에 정면을 향하고 배지는 0.3초 뒤 나타난다.", "scrolline": "진행률 0~1을 0.6초 구간에 매핑해 같은 타임라인을 scrub한다. 시간량은 none, 시각 전환은 power2.out을 쓰고 스프링 대신 끝점을 넘지 않는 ease-out을 쓴다." }, "prompts": { "claude": "<파일>의 <대상>에 대칭 패널 펼침를 구현해. 두 판이 양쪽에서 들어와 책을 펴듯 서로 반대 각도로 정면을 향한다. 진입 시간 600ms, 진입 거리 240px, Y 회전 15deg, 배지 지연 300ms, 이징 power2.out를 적용하고 GSAP 코어의 paused 타임라인 하나로 seek 가능하게 작성해. 원근으로 카드 본문의 가독성을 낮추지 않는다.", "codex": "<파일>의 <대상> 장면에 대칭 패널 펼침를 적용해. 진입 시간 600ms, 진입 거리 240px, Y 회전 15deg, 배지 지연 300ms, 이징 power2.out를 사용하고 다음 동작을 구현해: HTML 두 판의 x와 rotateY를 좌우 대칭으로 보간하고 안쪽 배지를 팝한다. 플러그인과 Math.random 없이 작성하고 0.15초·0.36초·0.6초를 캡처해 초기 상태, 중간 변화, 최종 상태가 연속적이며 다음 예시와 맞는지 확인해: 두 요금제 카드가 좌우 240px에서 들어와 0.6초에 정면을 향하고 배지는 0.3초 뒤 나타난다." }, "sources": [ { "title": "local/hyperframes-animation", "url": "claude-skill:hyperframes-animation/rules/split-tilt-cards.md", "license": "unknown" }, { "title": "local/hyperframes-animation", "url": "claude-skill:hyperframes-animation/blueprints/comparison-split.md", "license": "unknown" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:blocks/compare/block.html", "license": "Apache-2.0" } ], "runtime": "gsap", "variants": [ "A와 B", "상호 보완", "전후 비교", "comparison-split" ], "oneLinerEn": "Two panels enter from opposite sides and rotate toward a shared frontal plane.", "conveysEn": "Gives two subjects equal visual weight for comparison.", "useWhenEn": [ "Compare two products with equally sized cards.", "Reveal a before-and-after pair with synchronized timing." ], "promptsEn": { "claude": "Implement Symmetric Panel Open for in . Two panels enter from opposite sides and rotate toward a shared frontal plane. Use entrance duration: 600ms; entrance distance: 240px; Y rotation: 15deg; badge delay: 300ms; easing: power2.out in a single paused GSAP core timeline that supports seeking. Keep panel text readable through the perspective change.", "codex": "Apply Symmetric Panel Open to the scene in using entrance duration: 600ms; entrance distance: 240px; Y rotation: 15deg; badge delay: 300ms; easing: power2.out. Two panels enter from opposite sides and rotate toward a shared frontal plane. Use prepared data or geometry with GSAP core, no plugins, and no Math.random; capture at 0.15, 0.36, 0.6 seconds to verify continuous intermediate geometry, correct endpoints, and identical output after repeated seeks. Keep panel text readable through the perspective change." }, "render": "planned", "files": {}, "no": "027" }, { "slug": "back-entrance", "family": "entrance", "ko": "백 등장", "en": "Back Entrance", "aka": [ "Back Entrance and Exit", "뒤에서 밀려오는 등장" ], "variants": [ "backIn", "backOut", "spaceIn", "spaceOut", "상하좌우", "backInDown", "backInLeft", "backInRight", "backInUp", "backOutDown", "backOutLeft", "backOutRight", "backOutUp", "spaceInDown", "spaceInLeft", "spaceInRight", "spaceInUp", "spaceOutDown", "spaceOutLeft", "spaceOutRight", "spaceOutUp" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" } ], "runtime": "css", "oneLiner": "작고 흐린 요소가 멀리서 이동한 뒤 마지막에 원래 크기와 불투명도로 정착한다.", "conveys": "큰 이동 뒤의 정착을 강조한다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "주인공 카드가 먼 곳에서 도착할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "작은 제품 카드가 이동을 끝낸 뒤 원래 크기로 정착한다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "본문 전체를 먼 거리로 이동시키지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "1000ms", "range": "700~1400ms", "note": "한 번의 동작 기준" }, { "name": "이동 거리", "default": "1200px", "range": "600~1400px", "note": "1920x1080 기준" }, { "name": "시작 축척", "default": "0.7", "range": "0.6~0.85", "note": "이동 후 크기 복원" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 1000ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n 0%{transform:translateX(-1200px) scale(.7);opacity:.7} 70%{transform:translateX(0) scale(.7);opacity:.7} 100%{transform:translateX(0) scale(1);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 1초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 백 등장의 지속 1000ms, 이동 거리 1200px, 시작 축척 0.7, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 1초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 백 등장을 적용해. 지속 1000ms, 이동 거리 1200px, 시작 축척 0.7, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 백 등장 키프레임을 추가해. 지속 1000ms, 이동 거리 1200px, 시작 축척 0.7, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.5초·1초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "A small translucent element travels in, then settles at full size and opacity.", "conveysEn": "Emphasizes arrival after a long movement.", "useWhenEn": [ "Bring a hero card in from a distance.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Back Entrance to in . Implement the provided keyframes with 1000ms duration, travel distance 1200px, initial scale 0.7, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Back Entrance keyframes to the styles for in using 1000ms duration, travel distance 1200px, initial scale 0.7, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.5, and 1 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "028" }, { "slug": "blinds-reveal", "family": "entrance", "ko": "블라인드 리빌", "en": "Blinds Reveal", "aka": [ "Stripe Reveal", "줄무늬 셔터 리빌", "line-sweep-diagonal-stripes", "blinds-stripe-reveal" ], "variants": [ "세로 블라인드", "가로 블라인드", "사선 스트라이프", "object/blinds-stripe-triplet" ], "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/04-masks-mattes.md#line-sweep-diagonal-stripes", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/04-masks-mattes.md#blinds-stripe-reveal", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/gallery-index.json", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/fragments/object/blinds-stripe-triplet.html", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/fragments/object/blinds-stripe-triplet.meta.json", "license": "Apache-2.0" } ], "runtime": "gsap", "oneLiner": "여러 띠나 블라인드가 조금씩 다른 시점에 열려 전체 이미지를 완성한다.", "conveys": "분할된 조각이 합쳐지는 리듬을 느낀다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "포스터를 리듬 있게 공개할 때", "이미지를 띠 단위로 열 때" ], "goodExample": "여섯 띠가 0.05초씩 늦게 열려 0.65초에 사진을 완성한다.", "badExample": "띠마다 이미지 좌표가 달라 완성 사진이 끊긴다.", "avoid": [ "띠마다 이미지 좌표가 달라 완성 사진이 끊긴다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "0.4s", "range": "0.28~0.56s", "note": "0초부터 시작하는 공개 구간" }, { "name": "띠 수", "default": "6", "range": "4~10", "note": "동일 이미지 좌표 유지" }, { "name": "띠 간격", "default": "0.05s", "range": "0.03~0.08s", "note": "왼쪽부터 공개" }, { "name": "이징", "default": "power3.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power3.out", "snippet": "const strips = gsap.utils.toArray('.strip-window');\ngsap.set(strips, {clipPath:'inset(0 100% 0 0)'});\ntl.to(strips, {clipPath:'inset(0 0% 0 0)', duration:0.4, stagger:0.05, ease:'power3.out'}, 0);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 0.4초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 블라인드 리빌, 0.4초, 띠 수 6; 띠 간격 0.05s, power3.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 0.4초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 블라인드 리빌을 적용해. 0.4초, 띠 수 6; 띠 간격 0.05s, 이징 power3.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 블라인드 리빌을 적용해. 0.4초, 띠 수 6; 띠 간격 0.05s, power3.out를 사용하고 0초, 0.2초, 0.4초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "Staggered strips open to assemble a complete image.", "conveysEn": "Creates a rhythm of separate pieces becoming a whole.", "useWhenEn": [ "Use when presenting blinds reveal in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Blinds Reveal to in . Use a 0.4s segment with power3.out; implement these explicit settings: Strip count: 6, Strip delay: 0.05s. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Blinds Reveal to the layer in with Strip count: 6, Strip delay: 0.05s, using the supplied core snippet and a 0.4s segment with power3.out. Capture at 0s, 0.2s, and 0.4s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "done", "files": { "html": "effects/blinds-reveal/index.html", "mp4": "effects/blinds-reveal/clip.mp4", "gif": "effects/blinds-reveal/preview.gif", "poster": "effects/blinds-reveal/poster.jpg" }, "no": "029" }, { "slug": "blur-reveal", "family": "entrance", "ko": "블러 리빌", "en": "Blur Reveal", "aka": [ "Blur Resolve", "블러에서 선명", "blur-dissolve-in", "etch", "Blur to focus entrance", "흐림에서 선명한 등장" ], "variants": [ "글자별", "단어별", "이미지 전체", "확대 동반", "축소 동반", "흐려지는 퇴장" ], "sources": [ { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/GSAP/CorePlugins/CSS/", "license": "GSAP Standard License" }, { "title": "motiondivision/motion", "url": "https://motion.dev/docs/animate", "license": "MIT" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/05-text-animator.md#blur-dissolve-in", "license": "Apache-2.0" }, { "title": "local/embedded-captions", "url": "claude-skill:embedded-captions/references/motion-vocabulary.md", "license": "unknown" }, { "title": "local/music-to-video", "url": "claude-skill:music-to-video/references/motion-primitive-catalog.md#blur-resolve", "license": "unknown" } ], "runtime": "gsap", "oneLiner": "흐릿한 글자나 이미지가 선명해지면서 나타난다.", "conveys": "안개 속에서 내용이 확정되는 느낌을 준다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "기본", "useWhen": [ "짧은 제목이 초점을 찾게 할 때", "이미지를 부드럽게 공개할 때" ], "goodExample": "제목이 12px 흐림에서 0.6초 동안 선명해진다.", "badExample": "긴 본문에 블러를 오래 유지해 읽기를 지연한다.", "avoid": [ "긴 본문에 블러를 오래 유지해 읽기를 지연한다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "0.6s", "range": "0.42~0.84s", "note": "0초부터 시작하는 공개 구간" }, { "name": "시작 블러", "default": "12px", "range": "6~16px", "note": "작은 글자는 낮게" }, { "name": "시작 불투명도", "default": "0", "range": "0~0.2", "note": "완료 시 1" }, { "name": "이징", "default": "power2.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power2.out", "snippet": "gsap.set('.target', {filter:'blur(12px)', opacity:0});\ntl.to('.target', {filter:'blur(0px)', opacity:1, duration:0.6, ease:'power2.out'}, 0);\ntl.set('.target', {filter:'none'}, 0.6);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 0.6초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 블러 리빌, 0.6초, 시작 블러 12px; 시작 불투명도 0, power2.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 0.6초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 블러 리빌을 적용해. 0.6초, 시작 블러 12px; 시작 불투명도 0, 이징 power2.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 블러 리빌을 적용해. 0.6초, 시작 블러 12px; 시작 불투명도 0, power2.out를 사용하고 0초, 0.3초, 0.6초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "Blurred text or imagery resolves into sharp focus.", "conveysEn": "Makes content feel as if it is emerging from mist.", "useWhenEn": [ "Use when presenting blur reveal in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Blur Reveal to in . Use a 0.6s segment with power2.out; implement these explicit settings: Initial blur: 12px, Initial opacity: 0. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Blur Reveal to the layer in with Initial blur: 12px, Initial opacity: 0, using the supplied core snippet and a 0.6s segment with power2.out. Capture at 0s, 0.3s, and 0.6s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "done", "files": { "html": "effects/blur-reveal/index.html", "mp4": "effects/blur-reveal/clip.mp4", "gif": "effects/blur-reveal/preview.gif", "poster": "effects/blur-reveal/poster.jpg" }, "no": "030" }, { "slug": "blurred-slide", "family": "entrance", "ko": "블러 슬라이드", "en": "Blurred Slide", "aka": [ "속도 흐림 슬라이드" ], "variants": [ "slide-in-blurred", "slide-out-blurred", "slide-in-blurred-top", "slide-in-blurred-tr", "slide-in-blurred-right", "slide-in-blurred-br", "slide-in-blurred-bottom", "slide-in-blurred-bl", "slide-in-blurred-left", "slide-in-blurred-tl", "slide-out-blurred-top", "slide-out-blurred-tr", "slide-out-blurred-right", "slide-out-blurred-br", "slide-out-blurred-bottom", "slide-out-blurred-bl", "slide-out-blurred-left", "slide-out-blurred-tl" ], "sources": [ { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" } ], "runtime": "css", "oneLiner": "길게 늘어나고 흐린 요소가 이동하며 원래 모양으로 선명하게 정착한다.", "conveys": "빠른 이동의 잔상과 속도를 보여 준다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "빠른 제품 이동을 표현할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "제품 이미지가 가로로 늘어난 잔상에서 정착한다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "작은 본문에 30px 흐림을 걸지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "600ms", "range": "420~840ms", "note": "한 번의 동작 기준" }, { "name": "이동 거리", "default": "800px", "range": "300~1000px", "note": "1920x1080 기준" }, { "name": "시작 흐림", "default": "30px", "range": "8~30px", "note": "최종 0px" }, { "name": "가로 축척", "default": "2", "range": "1.2~2", "note": "세로 축척 1" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 600ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{transform:translateX(-800px) scaleX(2);filter:blur(30px);opacity:0} to{transform:translateX(0) scaleX(1);filter:blur(0);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.6초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 블러 슬라이드의 지속 600ms, 이동 거리 800px, 시작 흐림 30px, 가로 축척 2, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.6초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 블러 슬라이드을 적용해. 지속 600ms, 이동 거리 800px, 시작 흐림 30px, 가로 축척 2, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 블러 슬라이드 키프레임을 추가해. 지속 600ms, 이동 거리 800px, 시작 흐림 30px, 가로 축척 2, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.3초·0.6초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "A stretched blurred element moves into place and resolves to its normal shape.", "conveysEn": "Shows speed through a directional motion smear.", "useWhenEn": [ "Show a product moving rapidly.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Blurred Slide to in . Implement the provided keyframes with 600ms duration, travel distance 800px, initial blur 30px, horizontal scale 2, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Blurred Slide keyframes to the styles for in using 600ms duration, travel distance 800px, initial blur 30px, horizontal scale 2, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.3, and 0.6 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "031" }, { "slug": "bomb-exit", "family": "entrance", "ko": "밤 퇴장", "en": "Bomb Exit", "aka": [ "회전 폭발 이탈" ], "variants": [ "bombLeftOut", "bombRightOut" ], "sources": [ { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 옆으로 기울어 이동하며 크게 흐려져 사라진다.", "conveys": "폭발이나 강한 이탈을 표현한다.", "purposes": [ "전환", "주목 끌기" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "게임 소품의 강한 이탈을 보여 줄 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "깨진 소품이 오른쪽으로 기울어 날아가며 흐려진다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "큰 화면 레이어에 50px blur를 쓰지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "1000ms", "range": "700~1400ms", "note": "한 번의 동작 기준" }, { "name": "회전", "default": "160deg", "range": "90~160deg", "note": "중앙 기준" }, { "name": "가로 이동", "default": "200px", "range": "120~400px", "note": "1920x1080 기준" }, { "name": "끝 흐림", "default": "50px", "range": "16~50px", "note": "퇴장 종료에만 최대" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 1000ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{transform:translateX(0) rotate(0);filter:blur(0);opacity:1} to{transform:translateX(200px) rotate(160deg);filter:blur(50px);opacity:0}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 1초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 밤 퇴장의 지속 1000ms, 회전 160deg, 가로 이동 200px, 끝 흐림 50px, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 1초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 밤 퇴장을 적용해. 지속 1000ms, 회전 160deg, 가로 이동 200px, 끝 흐림 50px, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 밤 퇴장 키프레임을 추가해. 지속 1000ms, 회전 160deg, 가로 이동 200px, 끝 흐림 50px, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.5초·1초 시점을 캡처해 시작 상태, 중간 변화, 최종 퇴장를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element tilts sideways, moves away, and disappears into a heavy blur.", "conveysEn": "Conveys an explosive departure or forceful removal.", "useWhenEn": [ "Show a game prop being forcefully removed.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Bomb Exit to in . Implement the provided keyframes with 1000ms duration, rotation 160deg, horizontal travel 200px, final blur 50px, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Bomb Exit keyframes to the styles for in using 1000ms duration, rotation 160deg, horizontal travel 200px, final blur 50px, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.5, and 1 seconds to verify the initial state, intermediate motion, and disappearance. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "032" }, { "slug": "corner-mask-swing", "family": "entrance", "ko": "코너 매트 스윙", "en": "Corner Mask Swing", "aka": [ "corner-swing-mask" ], "variants": [ "좌상", "우상", "좌하", "우하" ], "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/04-masks-mattes.md#corner-swing-mask", "license": "Apache-2.0" } ], "runtime": "gsap", "oneLiner": "가리는 판이 모서리를 축으로 회전하며 뒤의 내용을 연다.", "conveys": "문이나 덮개를 젖히는 공개를 느낀다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "덮개를 젖혀 제품을 공개할 때", "모서리 방향을 강조하는 전환에 쓸 때" ], "goodExample": "불투명 덮개가 좌상단 축으로 돌아 사진을 연다.", "badExample": "판 크기가 작아 시작부터 사진 일부가 노출된다.", "avoid": [ "판 크기가 작아 시작부터 사진 일부가 노출된다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "0.6s", "range": "0.42~0.84s", "note": "0초부터 시작하는 공개 구간" }, { "name": "회전", "default": "90deg", "range": "80~110deg", "note": "가림판을 프레임 밖으로 회전" }, { "name": "앵커", "default": "0% 0%", "range": "네 모서리", "note": "판 크기는 프레임 덮음" }, { "name": "이징", "default": "power3.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power3.out", "snippet": "gsap.set('.frame', {overflow:'hidden'});\ngsap.set('.cover', {transformOrigin:'0% 0%', rotation:0});\ntl.to('.cover', {rotation:90, duration:0.6, ease:'power3.out'}, 0);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 0.6초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 코너 매트 스윙, 0.6초, 회전 90deg; 앵커 0% 0%, power3.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 0.6초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 코너 매트 스윙을 적용해. 0.6초, 회전 90deg; 앵커 0% 0%, 이징 power3.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 코너 매트 스윙을 적용해. 0.6초, 회전 90deg; 앵커 0% 0%, power3.out를 사용하고 0초, 0.3초, 0.6초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "An opaque cover rotates around a corner to reveal content.", "conveysEn": "Suggests lifting a lid or swinging a door aside.", "useWhenEn": [ "Use when presenting corner mask swing in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Corner Mask Swing to in . Use a 0.6s segment with power3.out; implement these explicit settings: Rotation: 90deg, Transform origin: 0% 0%. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Corner Mask Swing to the layer in with Rotation: 90deg, Transform origin: 0% 0%, using the supplied core snippet and a 0.6s segment with power3.out. Capture at 0s, 0.3s, and 0.6s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "planned", "files": {}, "no": "033" }, { "slug": "counter-moving-matte", "family": "entrance", "ko": "역방향 매트", "en": "Counter Moving Matte", "aka": [ "역방향 매트 이동", "dual-layer-counter-move" ], "variants": [ "가로 역이동", "세로 역이동" ], "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/04-masks-mattes.md#dual-layer-counter-move", "license": "Apache-2.0" } ], "runtime": "gsap", "oneLiner": "창은 한쪽으로 열리고 안쪽 그림은 반대쪽으로 움직이며 나타난다.", "conveys": "경계와 내용이 분리된 역동적인 공개를 느낀다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "제품 사진을 역동적으로 공개할 때", "섹션의 이미지 창을 열 때" ], "goodExample": "창은 오른쪽에서 오고 내부 사진은 왼쪽에서 정렬된다.", "badExample": "부모 overflow가 없어 창 밖 사진이 보인다.", "avoid": [ "부모 overflow가 없어 창 밖 사진이 보인다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "0.6s", "range": "0.42~0.84s", "note": "0초부터 시작하는 공개 구간" }, { "name": "창 시작 이동", "default": "100%", "range": "80~100%", "note": "오버플로 숨김 부모 내부" }, { "name": "내용 시작 이동", "default": "-30%", "range": "-15~-40%", "note": "창과 반대 방향" }, { "name": "이징", "default": "power3.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power3.out", "snippet": "gsap.set('.frame', {overflow:'hidden'});\ntl.fromTo('.matte', {xPercent:100}, {xPercent:0, duration:0.6, ease:'power3.out'}, 0);\ntl.fromTo('.content', {xPercent:-30}, {xPercent:0, duration:0.6, ease:'power3.out'}, 0);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 0.6초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 역방향 매트, 0.6초, 창 시작 이동 100%; 내용 시작 이동 -30%, power3.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 0.6초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 역방향 매트을 적용해. 0.6초, 창 시작 이동 100%; 내용 시작 이동 -30%, 이징 power3.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 역방향 매트을 적용해. 0.6초, 창 시작 이동 100%; 내용 시작 이동 -30%, power3.out를 사용하고 0초, 0.3초, 0.6초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "A revealing window and its content move in opposite directions.", "conveysEn": "Separates the boundary from the image for a dynamic reveal.", "useWhenEn": [ "Use when presenting counter moving matte in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Counter Moving Matte to in . Use a 0.6s segment with power3.out; implement these explicit settings: Initial window translation: 100%, Initial content translation: -30%. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Counter Moving Matte to the layer in with Initial window translation: 100%, Initial content translation: -30%, using the supplied core snippet and a 0.6s segment with power3.out. Capture at 0s, 0.3s, and 0.6s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "planned", "files": {}, "no": "034" }, { "slug": "curtain-reveal", "family": "entrance", "ko": "커튼 리빌", "en": "Curtain Reveal", "aka": [ "Canopy part", "전경 가림막 열기" ], "variants": [], "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/canopy-part-title/registry-item.json", "license": "Apache-2.0" } ], "runtime": "css", "oneLiner": "앞쪽 잎이나 물체가 양쪽으로 쓸려 나가 뒤의 제목이 보인다.", "conveys": "숨어 있던 장면과 깊이를 공개한다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "전경 뒤의 제목을 공개할 때", "풍경 장면의 깊이를 열어 보일 때" ], "goodExample": "앞쪽 잎 두 층이 좌우 600px 이동해 제목을 연다.", "badExample": "가림막이 제목 위에 남아 글자를 가린다.", "avoid": [ "가림막이 제목 위에 남아 글자를 가린다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "1.2s", "range": "0.84~1.68s", "note": "0초부터 시작하는 공개 구간" }, { "name": "좌우 거리", "default": "600px", "range": "400~1100px", "note": "가림막이 제목을 완전히 벗어남" }, { "name": "층 시간차", "default": "0.08s", "range": "0~0.15s", "note": "전경 층의 깊이 표현" }, { "name": "이징", "default": "power3.inOut", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power3.inOut", "snippet": "gsap.set('.curtain-left, .curtain-right', {x:0});\ntl.to('.curtain-left', {x:-600, duration:1.2, ease:'power3.inOut'}, 0);\ntl.to('.curtain-right', {x:600, duration:1.2, ease:'power3.inOut'}, 0);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 1.2초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 커튼 리빌, 1.2초, 좌우 거리 600px; 층 시간차 0.08s, power3.inOut를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 1.2초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 커튼 리빌을 적용해. 1.2초, 좌우 거리 600px; 층 시간차 0.08s, 이징 power3.inOut로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 커튼 리빌을 적용해. 1.2초, 좌우 거리 600px; 층 시간차 0.08s, power3.inOut를 사용하고 0초, 0.6초, 1.2초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "Foreground objects part to reveal a title behind them.", "conveysEn": "Reveals a hidden scene with depth.", "useWhenEn": [ "Use when presenting curtain reveal in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Curtain Reveal to in . Use a 1.2s segment with power3.inOut; implement these explicit settings: Horizontal travel per side: 600px, Layer delay: 0.08s. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Curtain Reveal to the layer in with Horizontal travel per side: 600px, Layer delay: 0.08s, using the supplied core snippet and a 1.2s segment with power3.inOut. Capture at 0s, 0.6s, and 1.2s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "planned", "files": {}, "no": "035" }, { "slug": "depixelate-reveal", "family": "entrance", "ko": "디픽셀 리빌", "en": "Depixelate Reveal", "aka": [ "Depixelate reveal", "픽셀 블록에서 선명화" ], "variants": [], "sources": [ { "title": "local/HyperFrames-skills", "url": "claude-skill:media-use/references/media-treatments.md", "license": "unknown" } ], "runtime": "webgl", "oneLiner": "큰 색 블록으로 가려진 이미지가 작은 블록으로 풀리며 선명해진다.", "conveys": "모호한 대상이 실제 정보로 드러나는 과정을 보여준다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "고급", "useWhen": [ "이미지 생성 결과가 확정될 때", "숨긴 사진을 단계적으로 공개할 때" ], "goodExample": "32px 블록 사진이 1.2초 동안 원본으로 선명해진다.", "badExample": "블러만 줄이고 픽셀 블록 효과라고 부른다.", "avoid": [ "블러만 줄이고 픽셀 블록 효과라고 부른다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "1.2s", "range": "0.84~1.68s", "note": "0초부터 시작하는 공개 구간" }, { "name": "시작 블록", "default": "32px", "range": "16~64px", "note": "1920x1080 출력 픽셀 기준" }, { "name": "최종 블록", "default": "1px", "range": "1~2px", "note": "원본 해상도로 복구" }, { "name": "이징", "default": "power2.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power2.out", "snippet": "// material.uniforms.blockPx controls UV quantization in the shader.\nconst block = material.uniforms.blockPx;\ngsap.set(block, {value:32});\ntl.to(block, {value:1, duration:1.2, ease:'power2.out'}, 0);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 1.2초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 디픽셀 리빌, 1.2초, 시작 블록 32px; 최종 블록 1px, power2.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 1.2초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 디픽셀 리빌을 적용해. 1.2초, 시작 블록 32px; 최종 블록 1px, 이징 power2.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 디픽셀 리빌을 적용해. 1.2초, 시작 블록 32px; 최종 블록 1px, power2.out를 사용하고 0초, 0.6초, 1.2초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "Large pixel blocks resolve into a sharp image.", "conveysEn": "Shows ambiguous imagery becoming concrete information.", "useWhenEn": [ "Use when presenting depixelate reveal in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Depixelate Reveal to in . Use a 1.2s segment with power2.out; implement these explicit settings: Initial block size: 32px, Final block size: 1px. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Depixelate Reveal to the layer in with Initial block size: 32px, Final block size: 1px, using the supplied core snippet and a 1.2s segment with power2.out. Capture at 0s, 0.6s, and 1.2s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "planned", "files": {}, "no": "036" }, { "slug": "elliptic-entrance", "family": "entrance", "ko": "타원 궤도 등장", "en": "Elliptic Entrance", "aka": [ "타원 궤도 진입" ], "variants": [ "slide-in-elliptic", "slide-out-elliptic", "앞", "뒤", "slide-in-elliptic-top-fwd", "slide-in-elliptic-top-bck", "slide-in-elliptic-right-fwd", "slide-in-elliptic-right-bck", "slide-in-elliptic-bottom-fwd", "slide-in-elliptic-bottom-bck", "slide-in-elliptic-left-fwd", "slide-in-elliptic-left-bck", "slide-out-elliptic-top-bck", "slide-out-elliptic-top-fwd", "slide-out-elliptic-right-bck", "slide-out-elliptic-right-fwd", "slide-out-elliptic-bottom-bck", "slide-out-elliptic-bottom-fwd", "slide-out-elliptic-left-bck", "slide-out-elliptic-left-fwd" ], "sources": [ { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" } ], "runtime": "css", "oneLiner": "요소가 3D로 기울고 찌그러진 상태에서 타원 곡선을 따라 정면으로 들어온다.", "conveys": "입체적인 이동 경로를 느끼게 한다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "고급", "useWhen": [ "카드가 곡선 경로로 도착할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "제품 카드가 위쪽에서 휘어 내려오며 정면을 향한다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "정밀한 궤도가 필요하면 위치 구간을 더 나눈다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "700ms", "range": "489~979ms", "note": "한 번의 동작 기준" }, { "name": "가로 이동", "default": "500px", "range": "250~700px", "note": "중간 위치 120px,60px" }, { "name": "시작 회전", "default": "60deg", "range": "30~60deg", "note": "원근 거리 800px" }, { "name": "시작 축척", "default": "0.1", "range": "0.1~0.4", "note": "타원 궤도의 구간 근사" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 700ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n 0%{transform:perspective(800px) translate(500px,-200px) rotateY(60deg) scale(.1);opacity:0} 60%{transform:perspective(800px) translate(120px,60px) rotateY(20deg) scale(.8);opacity:1} 100%{transform:perspective(800px) translate(0,0) rotateY(0) scale(1);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.7초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 타원 궤도 등장의 지속 700ms, 가로 이동 500px, 시작 회전 60deg, 시작 축척 0.1, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.7초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 타원 궤도 등장을 적용해. 지속 700ms, 가로 이동 500px, 시작 회전 60deg, 시작 축척 0.1, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 타원 궤도 등장 키프레임을 추가해. 지속 700ms, 가로 이동 500px, 시작 회전 60deg, 시작 축척 0.1, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.35초·0.7초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "A tilted scaled element follows an elliptical-style path into a front-facing position.", "conveysEn": "Suggests a three-dimensional travel path.", "useWhenEn": [ "Bring a card in along a curved path.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Elliptic Entrance to in . Implement the provided keyframes with 700ms duration, horizontal travel 500px, initial rotation 60deg, initial scale 0.1, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Elliptic Entrance keyframes to the styles for in using 700ms duration, horizontal travel 500px, initial rotation 60deg, initial scale 0.1, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.35, and 0.7 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "037" }, { "slug": "fade-slide", "family": "entrance", "ko": "페이드 슬라이드", "en": "Fade Slide", "aka": [ "Fade Up", "Fade Through Translation", "Fade and slide entrance", "페이드 슬라이드 등장", "Alpha Cutout", "알파 컷아웃 리빌", "alpha-matte-cutout" ], "variants": [ "페이드 업", "페이드 다운", "좌우 등장", "대각선 등장", "반대 방향 퇴장", "컷아웃 피사체 등장" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "michalsnik/aos", "url": "https://github.com/michalsnik/aos", "license": "MIT" }, { "title": "jamiebuilds/tailwindcss-animate", "url": "https://github.com/jamiebuilds/tailwindcss-animate", "license": "MIT" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" } ], "runtime": "gsap", "oneLiner": "요소가 가장자리나 조금 낮은 위치에서 이동하며 선명하게 나타난다.", "conveys": "새 항목의 위치와 등장 순서를 읽는다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "기본", "useWhen": [ "새 목록 항목을 보여줄 때", "제목과 설명을 순차 등장시킬 때" ], "goodExample": "설명 카드가 아래 24px에서 올라오며 선명해진다.", "badExample": "본문마다 큰 이동을 걸어 읽는 위치가 흔들린다.", "avoid": [ "본문마다 큰 이동을 걸어 읽는 위치가 흔들린다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "0.5s", "range": "0.35~0.7s", "note": "0초부터 시작하는 공개 구간" }, { "name": "이동 거리", "default": "24px", "range": "12~48px", "note": "기준 위치 아래에서 시작" }, { "name": "시작 불투명도", "default": "0", "range": "0~0.2", "note": "완료 시 1" }, { "name": "이징", "default": "power2.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power2.out", "snippet": "gsap.set('.target', {y:24, opacity:0});\ntl.to('.target', {y:0, opacity:1, duration:0.5, ease:'power2.out'}, 0);\ntl.set('.target', {y:0, opacity:1}, 0.5);", "pairsWith": [ "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 0.5초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 페이드 슬라이드, 0.5초, 이동 거리 24px; 시작 불투명도 0, power2.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 0.5초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 페이드 슬라이드을 적용해. 0.5초, 이동 거리 24px; 시작 불투명도 0, 이징 power2.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 페이드 슬라이드을 적용해. 0.5초, 이동 거리 24px; 시작 불투명도 0, power2.out를 사용하고 0초, 0.25초, 0.5초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "An element fades in while sliding into position.", "conveysEn": "Makes the location and arrival order of new content clear.", "useWhenEn": [ "Use when presenting fade slide in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Fade Slide to in . Use a 0.5s segment with power2.out; implement these explicit settings: Vertical travel: 24px, Initial opacity: 0. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Fade Slide to the layer in with Vertical travel: 24px, Initial opacity: 0, using the supplied core snippet and a 0.5s segment with power2.out. Capture at 0s, 0.25s, and 0.5s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "done", "files": { "html": "effects/fade-slide/index.html", "mp4": "effects/fade-slide/clip.mp4", "gif": "effects/fade-slide/preview.gif", "poster": "effects/fade-slide/poster.jpg" }, "no": "038" }, { "slug": "fade", "family": "entrance", "ko": "페이드", "en": "Fade", "aka": [ "Fade In and Out", "페이드 등장과 퇴장", "Opacity Reveal", "Dissolve In", "Fade visibility transition", "FadeIn", "FadeOut", "FadeInFromPoint" ], "variants": [ "페이드 인", "페이드 아웃", "등장 후 퇴장", "micro-scale-fade", "scale-down-fade" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "michalsnik/aos", "url": "https://github.com/michalsnik/aos", "license": "MIT" }, { "title": "foundation/motion-ui", "url": "https://github.com/foundation/motion-ui", "license": "MIT" }, { "title": "jamiebuilds/tailwindcss-animate", "url": "https://github.com/jamiebuilds/tailwindcss-animate", "license": "MIT" }, { "title": "pixel-point/animate-text", "url": "https://github.com/pixel-point/animate-text", "license": "unknown" }, { "title": "jschr/textillate", "url": "https://github.com/jschr/textillate", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 제자리에 머문 채 투명도만 바뀌어 나타나거나 사라진다.", "conveys": "조용한 시작과 종료를 알린다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "기본", "useWhen": [ "본문을 조용히 보여 줄 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "설명 문장이 위치 변화 없이 600ms 동안 나타난다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "같은 위치의 두 문장이 오래 겹치지 않게 한다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "600ms", "range": "420~840ms", "note": "한 번의 동작 기준" }, { "name": "시작 불투명도", "default": "0", "range": "0~0.3", "note": "등장 시작" }, { "name": "끝 불투명도", "default": "1", "range": "0.8~1", "note": "읽을 수 있는 최종 상태" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 600ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{opacity:0} to{opacity:1}\n}", "pairsWith": [ "slide", "mask-reveal" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.6초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 페이드의 지속 600ms, 시작 불투명도 0, 끝 불투명도 1, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.6초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 페이드을 적용해. 지속 600ms, 시작 불투명도 0, 끝 불투명도 1, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 페이드 키프레임을 추가해. 지속 600ms, 시작 불투명도 0, 끝 불투명도 1, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.3초·0.6초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element appears or disappears through opacity alone.", "conveysEn": "Creates a quiet beginning or ending.", "useWhenEn": [ "Reveal body text quietly.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Fade to in . Implement the provided keyframes with 600ms duration, initial opacity 0, final opacity 1, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Fade keyframes to the styles for in using 600ms duration, initial opacity 0, final opacity 1, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.3, and 0.6 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "039" }, { "slug": "flicker-reveal", "family": "entrance", "ko": "플리커 리빌", "en": "Flicker Reveal", "aka": [ "깜박이며 나타나기", "Text Opacity Flicker", "텍스트 점멸", "Glyph Flicker Reveal", "글자 깜박임 등장" ], "variants": [ "flicker-in", "flicker-out", "flicker-in-1", "flicker-in-2", "flicker-out-1", "flicker-out-2", "네온 점멸", "글자별 랜덤 점멸", "무작위 점등", "순차 점등" ], "sources": [ { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/animating-text/text-animation/animating-text.html", "license": "unknown" }, { "title": "airbnb/lottie-web", "url": "https://github.com/airbnb/lottie-web", "license": "MIT" }, { "title": "codrops/OnScrollTypographyAnimations", "url": "https://github.com/codrops/OnScrollTypographyAnimations", "license": "MIT" }, { "title": "codrops/TextBlockTransitions", "url": "https://github.com/codrops/TextBlockTransitions", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 불규칙하게 켜졌다 꺼졌다 하다가 완전히 나타나거나 사라진다.", "conveys": "전기 신호나 불안정한 연결을 표현한다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "가상의 신호 연결을 보여 줄 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "작은 통신 아이콘이 고정된 여섯 번의 점멸 뒤 켜진다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "큰 면적의 점멸과 반복 재생을 피한다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "1200ms", "range": "840~1680ms", "note": "한 번의 동작 기준" }, { "name": "점멸 횟수", "default": "6회", "range": "2~6회", "note": "고정된 불규칙 시점" }, { "name": "불투명도", "default": "0 또는 1", "range": "0~1", "note": "중간 보간 없음" } ], "ease": "steps(1, end)", "snippet": ".effect { animation: effect 1200ms steps(1, end) both; }\n@keyframes effect {\n 0%,12%,31%,48%,63%,77%,89%{opacity:0} 8%,25%,43%,58%,72%,84%,100%{opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 1.2초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 플리커 리빌의 지속 1200ms, 점멸 횟수 6회, 불투명도 0 또는 1, 이징 steps(1, end)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 1.2초 동작에 매핑한다. 점멸 시점은 고정 진행률 구간으로 유지한다." }, "prompts": { "claude": "<파일>에서 <대상>에 플리커 리빌을 적용해. 지속 1200ms, 점멸 횟수 6회, 불투명도 0 또는 1, 이징 steps(1, end)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 플리커 리빌 키프레임을 추가해. 지속 1200ms, 점멸 횟수 6회, 불투명도 0 또는 1, 이징 steps(1, end)을 적용하고 0초·0.6초·1.2초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element switches on and off at uneven intervals before becoming fully visible.", "conveysEn": "Suggests an electrical signal or an unstable connection.", "useWhenEn": [ "Represent a fictional signal connecting.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Flicker Reveal to in . Implement the provided keyframes with 1200ms duration, flickers 6, opacity 0 or 1, easing steps(1, end). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Flicker Reveal keyframes to the styles for in using 1200ms duration, flickers 6, opacity 0 or 1, easing steps(1, end). Capture at 0, 0.6, and 1.2 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "040" }, { "slug": "flip-reveal", "family": "entrance", "ko": "플립 등장", "en": "Flip Reveal", "aka": [ "Flip In and Out", "뒤집기 등장과 퇴장", "Slit Reveal", "얇은 면 펼치기" ], "variants": [ "가로축", "세로축", "대각축", "반동 동반", "깊이 이동 동반", "플립 퇴장" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "michalsnik/aos", "url": "https://github.com/michalsnik/aos", "license": "MIT" } ], "runtime": "css", "oneLiner": "얇은 옆면으로 보이던 요소가 3D 축을 돌아 정면으로 나타난다.", "conveys": "카드나 정보의 앞뒤 관계를 알린다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "카드의 새 정보를 공개할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "답안 카드가 얇은 옆면에서 정면으로 펼쳐진다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "뒷면 글자가 비치지 않게 한다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "700ms", "range": "489~979ms", "note": "한 번의 동작 기준" }, { "name": "원근 거리", "default": "800px", "range": "600~1200px", "note": "3D 깊이" }, { "name": "시작 회전", "default": "90deg", "range": "60~90deg", "note": "세로축 기준" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 700ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n.effect { backface-visibility: hidden; }\n@keyframes effect {\n from{transform:perspective(800px) rotateY(90deg);opacity:0} to{transform:perspective(800px) rotateY(0);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.7초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 플립 등장의 지속 700ms, 원근 거리 800px, 시작 회전 90deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.7초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 플립 등장을 적용해. 지속 700ms, 원근 거리 800px, 시작 회전 90deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 플립 등장 키프레임을 추가해. 지속 700ms, 원근 거리 800px, 시작 회전 90deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.35초·0.7초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An edge-on element rotates around a 3D axis to face the viewer.", "conveysEn": "Suggests the front and back of a card or piece of information.", "useWhenEn": [ "Reveal new information on a card.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Flip Reveal to in . Implement the provided keyframes with 700ms duration, perspective 800px, initial rotation 90deg, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Flip Reveal keyframes to the styles for in using 700ms duration, perspective 800px, initial rotation 90deg, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.35, and 0.7 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "done", "files": { "html": "effects/flip-reveal/index.html", "mp4": "effects/flip-reveal/clip.mp4", "gif": "effects/flip-reveal/preview.gif", "poster": "effects/flip-reveal/poster.jpg" }, "no": "041" }, { "slug": "foolish-reveal", "family": "entrance", "ko": "풀리시 리빌", "en": "Foolish Reveal", "aka": [ "모서리 순환 회전 등장" ], "variants": [ "foolishIn", "foolishOut" ], "sources": [ { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 크기를 바꾸며 여러 모서리를 축으로 차례로 회전해 정착한다.", "conveys": "장난스럽고 예측하기 어려운 진입을 만든다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "고급", "useWhen": [ "익살스러운 캐릭터 소품을 넣을 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "작은 선물 상자가 네 모서리를 차례로 축 삼아 회전한다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "모서리 변경으로 생기는 위치 점프를 확인한다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "1000ms", "range": "700~1400ms", "note": "한 번의 동작 기준" }, { "name": "총 회전", "default": "360deg", "range": "180~360deg", "note": "네 구간 분할" }, { "name": "기준점", "default": "모서리 4곳", "range": "2~4곳", "note": "마지막 중앙 복원" }, { "name": "시작 축척", "default": "0", "range": "0~0.2", "note": "최종 1" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 1000ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n 0%{transform-origin:0 0;transform:rotate(-360deg) scale(0);opacity:0} 25%{transform-origin:100% 0;transform:rotate(-270deg) scale(.4);opacity:1} 50%{transform-origin:100% 100%;transform:rotate(-180deg) scale(.7)} 75%{transform-origin:0 100%;transform:rotate(-90deg) scale(.9)} 100%{transform-origin:50% 50%;transform:rotate(0) scale(1);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 1초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 풀리시 리빌의 지속 1000ms, 총 회전 360deg, 기준점 모서리 4곳, 시작 축척 0, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 1초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 풀리시 리빌을 적용해. 지속 1000ms, 총 회전 360deg, 기준점 모서리 4곳, 시작 축척 0, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 풀리시 리빌 키프레임을 추가해. 지속 1000ms, 총 회전 360deg, 기준점 모서리 4곳, 시작 축척 0, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.5초·1초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element scales in while rotating around successive corner pivots.", "conveysEn": "Creates a playful, unpredictable entrance.", "useWhenEn": [ "Introduce a comic character prop.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Foolish Reveal to in . Implement the provided keyframes with 1000ms duration, total rotation 360deg, pivot four corners, initial scale 0, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Foolish Reveal keyframes to the styles for in using 1000ms duration, total rotation 360deg, pivot four corners, initial scale 0, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.5, and 1 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "042" }, { "slug": "hinge-drop", "family": "entrance", "ko": "힌지 드롭", "en": "Hinge Drop", "aka": [ "경첩 매달림 낙하", "Text Hinge Drop", "텍스트 경첩 낙하" ], "variants": [ "hinge", "단어 낙하", "제목 낙하" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "jschr/textillate", "url": "https://github.com/jschr/textillate", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 모서리에 매달려 흔들리다가 아래로 떨어져 사라진다.", "conveys": "실패나 제거를 익살스럽게 알린다.", "purposes": [ "전환", "주목 끌기" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "가벼운 게임 실패를 표현할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "게임 배지가 좌상단에 매달려 흔들린 뒤 아래로 떨어진다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "결제 실패 같은 중요한 오류에는 쓰지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "1600ms", "range": "1120~2240ms", "note": "한 번의 동작 기준" }, { "name": "최대 회전", "default": "70deg", "range": "45~75deg", "note": "좌상단 기준점" }, { "name": "낙하 거리", "default": "700px", "range": "500~1100px", "note": "마지막 15%에 낙하" }, { "name": "흔들림", "default": "3회", "range": "2~3회", "note": "고정 키프레임" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 1600ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n.effect { transform-origin: 0 0; }\n@keyframes effect {\n 0%{transform:rotate(0)} 20%,50%,75%{transform:rotate(70deg)} 35%,65%,85%{transform:rotate(50deg);opacity:1} 100%{transform:translateY(700px) rotate(70deg);opacity:0}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 1.6초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 힌지 드롭의 지속 1600ms, 최대 회전 70deg, 낙하 거리 700px, 흔들림 3회, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 1.6초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 힌지 드롭을 적용해. 지속 1600ms, 최대 회전 70deg, 낙하 거리 700px, 흔들림 3회, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 힌지 드롭 키프레임을 추가해. 지속 1600ms, 최대 회전 70deg, 낙하 거리 700px, 흔들림 3회, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.8초·1.6초 시점을 캡처해 시작 상태, 중간 변화, 최종 퇴장를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element swings from a corner, then drops out of view.", "conveysEn": "Gives failure or removal a comic tone.", "useWhenEn": [ "Express a lighthearted game failure.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Hinge Drop to in . Implement the provided keyframes with 1600ms duration, maximum rotation 70deg, drop distance 700px, swings 3, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Hinge Drop keyframes to the styles for in using 1600ms duration, maximum rotation 70deg, drop distance 700px, swings 3, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.8, and 1.6 seconds to verify the initial state, intermediate motion, and disappearance. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "043" }, { "slug": "hinge-reveal", "family": "entrance", "ko": "힌지 리빌", "en": "Hinge Reveal", "aka": [ "Hinge Open", "경첩 열기", "door-hinge-open", "orbit-3d-entry", "Swing Reveal", "문처럼 열리는 등장", "Boing Reveal", "탄성 힌지 튀기" ], "variants": [ "좌우 경첩", "상하 경첩", "안쪽 열기", "바깥쪽 열기", "반동 동반 열기" ], "sources": [ { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "foundation/motion-ui", "url": "https://github.com/foundation/motion-ui", "license": "MIT" }, { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/01-anchor-transform.md#door-hinge-open", "license": "Apache-2.0" } ], "runtime": "gsap", "oneLiner": "패널이 한쪽 변을 축으로 기울어진 상태에서 정면으로 열린다.", "conveys": "닫혀 있던 정보가 열리는 느낌을 준다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "닫힌 정보 패널을 열 때", "카드가 문처럼 등장할 때" ], "goodExample": "카드가 왼쪽 변을 축으로 90도에서 정면으로 열린다.", "badExample": "원근 없이 회전해 패널이 폭만 줄어든다.", "avoid": [ "원근 없이 회전해 패널이 폭만 줄어든다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "0.65s", "range": "0.455~0.91s", "note": "0초부터 시작하는 공개 구간" }, { "name": "시작 각도", "default": "90deg", "range": "60~90deg", "note": "왼쪽 경첩 기준" }, { "name": "원근 거리", "default": "800px", "range": "600~1200px", "note": "부모에 설정" }, { "name": "앵커", "default": "0% 50%", "range": "좌우 또는 상하 변", "note": "회전축 고정" }, { "name": "이징", "default": "power3.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power3.out", "snippet": "gsap.set('.panel-parent', {perspective:800});\ngsap.set('.target', {transformOrigin:'0% 50%', backfaceVisibility:'hidden'});\ntl.fromTo('.target', {rotationY:90}, {rotationY:0, duration:0.65, ease:'power3.out'}, 0);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 0.65초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 힌지 리빌, 0.65초, 시작 각도 90deg; 원근 거리 800px; 앵커 0% 50%, power3.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 0.65초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 힌지 리빌을 적용해. 0.65초, 시작 각도 90deg; 원근 거리 800px; 앵커 0% 50%, 이징 power3.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 힌지 리빌을 적용해. 0.65초, 시작 각도 90deg; 원근 거리 800px; 앵커 0% 50%, power3.out를 사용하고 0초, 0.325초, 0.65초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "A panel rotates from its edge into a front-facing position.", "conveysEn": "Makes concealed information feel like it is opening.", "useWhenEn": [ "Use when presenting hinge reveal in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Hinge Reveal to in . Use a 0.65s segment with power3.out; implement these explicit settings: Initial angle: 90deg, Perspective: 800px, Transform origin: 0% 50%. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Hinge Reveal to the layer in with Initial angle: 90deg, Perspective: 800px, Transform origin: 0% 50%, using the supplied core snippet and a 0.65s segment with power3.out. Capture at 0s, 0.325s, and 0.65s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "planned", "files": {}, "no": "044" }, { "slug": "jack-in-the-box", "family": "entrance", "ko": "잭 인 더 박스", "en": "Jack in the Box", "aka": [ "상자에서 튀어나오기" ], "variants": [ "jackInTheBox" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" } ], "runtime": "css", "oneLiner": "요소가 작고 기울어진 상태에서 갑자기 커진 뒤 좌우 회전하며 안정된다.", "conveys": "새 대상의 탄생과 놀라움을 준다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "보상 아이콘을 공개할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "획득한 선물이 작게 시작해 커지고 좌우로 정착한다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "모든 목록 항목에 반복하지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "1000ms", "range": "700~1400ms", "note": "한 번의 동작 기준" }, { "name": "시작 축척", "default": "0.1", "range": "0.1~0.3", "note": "하단 중앙 기준점" }, { "name": "시작 회전", "default": "30deg", "range": "15~30deg", "note": "반동 -10deg와 5deg" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 1000ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n.effect { transform-origin: 50% 100%; }\n@keyframes effect {\n 0%{transform:scale(.1) rotate(30deg);opacity:0} 55%{transform:scale(1) rotate(-10deg);opacity:1} 75%{transform:rotate(5deg)} 100%{transform:rotate(0)}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 1초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 잭 인 더 박스의 지속 1000ms, 시작 축척 0.1, 시작 회전 30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 1초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 잭 인 더 박스을 적용해. 지속 1000ms, 시작 축척 0.1, 시작 회전 30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 잭 인 더 박스 키프레임을 추가해. 지속 1000ms, 시작 축척 0.1, 시작 회전 30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.5초·1초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element pops up from a tiny tilted state and settles through alternating rotations.", "conveysEn": "Suggests a new arrival and surprise.", "useWhenEn": [ "Reveal a reward icon.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Jack in the Box to in . Implement the provided keyframes with 1000ms duration, initial scale 0.1, initial rotation 30deg, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Jack in the Box keyframes to the styles for in using 1000ms duration, initial scale 0.1, initial rotation 30deg, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.5, and 1 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "045" }, { "slug": "light-speed", "family": "entrance", "ko": "라이트 스피드", "en": "Light Speed", "aka": [ "빠른 진입과 기울기 정착" ], "variants": [ "lightSpeedInLeft", "lightSpeedInRight", "lightSpeedOutLeft", "lightSpeedOutRight" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" } ], "runtime": "css", "oneLiner": "요소가 빠르게 옆에서 들어오며 기울었다가 반대쪽으로 꺾이며 펴진다.", "conveys": "속도감과 급한 도착을 표현한다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "속보 배너를 빠르게 넣을 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "짧은 알림 배너가 옆에서 들어와 기울기를 두 번 줄인다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "작은 글자를 긴 시간 기울이지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "700ms", "range": "489~979ms", "note": "한 번의 동작 기준" }, { "name": "이동 거리", "default": "100%", "range": "200px~100%", "note": "요소 너비 기준" }, { "name": "시작 기울기", "default": "30deg", "range": "15~30deg", "note": "정착 반동 -8deg와 4deg" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 700ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n 0%{transform:translateX(-100%) skewX(30deg);opacity:0} 70%{transform:translateX(0) skewX(-8deg);opacity:1} 85%{transform:skewX(4deg)} 100%{transform:skewX(0)}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.7초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 라이트 스피드의 지속 700ms, 이동 거리 100%, 시작 기울기 30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.7초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 라이트 스피드을 적용해. 지속 700ms, 이동 거리 100%, 시작 기울기 30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 라이트 스피드 키프레임을 추가해. 지속 700ms, 이동 거리 100%, 시작 기울기 30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.35초·0.7초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element rushes in sideways, skews, then straightens with a small counter-skew.", "conveysEn": "Conveys speed and an abrupt arrival.", "useWhenEn": [ "Bring in a short breaking-news banner.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Light Speed to in . Implement the provided keyframes with 700ms duration, travel distance 100%, initial skew 30deg, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Light Speed keyframes to the styles for in using 700ms duration, travel distance 100%, initial skew 30deg, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.35, and 0.7 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "046" }, { "slug": "magic-exit", "family": "entrance", "ko": "매직 퇴장", "en": "Magic Exit", "aka": [ "Magic Disappearance", "마술 회전 이탈" ], "variants": [ "magic" ], "sources": [ { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 한쪽으로 커지며 회전하고 화면 밖으로 사라진다.", "conveys": "극적인 제거와 장면 종료를 만든다.", "purposes": [ "전환", "주목 끌기" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "장난스러운 장면을 끝낼 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "마술 소품 카드가 우하단을 축으로 커지며 사라진다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "중요한 확인 메시지를 빠르게 제거하지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "1000ms", "range": "700~1400ms", "note": "한 번의 동작 기준" }, { "name": "회전", "default": "270deg", "range": "180~270deg", "note": "우하단 기준점" }, { "name": "끝 축척", "default": "2", "range": "1.4~2", "note": "확대하며 퇴장" }, { "name": "이동 거리", "default": "100%", "range": "200px~100%", "note": "요소 너비 기준" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 1000ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n.effect { transform-origin: 100% 100%; }\n@keyframes effect {\n from{transform:translateX(0) rotate(0) scale(1);opacity:1} to{transform:translateX(100%) rotate(270deg) scale(2);opacity:0}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 1초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 매직 퇴장의 지속 1000ms, 회전 270deg, 끝 축척 2, 이동 거리 100%, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 1초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 매직 퇴장을 적용해. 지속 1000ms, 회전 270deg, 끝 축척 2, 이동 거리 100%, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 매직 퇴장 키프레임을 추가해. 지속 1000ms, 회전 270deg, 끝 축척 2, 이동 거리 100%, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.5초·1초 시점을 캡처해 시작 상태, 중간 변화, 최종 퇴장를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element grows, rotates around an offset pivot, and leaves the frame.", "conveysEn": "Creates a dramatic removal or scene ending.", "useWhenEn": [ "End a playful scene.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Magic Exit to in . Implement the provided keyframes with 1000ms duration, rotation 270deg, final scale 2, travel distance 100%, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Magic Exit keyframes to the styles for in using 1000ms duration, rotation 270deg, final scale 2, travel distance 100%, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.5, and 1 seconds to verify the initial state, intermediate motion, and disappearance. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "047" }, { "slug": "mosaic-reveal", "family": "entrance", "ko": "모자이크 리빌", "en": "Mosaic Reveal", "aka": [ "mosaic-matte-collage", "tile-mosaic" ], "variants": [ "대각선 공개", "바둑판 공개", "불규칙 콜라주" ], "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/04-masks-mattes.md#mosaic-matte-collage", "license": "Apache-2.0" }, { "title": "local/music-to-video", "url": "claude-skill:music-to-video/references/motion-primitive-catalog.md#tile-mosaic", "license": "unknown" }, { "title": "local/music-to-video", "url": "claude-skill:music-to-video/references/motion-primitives/tile-mosaic/scene.html", "license": "unknown" }, { "title": "local/music-to-video", "url": "claude-skill:music-to-video/references/motion-primitives/tile-mosaic/index.html", "license": "unknown" } ], "runtime": "gsap", "oneLiner": "작은 타일 창이 차례로 열려 한 장의 이미지나 포스터를 완성한다.", "conveys": "부분의 축적이 전체가 되는 것을 읽는다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "여러 조각이 포스터를 완성할 때", "콜라주 이미지를 단계적으로 보여줄 때" ], "goodExample": "6x4 타일이 행 순서로 열려 1.6초에 포스터를 완성한다.", "badExample": "매 실행마다 순서를 바꿔 캡처 결과가 달라진다.", "avoid": [ "매 실행마다 순서를 바꿔 캡처 결과가 달라진다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "1.6s", "range": "1.12~2.24s", "note": "0초부터 시작하는 공개 구간" }, { "name": "타일 격자", "default": "6x4", "range": "4x3~8x6", "note": "원본 이미지 좌표 공유" }, { "name": "타일 시간차", "default": "0.04s", "range": "0.02~0.06s", "note": "고정된 행 우선 순서" }, { "name": "타일 지속", "default": "0.68s", "range": "0.4~0.8s", "note": "24개 마지막 시작 0.92초" }, { "name": "이징", "default": "power2.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power2.out", "snippet": "const tiles = gsap.utils.toArray('.tile');\ngsap.set(tiles, {opacity:0});\ntl.to(tiles, {opacity:1, duration:0.68, stagger:0.04, ease:'power2.out'}, 0);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 1.6초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 모자이크 리빌, 1.6초, 타일 격자 6x4; 타일 시간차 0.04s; 타일 지속 0.68s, power2.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 1.6초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 모자이크 리빌을 적용해. 1.6초, 타일 격자 6x4; 타일 시간차 0.04s; 타일 지속 0.68s, 이징 power2.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 모자이크 리빌을 적용해. 1.6초, 타일 격자 6x4; 타일 시간차 0.04s; 타일 지속 0.68s, power2.out를 사용하고 0초, 0.8초, 1.6초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "Small tile windows reveal one complete image in sequence.", "conveysEn": "Shows accumulated parts forming a whole.", "useWhenEn": [ "Use when presenting mosaic reveal in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Mosaic Reveal to in . Use a 1.6s segment with power2.out; implement these explicit settings: Tile grid: 6x4, Tile delay: 0.04s, Tile duration: 0.68s. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Mosaic Reveal to the layer in with Tile grid: 6x4, Tile delay: 0.04s, Tile duration: 0.68s, using the supplied core snippet and a 1.6s segment with power2.out. Capture at 0s, 0.8s, and 1.6s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "planned", "files": {}, "no": "048" }, { "slug": "physical-exit", "family": "entrance", "ko": "관성 퇴장", "en": "Physical Exit", "aka": [ "Physical exit" ], "variants": [], "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/physical-exit/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/transitions-other/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/transitions/css-other.md", "license": "unknown" } ], "runtime": "gsap", "oneLiner": "요소가 회전하거나 가속하면서 화면 밖으로 던져지거나 떨어진다.", "conveys": "퇴장에도 물리적 방향과 무게가 남는다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "기본", "useWhen": [ "버린 카드를 화면 밖으로 보낼 때", "장면의 이전 오브젝트를 정리할 때" ], "goodExample": "폐기 카드가 오른쪽으로 가속하며 12도 기울어 사라진다.", "badExample": "필수 안내가 읽히기 전에 떨어져 사라진다.", "avoid": [ "필수 안내가 읽히기 전에 떨어져 사라진다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "0.5s", "range": "0.35~0.7s", "note": "0초부터 시작하는 공개 구간" }, { "name": "퇴장 거리", "default": "1200px", "range": "800~2200px", "note": "대상 경계가 화면 밖에 도착하도록 조정" }, { "name": "회전", "default": "12deg", "range": "6~24deg", "note": "이동 방향에 맞춘 기울기" }, { "name": "이징", "default": "power2.in", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power2.in", "snippet": "gsap.set('.target', {x:0, rotation:0});\ntl.to('.target', {x:1200, rotation:12, duration:0.5, ease:'power2.in'}, 0);\ntl.set('.target', {visibility:'hidden'}, 0.5);", "pairsWith": [ "fade-slide", "stagger", "scale-pop" ], "engines": { "hyperframes": "paused 타임라인에 0.5초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 관성 퇴장, 0.5초, 퇴장 거리 1200px; 회전 12deg, power2.in를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 0.5초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 관성 퇴장을 적용해. 0.5초, 퇴장 거리 1200px; 회전 12deg, 이징 power2.in로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 관성 퇴장을 적용해. 0.5초, 퇴장 거리 1200px; 회전 12deg, power2.in를 사용하고 0초, 0.25초, 0.5초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "An element accelerates off screen while rotating or falling.", "conveysEn": "Gives an exit direction and physical weight.", "useWhenEn": [ "Use when presenting physical exit in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Physical Exit to in . Use a 0.5s segment with power2.in; implement these explicit settings: Exit distance: 1200px, Rotation: 12deg. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Physical Exit to the layer in with Exit distance: 1200px, Rotation: 12deg, using the supplied core snippet and a 0.5s segment with power2.in. Capture at 0s, 0.25s, and 0.5s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "planned", "files": {}, "no": "049" }, { "slug": "puff-reveal", "family": "entrance", "ko": "퍼프 리빌", "en": "Puff Reveal", "aka": [ "흐림 확대 리빌", "Blur Scale Reveal", "Vanish", "압축 흐림 소멸" ], "variants": [ "확대 상태에서 축소 등장", "축소 상태에서 확대 등장", "확대하며 흐려지는 퇴장", "축소하며 흐려지는 퇴장" ], "sources": [ { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" }, { "title": "magicuidesign/magicui", "url": "https://magicui.design/docs/components/blur-fade", "license": "MIT" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" }, { "title": "ibelick/motion-primitives", "url": "https://github.com/ibelick/motion-primitives", "license": "MIT" } ], "runtime": "css", "oneLiner": "확대되고 흐린 요소가 줄어들며 선명해지거나 확대되며 흐려져 사라진다.", "conveys": "공기처럼 부드러운 생성과 소멸을 표현한다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "이미지나 로고를 부드럽게 공개할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "로고가 확대된 흐림 상태에서 제 크기로 선명해진다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "전체 화면의 큰 blur 레이어는 피한다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "700ms", "range": "489~979ms", "note": "한 번의 동작 기준" }, { "name": "시작 축척", "default": "2", "range": "1.2~2", "note": "중앙 기준" }, { "name": "시작 흐림", "default": "12px", "range": "4~16px", "note": "최종 0px" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 700ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{transform:scale(2);filter:blur(12px);opacity:0} to{transform:scale(1);filter:blur(0);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.7초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 퍼프 리빌의 지속 700ms, 시작 축척 2, 시작 흐림 12px, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.7초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 퍼프 리빌을 적용해. 지속 700ms, 시작 축척 2, 시작 흐림 12px, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 퍼프 리빌 키프레임을 추가해. 지속 700ms, 시작 축척 2, 시작 흐림 12px, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.35초·0.7초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An enlarged blurred element shrinks into focus, or expands and blurs away.", "conveysEn": "Creates a soft appearance or disappearance.", "useWhenEn": [ "Reveal an image or logo softly.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Puff Reveal to in . Implement the provided keyframes with 700ms duration, initial scale 2, initial blur 12px, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Puff Reveal keyframes to the styles for in using 700ms duration, initial scale 2, initial blur 12px, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.35, and 0.7 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "050" }, { "slug": "roll-reveal", "family": "entrance", "ko": "롤 등장", "en": "Roll Reveal", "aka": [ "Roll In and Out", "구르기 등장과 퇴장" ], "variants": [ "rollIn", "rollOut", "roll-in", "roll-out", "roll-in-blurred", "roll-out-blurred", "roll-in-left", "roll-in-top", "roll-in-right", "roll-in-bottom", "roll-in-blurred-left", "roll-in-blurred-top", "roll-in-blurred-right", "roll-in-blurred-bottom", "roll-out-left", "roll-out-top", "roll-out-right", "roll-out-bottom", "roll-out-blurred-left", "roll-out-blurred-top", "roll-out-blurred-right", "roll-out-blurred-bottom" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" } ], "runtime": "css", "oneLiner": "요소가 옆으로 이동하면서 평면 회전해 굴러 들어오거나 나간다.", "conveys": "재미있는 이동과 방향을 전달한다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "원형 아이콘을 장난스럽게 넣을 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "원형 쿠폰 아이콘이 왼쪽에서 구르며 들어온다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "표나 긴 텍스트에는 쓰지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "700ms", "range": "489~979ms", "note": "한 번의 동작 기준" }, { "name": "이동 거리", "default": "100%", "range": "100px~100%", "note": "요소 너비 기준" }, { "name": "시작 회전", "default": "-120deg", "range": "-180~-60deg", "note": "이동과 동시 회전" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 700ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{transform:translateX(-100%) rotate(-120deg);opacity:0} to{transform:translateX(0) rotate(0);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.7초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 롤 등장의 지속 700ms, 이동 거리 100%, 시작 회전 -120deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.7초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 롤 등장을 적용해. 지속 700ms, 이동 거리 100%, 시작 회전 -120deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 롤 등장 키프레임을 추가해. 지속 700ms, 이동 거리 100%, 시작 회전 -120deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.35초·0.7초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element rolls in or out through sideways movement and planar rotation.", "conveysEn": "Communicates playful movement and direction.", "useWhenEn": [ "Introduce a round icon playfully.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Roll Reveal to in . Implement the provided keyframes with 700ms duration, travel distance 100%, initial rotation -120deg, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Roll Reveal keyframes to the styles for in using 700ms duration, travel distance 100%, initial rotation -120deg, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.35, and 0.7 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "051" }, { "slug": "rotate-reveal", "family": "entrance", "ko": "회전 등장", "en": "Rotate Reveal", "aka": [ "Rotate In and Out", "회전 등장과 퇴장", "Spin Reveal" ], "variants": [ "rotateIn", "rotateOut", "rotate-in", "rotate-in-2", "rotate-out", "rotate-out-2", "시계", "반시계", "모서리 기준", "rotateInDownLeft", "rotateInDownRight", "rotateInUpLeft", "rotateInUpRight", "rotateOutDownLeft", "rotateOutDownRight", "rotateOutUpLeft", "rotateOutUpRight", "rotate-in-center", "rotate-in-top", "rotate-in-tr", "rotate-in-right", "rotate-in-br", "rotate-in-bottom", "rotate-in-bl", "rotate-in-left", "rotate-in-tl", "rotate-in-hor", "rotate-in-ver", "rotate-in-diag-1", "rotate-in-diag-2", "rotate-in-2-cw", "rotate-in-2-ccw", "rotate-in-2-fwd-cw", "rotate-in-2-fwd-ccw", "rotate-in-2-bck-cw", "rotate-in-2-bck-ccw", "rotate-in-2-tr-cw", "rotate-in-2-tr-ccw", "rotate-in-2-br-cw", "rotate-in-2-br-ccw", "rotate-in-2-bl-cw", "rotate-in-2-bl-ccw", "rotate-in-2-tl-cw", "rotate-in-2-tl-ccw", "rotate-out-center", "rotate-out-top", "rotate-out-tr", "rotate-out-right", "rotate-out-br", "rotate-out-bottom", "rotate-out-bl", "rotate-out-left", "rotate-out-tl", "rotate-out-hor", "rotate-out-ver", "rotate-out-diag-1", "rotate-out-diag-2", "rotate-out-2-cw", "rotate-out-2-ccw", "rotate-out-2-bck", "rotate-out-2-fwd", "rotate-out-2-tr-cw", "rotate-out-2-tr-ccw", "rotate-out-2-br-cw", "rotate-out-2-br-ccw", "rotate-out-2-bl-cw", "rotate-out-2-bl-ccw", "rotate-out-2-tl-cw", "rotate-out-2-tl-ccw" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "jamiebuilds/tailwindcss-animate", "url": "https://github.com/jamiebuilds/tailwindcss-animate", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 평면에서 회전하며 나타나거나 회전하며 사라진다.", "conveys": "장면에 방향감과 역동성을 더한다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "배지 방향을 강조할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "작은 배지가 반시계 방향에서 정면으로 돌아 나타난다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "긴 문장에는 큰 회전을 피한다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "600ms", "range": "420~840ms", "note": "한 번의 동작 기준" }, { "name": "시작 회전", "default": "-90deg", "range": "-90~90deg", "note": "평면 회전" }, { "name": "기준점", "default": "50% 50%", "range": "중앙 또는 모서리", "note": "회전 경로 확보" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 600ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{transform:rotate(-90deg);opacity:0} to{transform:rotate(0);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.6초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 회전 등장의 지속 600ms, 시작 회전 -90deg, 기준점 50% 50%, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.6초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 회전 등장을 적용해. 지속 600ms, 시작 회전 -90deg, 기준점 50% 50%, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 회전 등장 키프레임을 추가해. 지속 600ms, 시작 회전 -90deg, 기준점 50% 50%, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.3초·0.6초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element rotates in the plane while appearing or disappearing.", "conveysEn": "Adds direction and energy to a reveal.", "useWhenEn": [ "Give a badge a directional reveal.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Rotate Reveal to in . Implement the provided keyframes with 600ms duration, initial rotation -90deg, pivot 50% 50%, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Rotate Reveal keyframes to the styles for in using 600ms duration, initial rotation -90deg, pivot 50% 50%, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.3, and 0.6 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "052" }, { "slug": "scale-pop", "family": "entrance", "ko": "스케일 팝", "en": "Scale Pop", "aka": [ "Scale pop entrance", "스케일 팝 등장", "spring-pop-entrance", "overlay-pop", "Scale In and Out", "스케일 등장과 퇴장", "Zoom Reveal", "Grow Reveal", "Grow from anchor", "앵커에서 자라기", "GrowFromPoint", "GrowFromCenter", "GrowFromEdge", "ShrinkToCenter" ], "variants": [ "중심 성장", "가장자리 성장", "임의 앵커 성장", "한 축 성장", "확대 상태에서 축소 등장", "페이드 동반", "축소 퇴장", "탄성 팝은 spring-overshoot 조합" ], "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/number-pop-in/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/instagram-follow/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/tiktok-follow/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/reddit-post/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/x-post/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "oneLiner": "요소가 작은 크기에서 제 크기로 커져 멈춘다. 반대로 축소되어 사라질 수 있다.", "conveys": "새 요소의 도착을 분명하게 알린다.", "purposes": [ "주목 끌기", "전환" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "기본", "useWhen": [ "새 배지나 숫자를 등장시킬 때", "작은 팝업의 도착을 알릴 때" ], "goodExample": "완료 배지가 0.5초 동안 중심에서 커진다.", "badExample": "긴 본문 전체를 0에서 확대해 읽기를 방해한다.", "avoid": [ "긴 본문 전체를 0에서 확대해 읽기를 방해한다.", "완료 자세에서 내용이 가려지지 않게 한다." ], "params": [ { "name": "지속", "default": "0.5s", "range": "0.35~0.7s", "note": "0초부터 시작하는 공개 구간" }, { "name": "시작 크기", "default": "0", "range": "0~0.85", "note": "중심에서 성장" }, { "name": "앵커", "default": "50% 50%", "range": "중앙 또는 가장자리", "note": "성장 방향 기준" }, { "name": "이징", "default": "power3.out", "range": "power2.out, power3.out, sine.inOut", "note": "움직임의 가속과 감속" } ], "ease": "power3.out", "snippet": "gsap.set('.target', {transformOrigin:'50% 50%'});\ntl.fromTo('.target', {scale:0}, {scale:1, duration:0.5, ease:'power3.out'}, 0);\ntl.set('.target', {scale:1}, 0.5);", "pairsWith": [ "fade-slide", "stagger" ], "engines": { "hyperframes": "paused 타임라인에 0.5초 구간을 넣고 seek로 자세를 계산한다. 시작값과 완료값을 명시한다.", "reelforge": "씬 워커 브리프에 스케일 팝, 0.5초, 시작 크기 0; 앵커 50% 50%, power3.out를 싣고 대상 레이어를 지정한다.", "scrolline": "진행률 0~1을 0.5초 구간에 매핑한다. scrub에서는 스프링 대신 ease-out으로 같은 시작값과 완료값을 보간한다." }, "prompts": { "claude": "<파일>에서 <대상>에 스케일 팝을 적용해. 0.5초, 시작 크기 0; 앵커 50% 50%, 이징 power3.out로 구현해. paused 타임라인으로 seek 가능하게 하고 완료 자세를 유지해.", "codex": "<파일>의 <대상> 레이어에 스케일 팝을 적용해. 0.5초, 시작 크기 0; 앵커 50% 50%, power3.out를 사용하고 0초, 0.25초, 0.5초 캡처로 시작값, 중간 변화, 완료 자세를 확인해. 동일 시점을 두 번 seek해 같은 결과인지 검증해." }, "oneLinerEn": "An element grows from a small scale to its final size.", "conveysEn": "Clearly marks the arrival of a new element.", "useWhenEn": [ "Use when presenting scale pop in a content reveal scene.", "Use for a focused title, image, or card with a clearly visible final state." ], "promptsEn": { "claude": "Apply Scale Pop to in . Use a 0.5s segment with power3.out; implement these explicit settings: Initial scale: 0, Transform origin: 50% 50%. Use a paused, seekable timeline and hold the final pose.", "codex": "Apply Scale Pop to the layer in with Initial scale: 0, Transform origin: 50% 50%, using the supplied core snippet and a 0.5s segment with power3.out. Capture at 0s, 0.25s, and 0.5s to verify the initial pose, intermediate change, and final pose. Seek to the same timestamp twice and confirm identical output." }, "render": "done", "files": { "html": "effects/scale-pop/index.html", "mp4": "effects/scale-pop/clip.mp4", "gif": "effects/scale-pop/preview.gif", "poster": "effects/scale-pop/poster.jpg" }, "no": "053" }, { "slug": "slide", "family": "entrance", "ko": "슬라이드", "en": "Slide", "aka": [ "Slide In and Out", "슬라이드 등장과 퇴장", "Text Group Slide", "텍스트 묶음 슬라이드", "Line-by-Line Slide", "Short Slide Right", "Short Slide Down" ], "variants": [ "slideIn", "slideOut", "slide-in", "slide-out", "slide", "slideReturn", "상하좌우", "대각선", "slideInDown", "slideInLeft" ], "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" }, { "title": "michalsnik/aos", "url": "https://github.com/michalsnik/aos", "license": "MIT" }, { "title": "foundation/motion-ui", "url": "https://github.com/foundation/motion-ui", "license": "MIT" }, { "title": "pixel-point/animate-text", "url": "https://github.com/pixel-point/animate-text", "license": "unknown" }, { "title": "codrops/TextBlockTransitions", "url": "https://github.com/codrops/TextBlockTransitions", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 화면 가장자리 또는 일정 거리 밖에서 들어오거나 빠져나간다.", "conveys": "요소의 출처와 이동 방향을 알려 준다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "기본", "useWhen": [ "옆 패널의 출처를 보여 줄 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "안내 패널이 자신의 너비만큼 왼쪽에서 들어온다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "이동 경로에 본문을 두지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "600ms", "range": "420~840ms", "note": "한 번의 동작 기준" }, { "name": "이동 거리", "default": "100%", "range": "48px~100%", "note": "요소 너비 기준" }, { "name": "방향", "default": "왼쪽", "range": "상하좌우", "note": "부모 overflow hidden" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 600ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{transform:translateX(-100%)} to{transform:translateX(0)}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.6초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 슬라이드의 지속 600ms, 이동 거리 100%, 방향 왼쪽, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.6초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 슬라이드을 적용해. 지속 600ms, 이동 거리 100%, 방향 왼쪽, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 슬라이드 키프레임을 추가해. 지속 600ms, 이동 거리 100%, 방향 왼쪽, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.3초·0.6초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element slides in from an edge or moves out of view.", "conveysEn": "Makes the origin and direction of movement clear.", "useWhenEn": [ "Show where a side panel comes from.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Slide to in . Implement the provided keyframes with 600ms duration, travel distance 100%, direction left, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Slide keyframes to the styles for in using 600ms duration, travel distance 100%, direction left, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.3, and 0.6 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "054" }, { "slug": "swap-entrance", "family": "entrance", "ko": "스왑 등장", "en": "Swap Entrance", "aka": [ "큰 궤도 축소 진입" ], "variants": [ "swap" ], "sources": [ { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" } ], "runtime": "css", "oneLiner": "큰 크기로 화면 밖에 있던 요소가 넓게 돌아 작아지며 제자리에 들어온다.", "conveys": "과장된 교체와 깜짝 등장을 알린다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "새 주인공 카드로 교체할 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "큰 제품 카드가 넓게 돌아 들어와 작은 정규 카드로 정착한다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "겹치는 카드의 본문을 동시에 읽게 하지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "1000ms", "range": "700~1400ms", "note": "한 번의 동작 기준" }, { "name": "시작 축척", "default": "3", "range": "2~3", "note": "최종 1" }, { "name": "가로 이동", "default": "700px", "range": "400~900px", "note": "중간 위치 180px,60px" }, { "name": "시작 회전", "default": "-30deg", "range": "-45~-15deg", "note": "우상단 기준점" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 1000ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n.effect { transform-origin: 100% 0; }\n@keyframes effect {\n 0%{transform:translate(700px,-200px) rotate(-30deg) scale(3);opacity:0} 60%{transform:translate(180px,60px) rotate(-10deg) scale(1.4);opacity:1} 100%{transform:translate(0,0) rotate(0) scale(1);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 1초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 스왑 등장의 지속 1000ms, 시작 축척 3, 가로 이동 700px, 시작 회전 -30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 1초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 스왑 등장을 적용해. 지속 1000ms, 시작 축척 3, 가로 이동 700px, 시작 회전 -30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 스왑 등장 키프레임을 추가해. 지속 1000ms, 시작 축척 3, 가로 이동 700px, 시작 회전 -30deg, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.5초·1초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An oversized off-screen element sweeps around and shrinks into position.", "conveysEn": "Signals an exaggerated replacement or surprise arrival.", "useWhenEn": [ "Replace the hero card with a new one.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Swap Entrance to in . Implement the provided keyframes with 1000ms duration, initial scale 3, horizontal travel 700px, initial rotation -30deg, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Swap Entrance keyframes to the styles for in using 1000ms duration, initial scale 3, horizontal travel 700px, initial rotation -30deg, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.5, and 1 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "055" }, { "slug": "swirl-reveal", "family": "entrance", "ko": "스월 리빌", "en": "Swirl Reveal", "aka": [ "소용돌이 리빌", "Hole Disappearance", "구멍으로 말려 들어가기", "Spin grow entrance", "회전하며 커지는 등장", "SpinInFromNothing" ], "variants": [ "시계방향", "반시계방향", "회전 확대 등장", "회전 축소 퇴장", "반 회전", "여러 바퀴 회전" ], "sources": [ { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "miniMAC/magic", "url": "https://github.com/miniMAC/magic", "license": "MIT" }, { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/growing.py", "license": "MIT" } ], "runtime": "css", "oneLiner": "요소가 여러 바퀴 회전하며 축소 또는 확대되어 나타난다.", "conveys": "회오리 같은 강한 진입을 만든다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "마법 효과 아이콘을 등장시킬 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "작은 별 아이콘이 한 바퀴 반 돌며 제 크기가 된다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "읽어야 하는 문장에는 쓰지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "700ms", "range": "489~979ms", "note": "한 번의 동작 기준" }, { "name": "시작 회전", "default": "-540deg", "range": "-540~-180deg", "note": "중앙 기준" }, { "name": "시작 축척", "default": "0", "range": "0~0.3", "note": "회전과 동시 확대" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 700ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{transform:rotate(-540deg) scale(0);opacity:0} to{transform:rotate(0) scale(1);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.7초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 스월 리빌의 지속 700ms, 시작 회전 -540deg, 시작 축척 0, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.7초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 스월 리빌을 적용해. 지속 700ms, 시작 회전 -540deg, 시작 축척 0, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 스월 리빌 키프레임을 추가해. 지속 700ms, 시작 회전 -540deg, 시작 축척 0, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.35초·0.7초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element spins through multiple turns while scaling into view.", "conveysEn": "Creates a forceful, vortex-like entrance.", "useWhenEn": [ "Introduce a magic-effect icon.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Swirl Reveal to in . Implement the provided keyframes with 700ms duration, initial rotation -540deg, initial scale 0, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Swirl Reveal keyframes to the styles for in using 700ms duration, initial rotation -540deg, initial scale 0, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.35, and 0.7 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "056" }, { "slug": "tilt-entrance", "family": "entrance", "ko": "틸트 등장", "en": "Tilt Entrance", "aka": [ "기울기 복원 등장" ], "variants": [ "tilt-in", "tilt-in-fwd", "tilt-in-top-1", "tilt-in-top-2", "tilt-in-tr", "tilt-in-right-1", "tilt-in-right-2", "tilt-in-br", "tilt-in-bottom-1", "tilt-in-bottom-2", "tilt-in-bl", "tilt-in-left-1", "tilt-in-left-2", "tilt-in-tl", "tilt-in-fwd-tr", "tilt-in-fwd-br", "tilt-in-fwd-bl", "tilt-in-fwd-tl" ], "sources": [ { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" } ], "runtime": "css", "oneLiner": "요소가 비스듬한 평면에서 들어오며 기울기와 비틀림을 풀어 정렬된다.", "conveys": "역동적인 요소가 정돈되는 느낌을 준다.", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "포스터 카드를 정렬시킬 때", "장면의 주인공 한 요소에 시선을 모을 때" ], "goodExample": "기울어진 포스터가 올라오며 수평으로 정돈된다.", "badExample": "여러 요소에 동시에 적용해 읽을 위치와 시선을 계속 바꾸게 만든다.", "avoid": [ "읽는 동안 기울기를 유지하지 않는다.", "동작 줄이기 설정에서는 정적인 최종 상태를 보여 준다." ], "params": [ { "name": "지속 시간", "default": "650ms", "range": "454~909ms", "note": "한 번의 동작 기준" }, { "name": "시작 회전", "default": "30deg", "range": "10~30deg", "note": "중앙 기준" }, { "name": "시작 비틀림", "default": "15deg", "range": "5~15deg", "note": "가로 skew" }, { "name": "세로 이동", "default": "80px", "range": "40~120px", "note": "1920x1080 기준" } ], "ease": "cubic-bezier(0.22, 1, 0.36, 1)", "snippet": ".effect { animation: effect 650ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n@keyframes effect {\n from{transform:translateY(80px) rotate(30deg) skewX(15deg);opacity:0} to{transform:translateY(0) rotate(0) skewX(0);opacity:1}\n}", "pairsWith": [ "fade", "stagger" ], "engines": { "hyperframes": "CSS 키프레임 값을 paused 타임라인에 옮겨 0.65초 구간을 seek한다. 시작과 종료 상태를 명시한다.", "reelforge": "씬 워커 브리프에 틸트 등장의 지속 650ms, 시작 회전 30deg, 시작 비틀림 15deg, 세로 이동 80px, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 싣고 대상 한 요소와 안전 영역을 지정한다.", "scrolline": "진행률 0~1을 0.65초 동작에 매핑한다. scrub에서는 스프링 대신 ease-out을 사용한다." }, "prompts": { "claude": "<파일>에서 <대상>에 틸트 등장을 적용해. 지속 650ms, 시작 회전 30deg, 시작 비틀림 15deg, 세로 이동 80px, 이징 cubic-bezier(0.22, 1, 0.36, 1)으로 위 키프레임을 구현하고 최종 상태를 유지해. 동작 줄이기 설정에서는 최종 상태를 바로 보여 줘.", "codex": "<파일>의 <대상> 스타일에 틸트 등장 키프레임을 추가해. 지속 650ms, 시작 회전 30deg, 시작 비틀림 15deg, 세로 이동 80px, 이징 cubic-bezier(0.22, 1, 0.36, 1)을 적용하고 0초·0.325초·0.65초 시점을 캡처해 시작 상태, 중간 변화, 최종 정착를 확인해. 고정 키프레임으로 재현하고 동작 줄이기 설정을 확인해." }, "oneLinerEn": "An element arrives at an angle and removes its rotation and skew as it aligns.", "conveysEn": "Makes energetic movement feel organized at the end.", "useWhenEn": [ "Align a tilted poster card.", "Focus attention on one main element in a scene." ], "promptsEn": { "claude": "Apply Tilt Entrance to in . Implement the provided keyframes with 650ms duration, initial rotation 30deg, initial skew 15deg, vertical travel 80px, easing cubic-bezier(0.22, 1, 0.36, 1). Preserve the final state and show it immediately when reduced motion is enabled.", "codex": "Add Tilt Entrance keyframes to the styles for in using 650ms duration, initial rotation 30deg, initial skew 15deg, vertical travel 80px, easing cubic-bezier(0.22, 1, 0.36, 1). Capture at 0, 0.325, and 0.65 seconds to verify the initial state, intermediate motion, and settled state. Use fixed keyframes and verify reduced-motion behavior." }, "render": "planned", "files": {}, "no": "057" }, { "slug": "blur-resolve", "family": "entrance", "ko": "블러 해제", "en": "Blur Resolve", "aka": [ "Blur / Focus resolve", "초점 흐림", "Focus Text Reveal", "글자 초점 리빌", "Text Blur Reveal", "텍스트 블러 리빌", "Text Blur Resolve", "텍스트 초점 해소" ], "oneLiner": "흐릿한 대상이 블러 반경이 줄며 선명해져 시선을 모으는 등장", "conveys": "초점이 맞는 순간에 눈이 간다. 흐림에서 선명으로 가는 방향이 시선을 이끈다", "purposes": [ "주목 끌기", "전환" ], "media": [ "숏폼", "설명 영상", "웹 UI" ], "level": "기본", "useWhen": [ "제목이나 제품 이미지가 초점 맞듯 나타날 때", "뒤 배경은 흐리게 두고 대상만 선명하게 밀어 올릴 때" ], "goodExample": "제목이 blur 4px, opacity 0에서 250ms 동안 blur 0, opacity 1로 선명해진다", "badExample": "blur 20px 이상으로 시작해 오래 걸리거나, 글자 위에 항상 블러가 남는다", "avoid": [ "blur 시작 3~8px", "길이 0.2~0.4초", "종료 blur는 정확히 0(filter none)" ], "params": [ { "name": "시작 blur", "default": "4px", "range": "3~8px", "note": "1920x1080 기준" }, { "name": "길이", "default": "250ms", "range": "200~400ms", "note": "선명해지는 시간" }, { "name": "opacity", "default": "0→1", "range": "0→1", "note": "blur와 함께 상승" }, { "name": "이징", "default": "power2.out", "range": "power2~power3.out", "note": "끝에서 감속" } ], "ease": "power2.out", "snippet": "tl.fromTo('.title', { filter: 'blur(4px)', opacity: 0 }, { filter: 'blur(0px)', opacity: 1, duration: 0.25, ease: 'power2.out', clearProps: 'filter' }, 0.3);", "pairsWith": [ "rack-focus", "fade", "blur-reveal" ], "engines": { "hyperframes": "filter blur는 seek에서 결정론이지만 큰 요소에서 렌더가 무겁다. 작은 범위에 걸고 clearProps로 종료 후 filter를 제거", "reelforge": "브리프에 시작 blur·길이·대상 크기를 싣는다. 전체 화면 블러는 피한다", "scrolline": "scrub에서는 blur를 진행률 0~0.4에 4→0으로 매핑한다. 진행률이 커진 뒤 filter none으로 고정" }, "prompts": { "claude": "GSAP으로 <대상> 제목에 블러 해제를 넣어줘. 0.3초에 filter blur(4px), opacity 0에서 시작해 0.25초 동안 blur(0px), opacity 1로 power2.out. 종료 시 clearProps로 filter를 제거해 글자가 선명하게 남게 해. paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 blur-resolve를 적용해. fromTo({filter blur(4px), opacity 0}, {blur(0px), opacity 1}, 0.25s, power2.out, position 0.3, clearProps filter). 0.3초·0.42초·0.8초를 캡처해 흐림 정도가 줄어드는지, 0.8초에 computed filter가 none인지 확인해." }, "sources": [ { "title": "motion dictionary 1-principles.md#8. 2D 속성 기본 동작", "url": "", "license": "own" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/animating-text/text-animation/animating-text.html", "license": "unknown" }, { "title": "airbnb/lottie-web", "url": "https://github.com/airbnb/lottie-web", "license": "MIT" }, { "title": "pixel-point/animate-text", "url": "https://github.com/pixel-point/animate-text", "license": "unknown" } ], "runtime": "gsap", "variants": [ "text-focus-in", "focus-in", "text-blur-out", "blur-out", "BlurText", "focus-in-expand", "focus-in-expand-fwd", "focus-in-contract", "focus-in-contract-bck", "blur-out-contract" ], "oneLinerEn": "A blurred target sharpens as its blur radius falls, pulling the eye.", "conveysEn": "The eye goes to what comes into focus. The blur to sharp direction leads attention.", "useWhenEn": [ "When a title or product shot arrives as if focusing", "When the background stays soft and only the target sharpens" ], "promptsEn": { "claude": "Add a blur resolve to the title with GSAP. At 0.3s start with filter blur(4px), opacity 0 and animate to blur(0px), opacity 1 over 0.25s with power2.out. Use clearProps to remove filter afterward so text stays sharp. Paused timeline.", "codex": "Apply blur-resolve to in . fromTo({filter blur(4px), opacity 0}, {blur(0px), opacity 1}, 0.25s, power2.out, position 0.3, clearProps filter). Capture at 0.3s, 0.42s and 0.8s to check the blur shrinks and that computed filter is none at 0.8s." }, "render": "planned", "files": {}, "no": "058" }, { "slug": "clip-reveal", "family": "entrance", "ko": "클립 리빌", "en": "Clip Reveal", "aka": [ "Clip reveal" ], "oneLiner": "경계 안에서 보이는 영역을 넓혀 내용을 한쪽에서 드러내는 등장", "conveys": "경계를 따라 내용이 열리며 읽는 방향을 이끈다", "purposes": [ "설명", "순서·흐름" ], "media": [ "설명 영상", "웹 UI", "스크롤덱", "발표" ], "level": "기본", "useWhen": [ "제목·이미지가 아래에서 위로 열리듯 나타날 때", "차트 막대나 선이 방향성 있게 공개될 때" ], "goodExample": "이미지가 clip-path inset(100% 0 0 0)에서 inset(0)으로 350ms 동안 위로 열린다", "badExample": "clip-path 시작 상태가 CSS에 없어 첫 프레임에 전체가 보였다가 사라진다", "avoid": [ "시작 상태를 CSS에 미리 넣는다", "방향은 읽는 방향과 맞춘다", "길이 0.25~0.6초" ], "params": [ { "name": "길이", "default": "350ms", "range": "250~600ms", "note": "공개 시간" }, { "name": "inset", "default": "100%→0%", "range": "방향별", "note": "상향 공개 시 top" }, { "name": "방향", "default": "상향", "range": "상/하/좌/우", "note": "읽는 방향 기준" }, { "name": "이징", "default": "power3.out", "range": "power2~power4.out", "note": "끝이 부드럽게" } ], "ease": "power3.out", "snippet": "tl.fromTo('.img', { clipPath: 'inset(100% 0% 0% 0%)' }, { clipPath: 'inset(0% 0% 0% 0%)', duration: 0.35, ease: 'power3.out' }, 0.3);", "pairsWith": [ "mask-reveal", "wipe", "blinds-reveal" ], "engines": { "hyperframes": "clipPath 문자열 보간은 형식이 같아야 안전하다(inset 4값). fromTo로 시작을 명시하고 CSS에도 같은 초기값을 둔다", "reelforge": "브리프에 방향·길이·대상 목록을 싣는다. 텍스트 줄 단위 공개는 줄마다 overflow 컨테이너 사용", "scrolline": "scrub에서는 inset을 진행률에 선형으로 매핑하고 ease-out. 되감기 시 자연스럽게 닫힌다" }, "prompts": { "claude": "GSAP으로 <대상> 이미지에 클립 리빌을 넣어줘. 0.3초부터 0.35초 동안 clipPath를 inset(100% 0% 0% 0%)에서 inset(0% 0% 0% 0%)로 power3.out으로 열어 위쪽으로 공개해. 초기값을 CSS에도 같이 넣어 첫 프레임 깜빡임을 막아. paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 clip-reveal을 적용해. fromTo clipPath inset(100% 0 0 0)→inset(0 0 0 0), 0.35s, power3.out, position 0.3, CSS 초기값도 동일. 0.0초·0.4초·0.8초를 캡처해 0초에 보이지 않고 0.8초에 전체가 보이는지 확인해." }, "sources": [ { "title": "motion dictionary 1-principles.md#8. 2D 속성 기본 동작", "url": "", "license": "own" } ], "runtime": "gsap", "variants": [ "상향 공개", "하향 공개", "수평 공개" ], "oneLinerEn": "The visible region inside a boundary widens to reveal content from one side.", "conveysEn": "Content opens along its edge and guides the reading direction.", "useWhenEn": [ "When a title or image opens upward like a shutter", "When bars or lines are revealed with a direction" ], "promptsEn": { "claude": "Add a clip reveal to the image with GSAP. From 0.3s over 0.35s open clipPath from inset(100% 0% 0% 0%) to inset(0% 0% 0% 0%) with power3.out, revealing upward. Put the same initial value in CSS to avoid a first-frame flash. Paused timeline.", "codex": "Apply clip-reveal to in . fromTo clipPath inset(100% 0 0 0) to inset(0 0 0 0), 0.35s, power3.out, position 0.3, with the same initial value in CSS. Capture at 0s, 0.4s and 0.8s to check it is hidden at 0s and fully visible at 0.8s." }, "render": "planned", "files": {}, "no": "059" }, { "slug": "hard-appearance", "family": "entrance", "ko": "즉시 등장", "en": "Hard Appearance", "aka": [ "Hard appearance cut", "즉시 나타나기", "Add" ], "variants": [ "단일 등장", "그룹 동시 등장" ], "sources": [ { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/animation.py", "license": "MIT" } ], "runtime": "gsap", "oneLiner": "정해진 순간에 요소가 중간 과정 없이 화면에 나타난다.", "conveys": "컷의 순간을 분명히 하고 즉시 새 정보를 제시한다.", "purposes": [ "설명", "강조" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "컷에 맞춰 정보를 즉시 제시할 때", "연속 동작 없이 상태를 바꿀 때" ], "goodExample": "0.4초 컷 순간에 결론 카드를 바로 켜고 0.8초 유지한다.", "badExample": "긴 설명 중 새 요소를 예고 없이 계속 켜 주의가 분산된다.", "avoid": [ "같은 장면의 여러 대상에 동시에 적용하지 않는다.", "동작 감소 설정에서는 정적인 대표 상태를 보여 준다." ], "params": [ { "name": "전환 지속", "default": "0ms", "range": "0ms", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "유지", "default": "800ms", "range": "500~1500ms", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "등장 시점", "default": "400ms", "range": "0~2000ms", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "최종 불투명도", "default": "1", "range": "1", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." } ], "ease": "none", "snippet": "const tl = gsap.timeline({ paused: true });\ntl.set('.item', { autoAlpha: 0 }, 0);\ntl.set('.item', { autoAlpha: 1 }, 0.4);\ntl.to({}, { duration: 0.8 }, 0.4);", "pairsWith": [ "hard-cut", "word-emphasis" ], "engines": { "hyperframes": "paused GSAP 타임라인으로 즉시 등장의 절대 시간을 구동하고 seek 뒤 같은 상태를 다시 그린다.", "reelforge": "씬 워커 브리프에 전환 지속 0ms, 유지 800ms, 등장 시점 400ms, 최종 불투명도 1을 싣고 gsap 레이어의 대상과 합성 순서를 지정한다.", "scrolline": "진행률 0~1을 0ms 구간에 매핑한다. scrub에서는 스프링 대신 power2.out을 쓰고 역스크롤에도 같은 상태를 계산한다." }, "prompts": { "claude": "<대상>에 즉시 등장을 구현해. 전환 지속 0ms, 유지 800ms, 등장 시점 400ms, 최종 불투명도 1, 이징 none을 적용해. 타임라인의 지정 시점에 visibility를 바꾼다. paused 타임라인과 절대 시간으로 구동하고 Math.random 없이 같은 시점의 상태를 재현해.", "codex": "<파일>의 즉시 등장 장면 레이어에 적용해. 전환 지속 0ms, 유지 800ms, 등장 시점 400ms, 최종 불투명도 1, 이징 none을 사용하고 본문과 분리해. 0초, 0.4초, 1.2초 시점을 캡처해 시작 상태와 중간 변화 및 대상 가독성을 확인하고 같은 시점을 다시 seek해 결과가 같은지 검증해." }, "oneLinerEn": "An element appears instantly at a defined moment.", "conveysEn": "Makes a cut explicit and presents information immediately.", "useWhenEn": [ "Present information exactly on a cut.", "Change state without an animated transition." ], "promptsEn": { "claude": "Implement Hard Appearance on . Use transition duration 0ms; hold duration 800ms; appearance time 400ms; final opacity 1; use none easing. Drive the effect with a paused GSAP timeline and absolute time, and reproduce the same state on every seek without Math.random. Keep foreground text readable.", "codex": "Apply Hard Appearance to the scene layer in . Use transition duration 0ms; hold duration 800ms; appearance time 400ms; final opacity 1 and none easing. Capture at 0, 0.4, and 1.2 seconds to check the initial state, motion progression, and foreground readability; seek to each time again to verify identical output." }, "render": "planned", "files": {}, "no": "060" }, { "slug": "outline-flash-reveal", "family": "entrance", "ko": "윤곽 섬광 등장", "en": "Outline Flash Reveal", "aka": [ "Flashing-outline entrance", "윤곽 섬광 동반 등장", "FlashyFadeIn" ], "variants": [ "윤곽 먼저", "윤곽과 내부 동시" ], "sources": [ { "title": "3b1b/manim", "url": "https://github.com/3b1b/manim/blob/master/manimlib/animation/indication.py", "license": "MIT" } ], "runtime": "svg", "oneLiner": "밝은 윤곽선이 대상을 훑는 동안 내부 대상이 점점 선명해진다.", "conveys": "등장과 주의 신호를 같은 순간에 만든다.", "purposes": [ "설명", "강조" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "아이콘 등장에 주의를 모을 때", "윤곽과 내부의 순서를 보여 줄 때" ], "goodExample": "제품 아이콘 윤곽이 먼저 훑고 0.12초 뒤 내부가 선명해진다.", "badExample": "굵은 흰색 윤곽을 여러 번 번쩍여 작은 아이콘이 뭉개진다.", "avoid": [ "같은 장면의 여러 대상에 동시에 적용하지 않는다.", "동작 감소 설정에서는 정적인 대표 상태를 보여 준다." ], "params": [ { "name": "지속", "default": "900ms", "range": "600~1200ms", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "윤곽 두께", "default": "2px", "range": "1~3px", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "내부 지연", "default": "120ms", "range": "80~200ms", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "내부 최종 불투명도", "default": "1", "range": "1", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." } ], "ease": "power2.out", "snippet": "const path = document.querySelector('.outline');\nconst length = path.getTotalLength();\nconst tl = gsap.timeline({ paused: true });\ntl.set(path, { strokeDasharray: length, strokeDashoffset: length, strokeWidth: 2 });\ntl.to(path, { strokeDashoffset: 0, duration: 0.6, ease: 'power2.out' }, 0);\ntl.fromTo('.fill', { opacity: 0 }, { opacity: 1, duration: 0.78, ease: 'power2.out' }, 0.12);\ntl.to(path, { opacity: 0, duration: 0.3 }, 0.6);", "pairsWith": [ "outline-then-fill", "line-draw" ], "engines": { "hyperframes": "paused GSAP 타임라인으로 윤곽 섬광 등장의 절대 시간을 구동하고 seek 뒤 같은 상태를 다시 그린다.", "reelforge": "씬 워커 브리프에 지속 900ms, 윤곽 두께 2px, 내부 지연 120ms, 내부 최종 불투명도 1을 싣고 svg 레이어의 대상과 합성 순서를 지정한다.", "scrolline": "진행률 0~1을 900ms 구간에 매핑한다. scrub에서는 스프링 대신 power2.out을 쓰고 역스크롤에도 같은 상태를 계산한다." }, "prompts": { "claude": "<대상>에 윤곽 섬광 등장을 구현해. 지속 900ms, 윤곽 두께 2px, 내부 지연 120ms, 내부 최종 불투명도 1, 이징 power2.out을 적용해. SVG 윤곽의 이동 dash와 내부 fill-opacity를 같은 타임라인에서 보간한다. paused 타임라인과 절대 시간으로 구동하고 Math.random 없이 같은 시점의 상태를 재현해.", "codex": "<파일>의 윤곽 섬광 등장 장면 레이어에 적용해. 지속 900ms, 윤곽 두께 2px, 내부 지연 120ms, 내부 최종 불투명도 1, 이징 power2.out을 사용하고 본문과 분리해. 0초, 0.4초, 1.2초 시점을 캡처해 시작 상태와 중간 변화 및 대상 가독성을 확인하고 같은 시점을 다시 seek해 결과가 같은지 검증해." }, "oneLinerEn": "A bright outline traces the subject while its interior fades into clarity.", "conveysEn": "Combines an entrance with a focused attention cue.", "useWhenEn": [ "Draw attention to an entering icon.", "Explain the relationship between outline and fill." ], "promptsEn": { "claude": "Implement Outline Flash Reveal on . Use duration 900ms; stroke width 2px; fill delay 120ms; final fill opacity 1; use power2.out easing. Drive the effect with a paused GSAP timeline and absolute time, and reproduce the same state on every seek without Math.random. Keep foreground text readable.", "codex": "Apply Outline Flash Reveal to the scene layer in . Use duration 900ms; stroke width 2px; fill delay 120ms; final fill opacity 1 and power2.out easing. Capture at 0, 0.4, and 1.2 seconds to check the initial state, motion progression, and foreground readability; seek to each time again to verify identical output." }, "render": "planned", "files": {}, "no": "061" }, { "slug": "paper-shred-exit", "family": "entrance", "ko": "종이 파쇄 퇴장", "en": "Paper Shred Exit", "aka": [], "variants": [ "Shredder" ], "sources": [ { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" } ], "runtime": "canvas", "oneLiner": "문서가 좁은 틈으로 들어가 여러 가느다란 띠로 잘려 떨어지고 사라진다.", "conveys": "되돌리기 어려운 삭제와 해체를 표현한다.", "purposes": [ "설명", "강조" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "삭제 완료를 시각화할 때", "문서 해체를 설명할 때" ], "goodExample": "삭제 확인 후 문서가 틈 아래로 들어가 세로 띠로 떨어진다.", "badExample": "임시 숨김 동작에 파쇄를 써 영구 삭제처럼 보인다.", "avoid": [ "같은 장면의 여러 대상에 동시에 적용하지 않는다.", "동작 감소 설정에서는 정적인 대표 상태를 보여 준다." ], "params": [ { "name": "지속", "default": "2s", "range": "1.5~3s", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "공급 속도", "default": "180px/s", "range": "120~240px/s", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "띠 폭", "default": "10px", "range": "8~18px", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "낙하 거리", "default": "140px", "range": "100~220px", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." } ], "ease": "none", "snippet": "const tl = gsap.timeline({ paused: true });\ntl.to('.paper', { y: 180, duration: 1, ease: 'none' }, 0);\ntl.fromTo('.strip', { y: 0, opacity: 1 },\n { y: 140, rotation: 12, opacity: 0, duration: 1, stagger: 0.02, ease: 'power2.in' }, 0.6);", "pairsWith": [ "physical-exit", "shatter" ], "engines": { "hyperframes": "paused GSAP 타임라인으로 종이 파쇄 퇴장의 절대 시간을 구동하고 seek 뒤 같은 상태를 다시 그린다.", "reelforge": "씬 워커 브리프에 지속 2s, 공급 속도 180px/s, 띠 폭 10px, 낙하 거리 140px을 싣고 canvas 레이어의 대상과 합성 순서를 지정한다.", "scrolline": "진행률 0~1을 2s 구간에 매핑한다. scrub에서는 스프링 대신 power2.out을 쓰고 역스크롤에도 같은 상태를 계산한다." }, "prompts": { "claude": "<대상>에 종이 파쇄 퇴장을 구현해. 지속 2s, 공급 속도 180px/s, 띠 폭 10px, 낙하 거리 140px, 이징 none을 적용해. canvas 문서 영역을 세로 조각으로 나눠 틈 아래에서 낙하와 말림을 보간한다. paused 타임라인과 절대 시간으로 구동하고 Math.random 없이 같은 시점의 상태를 재현해.", "codex": "<파일>의 종이 파쇄 퇴장 장면 레이어에 적용해. 지속 2s, 공급 속도 180px/s, 띠 폭 10px, 낙하 거리 140px, 이징 none을 사용하고 본문과 분리해. 0초, 0.4초, 1.2초 시점을 캡처해 시작 상태와 중간 변화 및 대상 가독성을 확인하고 같은 시점을 다시 seek해 결과가 같은지 검증해." }, "oneLinerEn": "A document feeds through a slit and falls away as narrow strips.", "conveysEn": "Communicates destruction and a completed deletion.", "useWhenEn": [ "Visualize a confirmed deletion.", "Explain how a document is dismantled." ], "promptsEn": { "claude": "Implement Paper Shred Exit on . Use duration 2s; feed speed 180px/s; strip width 10px; fall distance 140px; use none easing. Drive the effect with a paused GSAP timeline and absolute time, and reproduce the same state on every seek without Math.random. Keep foreground text readable.", "codex": "Apply Paper Shred Exit to the scene layer in . Use duration 2s; feed speed 180px/s; strip width 10px; fall distance 140px and none easing. Capture at 0, 0.4, and 1.2 seconds to check the initial state, motion progression, and foreground readability; seek to each time again to verify identical output." }, "render": "planned", "files": {}, "no": "062" }, { "slug": "spiral-assembly", "family": "entrance", "ko": "나선 조립", "en": "Spiral Assembly", "aka": [ "Spiral assembly", "나선 궤도 조립", "SpiralIn" ], "variants": [ "안쪽 수렴", "바깥쪽 해체" ], "sources": [ { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/creation.py", "license": "MIT" } ], "runtime": "gsap", "oneLiner": "조각들이 바깥의 나선 궤도를 따라 들어와 최종 형태에 붙는다.", "conveys": "흩어진 조각이 하나의 구조로 모임을 느낀다.", "purposes": [ "설명", "강조" ], "media": [ "설명 영상", "웹 UI", "숏폼" ], "level": "중급", "useWhen": [ "흩어진 조각을 조립할 때", "하나의 구조로 수렴함을 보여 줄 때" ], "goodExample": "로고 조각 여덟 개가 나선을 따라 각자의 최종 좌표에 붙는다.", "badExample": "문장 글자를 크게 회전시켜 읽기 시작을 늦춘다.", "avoid": [ "같은 장면의 여러 대상에 동시에 적용하지 않는다.", "동작 감소 설정에서는 정적인 대표 상태를 보여 준다." ], "params": [ { "name": "지속", "default": "1.5s", "range": "1~2s", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "회전량", "default": "540deg", "range": "180~540deg", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "시작 반경", "default": "300px", "range": "160~400px", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." }, { "name": "조각 시간차", "default": "0.04s", "range": "0~0.08s", "note": "1920x1080 기준. 장면 시작을 0초로 둔다." } ], "ease": "power2.out", "snippet": "const tl = gsap.timeline({ paused: true });\ngsap.utils.toArray('.piece').forEach((el, i) => {\n const s = { p: 0 };\n tl.to(s, { p: 1, duration: 1.5, ease: 'power2.out', onUpdate: () => {\n const a = (1-s.p) * Math.PI * 3 + i * Math.PI / 4;\n gsap.set(el, { x: 300*(1-s.p)*Math.cos(a), y: 300*(1-s.p)*Math.sin(a) });\n } }, i * 0.04);\n});", "pairsWith": [ "piece-assembly", "arc-motion" ], "engines": { "hyperframes": "paused GSAP 타임라인으로 나선 조립의 절대 시간을 구동하고 seek 뒤 같은 상태를 다시 그린다.", "reelforge": "씬 워커 브리프에 지속 1.5s, 회전량 540deg, 시작 반경 300px, 조각 시간차 0.04s을 싣고 gsap 레이어의 대상과 합성 순서를 지정한다.", "scrolline": "진행률 0~1을 1.5s 구간에 매핑한다. scrub에서는 스프링 대신 power2.out을 쓰고 역스크롤에도 같은 상태를 계산한다." }, "prompts": { "claude": "<대상>에 나선 조립을 구현해. 지속 1.5s, 회전량 540deg, 시작 반경 300px, 조각 시간차 0.04s, 이징 power2.out을 적용해. 조각별 극좌표 반경과 각도를 최종 좌표까지 보간한다. paused 타임라인과 절대 시간으로 구동하고 Math.random 없이 같은 시점의 상태를 재현해.", "codex": "<파일>의 나선 조립 장면 레이어에 적용해. 지속 1.5s, 회전량 540deg, 시작 반경 300px, 조각 시간차 0.04s, 이징 power2.out을 사용하고 본문과 분리해. 0초, 0.4초, 1.2초 시점을 캡처해 시작 상태와 중간 변화 및 대상 가독성을 확인하고 같은 시점을 다시 seek해 결과가 같은지 검증해." }, "oneLinerEn": "Pieces spiral inward and settle into their final arrangement.", "conveysEn": "Communicates convergence and assembly.", "useWhenEn": [ "Assemble scattered pieces.", "Show separate parts becoming a single structure." ], "promptsEn": { "claude": "Implement Spiral Assembly on . Use duration 1.5s; rotation 540deg; initial radius 300px; piece stagger 0.04s; use power2.out easing. Drive the effect with a paused GSAP timeline and absolute time, and reproduce the same state on every seek without Math.random. Keep foreground text readable.", "codex": "Apply Spiral Assembly to the scene layer in . Use duration 1.5s; rotation 540deg; initial radius 300px; piece stagger 0.04s and power2.out easing. Capture at 0, 0.4, and 1.2 seconds to check the initial state, motion progression, and foreground readability; seek to each time again to verify identical output." }, "render": "planned", "files": {}, "no": "063" }, { "slug": "letter-spin-in", "family": "entrance", "ko": "글자 회전 입장", "en": "Letter Spin In", "aka": [ "Letter Spin and Roll", "글자 회전과 굴림" ], "oneLiner": "글자가 기울거나 회전한 상태에서 바른 방향으로 돌아와 정렬되는 등장", "conveys": "가볍고 장난스러운 제목 리듬. 글자마다 통통 튀는 생기가 있다", "purposes": [ "주목 끌기", "분위기" ], "media": [ "숏폼", "설명 영상" ], "level": "기본", "useWhen": [ "밝고 가벼운 톤의 제목이나 인트로", "유아·교육·커머스 숏폼의 타이틀" ], "goodExample": "\"안녕하세요\" 각 음절이 rotateZ 90도에서 0도로 0.7초 돌아 들어오고 시작 차는 0.035초다", "badExample": "음절마다 여러 바퀴를 돌려 1.5초가 걸리고 마지막 글자가 도착하기 전에 처음 글자가 흔들린다", "avoid": [ "진지한 톤의 제목에는 쓰지 않는다", "총 시간 1.2초를 넘지 않게 한다. 글자 수가 많으면 시작 차를 줄인다" ], "params": [ { "name": "글자당 시간", "default": "0.7s", "range": "0.5~0.9s", "note": "power3.out" }, { "name": "시작 차", "default": "0.035s", "range": "0.02~0.06s", "note": "왼쪽부터" }, { "name": "시작 회전", "default": "90deg", "range": "45~180deg", "note": "rotateZ" }, { "name": "시작 y 이동", "default": "24px", "range": "0~40px", "note": "아래에서 올라옴" } ], "ease": "power3.out", "snippet": "tl.from('.ch', {rotation:90, y:24, opacity:0, transformOrigin:'50% 50%', duration:0.7, ease:'power3.out', stagger:0.035}, 0.2);", "pairsWith": [ "letter-flip-3d", "char-stagger", "text-wave" ], "engines": { "hyperframes": "transform-origin을 글자 중앙으로 고정하고 inline-block으로 만들어야 회전이 된다. 단일 timeline stagger로 충분하다", "reelforge": "브리프에 시작 회전각, y 이동, 시작 차를 파라미터로 노출한다. 회전 방향을 글자마다 번갈아 하려면 홀짝 규칙으로 정한다", "scrolline": "진행률을 stagger에 매핑한다. 회전량이 큰 편이라 scrub에서는 회전각을 90도 이하로 제한한다" }, "prompts": { "claude": "<대상> 제목의 각 음절이 rotation 90도, y 24px 아래에서 0.7초 power3.out으로 바르게 돌아와 정렬되게 해줘. 음절 시작 차는 0.035초, transform-origin은 중앙, display는 inline-block. 전체는 1.2초 안에 끝내.", "codex": "<파일>의 제목에 letter spin in을 적용해. .ch inline-block, from rotation 90, y 24, opacity 0, duration 0.7, stagger 0.035, power3.out. 0.35초·0.7초·1.2초를 캡처해 회전각이 순서대로 줄어들고 마지막 프레임에서 rotation 0인지 확인해." }, "sources": [ { "title": "codrops/LetterEffects", "url": "https://github.com/codrops/LetterEffects", "license": "unknown" }, { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "jschr/textillate", "url": "https://github.com/jschr/textillate", "license": "MIT" } ], "runtime": "gsap", "variants": [ "측면 굴림", "무작위 각도 정렬", "전체 회전" ], "oneLinerEn": "Letters arrive tilted or spinning and rotate into their upright alignment.", "conveysEn": "A light, playful title rhythm with a little life in every character.", "useWhenEn": [ "Bright, light-toned titles and intros", "Titles for kids, education or commerce shorts" ], "promptsEn": { "claude": "Make each syllable of the title in rotate in from 90deg and 24px below, landing upright over 0.7s power3.out. Stagger syllables 0.035s, keep transform-origin at center and display inline-block. Finish within 1.2s.", "codex": "Apply letter spin in to the title in . .ch inline-block; from rotation 90, y 24, opacity 0; duration 0.7; stagger 0.035; power3.out. Capture at 0.35s, 0.7s and 1.2s and verify rotation decreases in order and reaches 0 in the last frame." }, "render": "planned", "files": {}, "no": "064" }, { "slug": "overwhelm-surround", "family": "emphasis", "ko": "알림 포위", "en": "Overwhelm Surround", "aka": [ "Overwhelm surround", "과부하 둘러싸기", "밀집 포위" ], "variants": [ "도착 가속", "중심 인물 포위", "카드 밀어내기" ], "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/overwhelm-surround/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/notification-pileup/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/slack-notification-ad/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/blueprints/overwhelm-surround.md", "license": "unknown" } ], "runtime": "gsap", "oneLiner": "주인공은 고정된 채 알림과 작업 카드가 더 빠르게 주변을 채운다.", "conveys": "과부하와 공간적 압박을 느끼게 한다.", "purposes": [ "분위기" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "업무 과부하 문제를 소개할 때", "해결책 등장 전 압박을 표현할 때" ], "goodExample": "중앙 인물 주변에 카드 12개가 점점 빠르게 도착한다", "badExample": "카드가 인물 얼굴과 핵심 문장을 완전히 덮는다", "avoid": [ "중심 인물과 핵심 문장의 안전 영역을 비워 둔다" ], "params": [ { "name": "카드 수", "default": "12", "range": "8~16", "note": "중앙 안전 영역 유지" }, { "name": "누적", "default": "2500ms", "range": "2000~4000ms", "note": "도착 스케줄 길이" }, { "name": "포위 정착", "default": "1200ms", "range": "800~1600ms", "note": "마지막 카드 정착" }, { "name": "도착 간격", "default": "600~150ms", "range": "600~150ms", "note": "시간표를 2500ms 안으로 정규화" } ], "ease": "power3.out", "snippet": "const cards = gsap.utils.toArray('.surround-card').slice(0,12);\nconst weights = cards.map((_,i)=>0.6-i*0.45/11);\nconst total = weights.reduce((a,b)=>a+b,0);\nlet at=0;\ncards.forEach((card,i)=>{\n const a=i*Math.PI/6;\n tl.fromTo(card,{x:Math.cos(a)*900,y:Math.sin(a)*600,opacity:0},{x:Math.cos(a)*600,y:Math.sin(a)*360,opacity:1,duration:1.2,ease:'power3.out'},at);\n at+=weights[i]/total*2.5;\n});", "pairsWith": [ "toast-stack", "layout-yield" ], "engines": { "hyperframes": "paused 타임라인에 알림 포위의 초기 상태와 종료 상태를 함께 기록하고 3.70초까지 seek로 재현한다.", "reelforge": "씬 워커 브리프에 알림 포위 대상 선택자와 카드 수 12, 누적 2500ms, 포위 정착 1200ms를 싣고 최종 상태 유지 시간을 지정한다.", "scrolline": "진행률 0~1을 3.70초 구간에 매핑하고 scrub에서는 스프링 대신 power3.out으로 위치를 계산한다." }, "prompts": { "claude": "<파일>의 <대상>에 알림 포위을 적용한다. 카드 12개를 중앙에서 가로 600px, 세로 360px 타원에 놓고 도착 간격 600ms에서 150ms의 비율을 누적 2500ms로 정규화한다. 초기 상태를 명시한 paused 타임라인으로 만들고 고정 좌표와 시간만 사용해 seek를 재현한다.", "codex": "<파일>에서 <대상>의 알림 포위 장면에 적용한다. 카드 12개를 중앙에서 가로 600px, 세로 360px 타원에 놓고 도착 간격 600ms에서 150ms의 비율을 누적 2500ms로 정규화한다. 0.93초·2.41초·3.90초 시점을 캡처해 시작 상태, 중간 관계, 최종 정착과 겹침 여부를 확인한다." }, "oneLinerEn": "Cards arrive increasingly quickly around a stationary focal subject.", "conveysEn": "Builds a sense of overload and spatial pressure.", "useWhenEn": [ "Introduce a workload problem.", "Build pressure before presenting a solution." ], "promptsEn": { "claude": "Apply Overwhelm Surround to in . Place twelve cards on a 600px by 360px elliptical orbit and normalize arrival gaps with a 600ms to 150ms ratio to a 2500ms schedule. Define the initial state and use a paused timeline with fixed coordinates and timings for repeatable seeking.", "codex": "Implement Overwhelm Surround in the scene for in . Place twelve cards on a 600px by 360px elliptical orbit and normalize arrival gaps with a 600ms to 150ms ratio to a 2500ms schedule. Capture at 0.93s, 2.41s, 3.90s to verify the initial state, intermediate relationships, final settling, and unwanted overlap." }, "render": "planned", "files": {}, "no": "065" }, { "slug": "threshold-pulse", "family": "emphasis", "ko": "임계값 경고 펄스", "en": "Threshold Pulse", "aka": [ "Threshold counter pulse", "임계값 숫자 경고" ], "oneLiner": "값이 한계에 가까워질수록 숫자가 진해지고 튀어 오른다.", "conveys": "남은 용량이나 위험 구간을 알린다.", "purposes": [ "피드백", "강조" ], "media": [ "설명 영상", "스크롤덱", "웹 UI" ], "level": "중급", "useWhen": [ "저장 공간 잔량이 20% 이하로 떨어질 때", "남은 시간이 경고 구간에 진입할 때" ], "goodExample": "잔량이 20%에 도달하면 숫자가 1.12배로 0.2초 커졌다가 원래 크기로 돌아온다.", "badExample": "임계값 밖에서도 계속 튀어 경고와 정상 상태가 구분되지 않는다.", "avoid": [ "색만으로 경고를 전달하지 않는다." ], "params": [ { "name": "임계값", "default": "20%", "range": "10~30%", "note": "잔량 기준으로 이하에서 경고한다." }, { "name": "최대 배율", "default": "1.12", "range": "1.06~1.16", "note": "숫자 중심을 기준으로 한다." }, { "name": "팝 시간", "default": "200ms", "range": "120~300ms", "note": "확대와 복귀에 각각 적용한다." }, { "name": "이징", "default": "power2.out", "range": "none | power2.out | power2.inOut", "note": "끝점을 넘지 않는 이징을 쓴다." } ], "ease": "power2.out", "snippet": "const tl=gsap.timeline({paused:true});\nconst remaining=20, threshold=20;\nif(remaining<=threshold){\n tl.fromTo('.counter',{scale:1,color:'#334155'},{scale:1.12,color:'#b91c1c',duration:0.2,ease:'power2.out'});\n tl.to('.counter',{scale:1,duration:0.2,ease:'power2.inOut'});\n}", "pairsWith": [ "count-up", "outline-pulse" ], "engines": { "hyperframes": "paused 타임라인 하나에 임계값 경고 펄스 상태를 넣고 seek(t)로 0.4초 구간을 재현한다. 현재 시간에서 도형을 계산해 프레임 누적을 피한다.", "reelforge": "씬 워커 브리프에 임계값 20%, 최대 배율 1.12, 팝 시간 200ms, 이징 power2.out를 싣고 입력 자료와 초기 도형 좌표를 고정한다. 잔량이 20%에 도달하면 숫자가 1.12배로 0.2초 커졌다가 원래 크기로 돌아온다.", "scrolline": "진행률 0~1을 0.4초 구간에 매핑해 같은 타임라인을 scrub한다. 시간량은 none, 시각 전환은 power2.out을 쓰고 스프링 대신 끝점을 넘지 않는 ease-out을 쓴다." }, "prompts": { "claude": "<파일>의 <대상>에 임계값 경고 펄스를 구현해. 값이 한계에 가까워질수록 숫자가 진해지고 튀어 오른다. 임계값 20%, 최대 배율 1.12, 팝 시간 200ms, 이징 power2.out를 적용하고 GSAP 코어의 paused 타임라인 하나로 seek 가능하게 작성해. 색만으로 경고를 전달하지 않는다.", "codex": "<파일>의 <대상> 장면에 임계값 경고 펄스를 적용해. 임계값 20%, 최대 배율 1.12, 팝 시간 200ms, 이징 power2.out를 사용하고 다음 동작을 구현해: 수치의 임계 근접도를 color와 스프링 초기 속도에 매핑한다. 플러그인과 Math.random 없이 작성하고 0.1초·0.24초·0.4초를 캡처해 초기 상태, 중간 변화, 최종 상태가 연속적이며 다음 예시와 맞는지 확인해: 잔량이 20%에 도달하면 숫자가 1.12배로 0.2초 커졌다가 원래 크기로 돌아온다." }, "sources": [ { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-characters-remaining", "license": "unknown" } ], "runtime": "gsap", "variants": [ "글자 수", "잔량", "시간", "한도" ], "oneLinerEn": "A counter becomes more prominent and briefly scales up when it reaches a warning threshold.", "conveysEn": "Signals limited remaining capacity or an approaching limit.", "useWhenEn": [ "Warn when remaining storage falls below 20 percent.", "Emphasize a countdown entering its final warning range." ], "promptsEn": { "claude": "Implement Threshold Pulse for in . A counter becomes more prominent and briefly scales up when it reaches a warning threshold. Use warning threshold: 20%; peak scale: 1.12; pulse duration: 200ms; easing: power2.out in a single paused GSAP core timeline that supports seeking. Use a warning label as well as color.", "codex": "Apply Threshold Pulse to the scene in using warning threshold: 20%; peak scale: 1.12; pulse duration: 200ms; easing: power2.out. A counter becomes more prominent and briefly scales up when it reaches a warning threshold. Use prepared data or geometry with GSAP core, no plugins, and no Math.random; capture at 0.1, 0.24, 0.4 seconds to verify continuous intermediate geometry, correct endpoints, and identical output after repeated seeks. Use a warning label as well as color." }, "render": "planned", "files": {}, "no": "066" }, { "slug": "border-reveal", "family": "emphasis", "ko": "테두리 리빌", "en": "Border Reveal", "aka": [ "테두리 드러내기" ], "oneLiner": "요소의 테두리 선이 바깥에서 안으로 들어오며 윤곽을 드러내는 표시", "conveys": "선택되었거나 경계가 중요한 대상을 표시한다. 채움 없이 가볍게 지정한다", "purposes": [ "강조", "피드백" ], "media": [ "웹 UI", "제품 시연", "설명 영상" ], "level": "기본", "useWhen": [ "폼 필드·카드가 선택 상태로 바뀔 때", "화면 요소의 경계를 짚어 설명할 때" ], "goodExample": "입력 카드 주위 8px 바깥에 2px 선이 0.35초 동안 카드 경계로 좁혀 들어오며 선명해진다", "badExample": "선 두께 6px 이상으로 요소보다 테두리가 더 튀거나 색이 배경과 대비되지 않는다", "avoid": [ "테두리와 배경의 대비비 3:1 이상", "두께 4px 초과 금지", "동시에 강조하는 요소는 2개 이하" ], "params": [ { "name": "두께", "default": "2px", "range": "1~4px", "note": "1920x1080 기준" }, { "name": "시작 간격", "default": "8px", "range": "4~12px", "note": "경계 바깥 오프셋" }, { "name": "길이", "default": "0.35s", "range": "0.25~0.5s", "note": "이동과 페이드 합산" }, { "name": "이징", "default": "power2.out", "range": "power2~power3.out", "note": "도착 직전 감속" } ], "ease": "power2.out", "snippet": "tl.fromTo('.ring', { outlineOffset: 8, outlineColor: 'rgba(0,120,255,0)' }, { outlineOffset: 0, outlineColor: 'rgba(0,120,255,1)', duration: 0.35, ease: 'power2.out' }, 0.4);", "pairsWith": [ "circumscribe", "spotlight", "hotspot-pulse" ], "engines": { "hyperframes": "outline은 레이아웃을 바꾸지 않으므로 그대로 tween한다. 색은 rgba로 알파를 같이 올려 seek에서도 안전하다", "reelforge": "브리프에 두께·시작 간격·색 토큰을 실어 브랜드 색으로 바꾼다", "scrolline": "scrub에서는 outlineOffset 8→0을 진행률 0~0.4에 매핑하고 이후 유지한다" }, "prompts": { "claude": "GSAP으로 <대상> 요소에 테두리 리빌을 넣어줘. 0.4초에 outline 2px, outlineOffset 8px, 알파 0에서 시작해 0.35초 동안 outlineOffset 0, 알파 1로 power2.out. 색은 <색상>이고 배경과 대비 3:1 이상이어야 해. 레이아웃이 밀리지 않게 outline을 써.", "codex": "<파일>의 <대상>에 border-reveal을 적용해. outline 2px solid, fromTo outlineOffset 8→0, 알파 0→1, 0.35s, power2.out, position 0.4. 0.4초·0.6초·0.9초를 캡처해 선이 안쪽으로 들어오는지, 최종에 레이아웃 이동이 0px인지 확인해." }, "sources": [ { "title": "IanLunn/Hover", "url": "https://github.com/IanLunn/Hover", "license": "MIT personal/open-source + paid commercial" } ], "runtime": "css", "variants": [ "border-fade", "outline-in", "outline-out", "reveal", "trim" ], "oneLinerEn": "A border line closes in from outside the element to reveal its outline.", "conveysEn": "Marks a selected element or an important boundary without filling it.", "useWhenEn": [ "When a form field or card becomes selected", "When pointing at the edge of an on-screen element" ], "promptsEn": { "claude": "Add a border reveal to with GSAP. At 0.4s start with a 2px outline, outlineOffset 8px, alpha 0 and animate to offset 0, alpha 1 over 0.35s with power2.out. Use with at least 3:1 contrast against the background, and use outline so layout does not shift.", "codex": "Apply border-reveal to in . Use outline 2px solid, fromTo outlineOffset 8 to 0 and alpha 0 to 1, 0.35s, power2.out, position 0.4. Capture at 0.4s, 0.6s and 0.9s to check the line moves inward and that layout shift is 0px at the end." }, "render": "planned", "files": {}, "no": "067" }, { "slug": "jello", "family": "emphasis", "ko": "젤로", "en": "Jello", "aka": [ "젤리 전단 진동", "Text Elastic Skew", "텍스트 탄성 비틀림" ], "oneLiner": "가로와 세로로 번갈아 비틀리며 감쇠하는 젤리 같은 진동", "conveys": "부드럽고 말랑한 물성. 눌렀을 때 탄성 있게 반응한다는 느낌", "purposes": [ "분위기", "주목 끌기" ], "media": [ "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "젤리·말랑한 캐릭터·소프트 UI 카드를 눌렀을 때", "브랜드 톤이 장난스러운 앱 소개 영상" ], "goodExample": "카드를 누르면 skewX 12deg, -6deg, 3deg, 0 순서로 0.9초 안에 감쇠하며 멈춘다", "badExample": "skew 25도 이상으로 글자가 찌그러지거나, 진동이 감쇠 없이 계속된다", "avoid": [ "skew 15도 초과 금지", "텍스트가 많은 카드에는 약하게(6도 이하)", "1초 안에 정지" ], "params": [ { "name": "skew 진폭", "default": "12deg", "range": "6~15deg", "note": "첫 번째 최대 각" }, { "name": "감쇠비", "default": "0.5배", "range": "0.4~0.6", "note": "매 왕복 진폭 곱" }, { "name": "단계 수", "default": "5", "range": "4~6", "note": "마지막은 0" }, { "name": "총 길이", "default": "0.9s", "range": "0.6~1.1s", "note": "감쇠 포함" } ], "ease": "sine.inOut", "snippet": "const k = [12, -6, 3, -1.5, 0];\nk.forEach((a, i) => tl.to('.card', { skewX: a, skewY: -a / 2, duration: 0.18, ease: 'sine.inOut' }, 0.3 + i * 0.18));", "pairsWith": [ "wobble", "squash-stretch", "spring-overshoot" ], "engines": { "hyperframes": "skewX/skewY 값 배열을 절대 시각에 배치하고 마지막을 0으로 닫는다. transform-origin 50% 50% 고정", "reelforge": "브리프에 진폭·감쇠비·단계 수를 넣고, 텍스트 카드는 진폭을 6도로 낮춘다", "scrolline": "scrub에서는 진폭 배열을 진행률에 나눠 매핑하고 스프링 대신 sine.inOut으로 닫는다" }, "prompts": { "claude": "GSAP으로 <대상> 카드에 젤로 효과를 넣어줘. 0.3초부터 0.18초 간격으로 skewX가 12, -6, 3, -1.5, 0도가 되고 skewY는 그 값의 -0.5배야. 이징은 sine.inOut, 총 0.9초 안에 정지. 글자가 있으면 진폭을 6도로 낮춰. paused 타임라인 하나로 만들어.", "codex": "<파일>의 <대상>에 jello를 적용해. k=[12,-6,3,-1.5,0]를 순회해 skewX=a, skewY=-a/2, duration 0.18, position 0.3+i*0.18로 tween. 0.4초·0.7초·1.3초를 캡처해 비틀림 방향이 번갈아 바뀌고 1.3초에 skew 0인지 확인해." }, "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" } ], "runtime": "css", "variants": [ "jello", "jello-horizontal", "jello-vertical", "JellyRadio", "jello-diagonal-1", "jello-diagonal-2", "가로 비틀림", "복합 비틀림" ], "oneLinerEn": "A damped alternating shear that makes an element wobble like jelly.", "conveysEn": "A soft, squishy material that responds elastically when touched.", "useWhenEn": [ "When a jelly-like character or soft UI card is pressed", "In app intro videos with a playful brand tone" ], "promptsEn": { "claude": "Add a jello to the card with GSAP. From 0.3s, every 0.18s set skewX to 12, -6, 3, -1.5, 0 degrees with skewY at -0.5x of that. Ease sine.inOut and stop within 0.9s. Drop the amplitude to 6 degrees if the card has text. One paused timeline.", "codex": "Apply jello to in . Loop k=[12,-6,3,-1.5,0] with skewX=a, skewY=-a/2, duration 0.18, position 0.3 + i*0.18. Capture at 0.4s, 0.7s and 1.3s to check the shear alternates direction and skew is 0 at 1.3s." }, "render": "planned", "files": {}, "no": "068" }, { "slug": "pulse", "family": "emphasis", "ko": "펄스", "en": "Pulse", "aka": [ "크기 맥동", "Heartbeat", "심장 박동", "Scale and color indication", "크기와 색 맥동 강조", "Indicate" ], "oneLiner": "요소가 제자리에서 살짝 커졌다 원래 크기로 돌아오는 맥동", "conveys": "대상이 살아 있고 눌러 볼 수 있다는 신호. 위치를 옮기지 않고 시선만 끌어온다", "purposes": [ "주목 끌기", "강조" ], "media": [ "웹 UI", "제품 시연", "숏폼" ], "level": "기본", "useWhen": [ "버튼·배지처럼 지금 눌러야 할 요소에 시선을 끌 때", "알림 점이나 녹화 표시처럼 활성 상태를 알릴 때", "설명 도중 특정 아이콘을 한 번 짚을 때" ], "goodExample": "CTA 버튼이 1초 주기로 scale 1.05까지 커졌다 돌아오며 세 번 뛰고 멈춘다", "badExample": "scale 1.3 이상으로 커져 주변 글자를 가리거나, 화면 전체 요소가 동시에 무한 반복한다", "avoid": [ "scale 1.08 초과 금지(주변 요소와 겹침)", "한 화면에 동시에 맥동하는 요소는 1개로 제한", "반복은 3회 이내로 끝내고 정지한다" ], "params": [ { "name": "주기", "default": "1.0s", "range": "0.6~1.4s", "note": "커짐과 복귀를 합친 한 번의 길이" }, { "name": "최대 배율", "default": "1.05", "range": "1.03~1.08", "note": "1.08을 넘기면 경계가 겹침" }, { "name": "반복 횟수", "default": "3회", "range": "2~4회", "note": "무한 반복은 데코용에만" }, { "name": "이징", "default": "sine.inOut", "range": "sine~power2.inOut", "note": "왕복이 매끄럽게 이어지는 곡선" } ], "ease": "sine.inOut", "snippet": "tl.to('.cta', { scale: 1.05, duration: 0.5, ease: 'sine.inOut', yoyo: true, repeat: 5 }, 0.4);", "pairsWith": [ "bounce-attention", "hotspot-pulse", "threshold-pulse" ], "engines": { "hyperframes": "repeat는 유한 값으로 두고 paused 타임라인 하나에 넣는다. yoyo 왕복이 끝나는 시각(0.4+0.5×6=3.4s)을 클립 길이 안에 둔다", "reelforge": "씬 워커 브리프에 대상 셀렉터·주기·최대 배율·반복 횟수를 싣고 무한 반복은 금지한다", "scrolline": "scrub에서는 반복 대신 진행률 0.3~0.5 구간에 한 번만 맥동시킨다. 스프링 없이 sine.inOut" }, "prompts": { "claude": "GSAP으로 <대상> 버튼에 펄스를 넣어줘. 0.4초에 시작해 scale 1에서 1.05까지 0.5초에 커지고 같은 시간에 돌아오는 왕복을 3번 반복해. 이징은 sine.inOut, 반복 뒤에는 원래 크기로 정지해. transform만 쓰고 paused 타임라인에 넣어 seek가 되게 해.", "codex": "<파일>의 <대상>에 pulse를 적용해. tween은 scale 1.05, duration 0.5, yoyo true, repeat 5, ease sine.inOut, position 0.4로 건다. 무한 반복과 Math.random은 쓰지 않는다. 0.4초·0.9초·3.6초 시점을 캡처해 시작 크기, 최대 크기(1.05), 종료 후 크기 1.0을 각각 확인해." }, "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/indication.py", "license": "MIT" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "IanLunn/Hover", "url": "https://github.com/IanLunn/Hover", "license": "MIT personal/open-source + paid commercial" }, { "title": "loadingio/css-spinner", "url": "https://github.com/loadingio/css-spinner", "license": "CC0 loaders (README; root LICENSE absent)" } ], "runtime": "css", "variants": [ "단일 맥동", "두 번 심장 박동", "색 동반 맥동" ], "oneLinerEn": "A short scale beat where an element grows slightly and returns to its size in place.", "conveysEn": "Signals the element is alive and clickable. It draws the eye without moving anything.", "useWhenEn": [ "When pulling attention to a button or badge that should be pressed now", "When showing an active state such as a notification dot or record indicator", "When pointing at one icon during an explanation" ], "promptsEn": { "claude": "Add a pulse to with GSAP. Start at 0.4s, scale from 1 to 1.05 over 0.5s and back, repeated 3 times. Ease sine.inOut, then hold at the original size. Use transforms only, on a paused timeline so it can be seeked.", "codex": "Apply pulse to in . Tween scale 1.05, duration 0.5, yoyo true, repeat 5, ease sine.inOut, position 0.4. No infinite repeat and no Math.random. Capture at 0.4s, 0.9s and 3.6s and check the start size, the peak size (1.05) and that the final size is 1.0." }, "render": "done", "files": { "html": "effects/pulse/index.html", "mp4": "effects/pulse/clip.mp4", "gif": "effects/pulse/preview.gif", "poster": "effects/pulse/poster.jpg" }, "no": "069" }, { "slug": "rotate-scale", "family": "emphasis", "ko": "회전 스케일", "en": "Rotate Scale", "aka": [ "회전 신축" ], "oneLiner": "회전하면서 커졌다 원래 크기로 돌아오는 강조 동작", "conveys": "한 대상의 변화를 풍부하게 강조한다. 등장이나 상태 전환의 에너지를 더한다", "purposes": [ "주목 끌기", "전환" ], "media": [ "숏폼", "설명 영상", "웹 UI" ], "level": "기본", "useWhen": [ "로고·아이콘이 등장하며 힘 있게 자리 잡을 때", "상태 전환(잠금 해제, 완료)을 아이콘으로 보여 줄 때" ], "goodExample": "자물쇠 아이콘이 360도 돌며 scale 0.7에서 1.2를 거쳐 1로 정착한다", "badExample": "720도 이상 여러 바퀴 돌아 방향을 잃거나, 텍스트를 함께 회전시켜 읽을 수 없다", "avoid": [ "회전은 360도 이하", "텍스트가 든 요소는 회전 대신 scale만 사용", "이동과 동시에 걸지 않는다" ], "params": [ { "name": "길이", "default": "0.7s", "range": "0.5~1.0s", "note": "전체 동작" }, { "name": "회전", "default": "360deg", "range": "180~360deg", "note": "한 방향 고정" }, { "name": "배율 경로", "default": "0.7→1.2→1", "range": "0.6~1.3", "note": "정점에서 오버슈트" }, { "name": "이징", "default": "power3.out", "range": "power2~power4.out", "note": "끝에서 부드럽게 정지" } ], "ease": "power3.out", "snippet": "tl.fromTo('.icon', { rotation: -360, scale: 0.7 }, { rotation: 0, scale: 1.2, duration: 0.45, ease: 'power3.out' }, 0.3)\n .to('.icon', { scale: 1, duration: 0.25, ease: 'power2.inOut' });", "pairsWith": [ "spring-overshoot", "scale-pop", "squash-stretch" ], "engines": { "hyperframes": "fromTo로 시작 상태를 명시하고 paused 타임라인에서 seek해도 초기값이 유지되게 한다", "reelforge": "브리프에 회전 각·배율 경로·소요 시간을 넣는다. 아이콘 SVG 중심점을 정렬해 도는 중심이 어긋나지 않게 한다", "scrolline": "scrub에서는 진행률 0~0.6에 회전과 확대, 0.6~1에 복귀를 나눠 ease-out으로 매핑한다" }, "prompts": { "claude": "GSAP으로 <대상> 아이콘에 회전 스케일 효과를 넣어줘. 0.3초부터 0.45초 동안 rotation -360에서 0, scale 0.7에서 1.2로 power3.out, 이어서 0.25초 동안 scale 1.2에서 1로 power2.inOut. 중심점은 50% 50%, paused 타임라인 하나로 만들어.", "codex": "<파일>의 <대상>에 rotate-scale을 적용해. fromTo(rotation -360→0, scale 0.7→1.2, 0.45s, power3.out, position 0.3) 뒤 to(scale 1, 0.25s). 0.3초·0.55초·1.0초를 캡처해 시작 기울기, 최대 배율 1.2, 종료 배율 1.0을 확인해." }, "sources": [ { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "IanLunn/Hover", "url": "https://github.com/IanLunn/Hover", "license": "MIT personal/open-source + paid commercial" } ], "runtime": "css", "variants": [ "rotate-scale-up", "rotate-scale-down", "수평축", "수직축", "대각축", "rotate-scale-up-hor", "rotate-scale-down-hor", "rotate-scale-up-ver", "rotate-scale-down-ver", "rotate-scale-up-diag-1", "rotate-scale-down-diag-1", "rotate-scale-up-diag-2", "rotate-scale-down-diag-2" ], "oneLinerEn": "An element spins while growing, then settles back to its original size.", "conveysEn": "Adds energy to one object's entrance or state change.", "useWhenEn": [ "When a logo or icon should land with force", "When showing a state change such as unlock or done with an icon" ], "promptsEn": { "claude": "Add a rotate-scale to the icon with GSAP. From 0.3s for 0.45s, rotation -360 to 0 and scale 0.7 to 1.2 with power3.out, then scale 1.2 to 1 over 0.25s with power2.inOut. Pivot at 50% 50% and use one paused timeline.", "codex": "Apply rotate-scale to in . fromTo (rotation -360 to 0, scale 0.7 to 1.2, 0.45s, power3.out, position 0.3), then to scale 1 over 0.25s. Capture at 0.3s, 0.55s and 1.0s to check the start tilt, the peak scale 1.2 and the final scale 1.0." }, "render": "planned", "files": {}, "no": "070" }, { "slug": "tada", "family": "emphasis", "ko": "타다", "en": "Tada", "aka": [ "축소 뒤 환호 흔들기" ], "oneLiner": "움츠렸다 커지면서 좌우로 흔들리고 제자리로 돌아오는 축하 동작", "conveys": "성과나 완료를 알리는 축하의 신호. 짧고 명랑하게 끝난다", "purposes": [ "주목 끌기", "강조" ], "media": [ "숏폼", "웹 UI", "설명 영상" ], "level": "기본", "useWhen": [ "목표 달성·결제 완료·업로드 성공을 알릴 때", "배지나 트로피 아이콘이 등장한 직후 한 번 더 강조할 때" ], "goodExample": "완료 배지가 scale 0.9로 움츠렸다가 1.1로 커지며 ±3도 네 번 흔들리고 1로 돌아온다", "badExample": "회전 10도 이상으로 흔들리거나 여러 요소가 동시에 tada를 해서 화면이 소란스럽다", "avoid": [ "회전 ±5도 초과 금지", "한 장면에 1회만 사용", "오류·경고 상황에는 쓰지 않는다" ], "params": [ { "name": "총 길이", "default": "1.0s", "range": "0.7~1.2s", "note": "준비와 흔들림 전체" }, { "name": "준비 배율", "default": "0.9", "range": "0.88~0.95", "note": "움츠러드는 깊이" }, { "name": "절정 배율", "default": "1.1", "range": "1.06~1.15", "note": "흔들리는 동안 유지" }, { "name": "회전 진폭", "default": "±3deg", "range": "2~5deg", "note": "4회 왕복, 마지막은 0으로" } ], "ease": "power2.out", "snippet": "tl.to('.badge', { scale: 0.9, duration: 0.1, ease: 'power2.out' }, 0.3)\n .to('.badge', { scale: 1.1, rotation: 3, duration: 0.1 })\n .to('.badge', { rotation: -3, duration: 0.1 }).to('.badge', { rotation: 3, duration: 0.1 })\n .to('.badge', { rotation: -3, duration: 0.1 }).to('.badge', { scale: 1, rotation: 0, duration: 0.3, ease: 'power3.out' });", "pairsWith": [ "scale-pop", "spring-overshoot", "bounce-attention" ], "engines": { "hyperframes": "체인 tween을 한 paused 타임라인에 이어 붙이고 마지막에 rotation 0, scale 1로 명시 복귀시킨다", "reelforge": "씬 워커 브리프에 대상, 절정 배율, 진폭, 흔들림 횟수를 파라미터로 넣는다. 기본값에서 벗어나면 회전을 줄인다", "scrolline": "scrub에서는 스프링 대신 진행률 0.4~0.6 구간의 짧은 ease-out 흔들림만 쓰고 앞뒤는 정지시킨다" }, "prompts": { "claude": "GSAP으로 <대상> 배지에 tada를 넣어줘. 0.3초에 scale 0.9로 0.1초 움츠렸다가 1.1로 커지며 rotation +3, -3, +3, -3도를 0.1초씩 오가고 마지막 0.3초에 scale 1, rotation 0으로 돌아와. 회전은 5도를 넘기지 마. paused 타임라인 하나에 넣어.", "codex": "<파일>의 <대상>에 tada를 적용해. 0.3s scale 0.9(0.1s) → scale 1.1 + rotation ±3 네 번(각 0.1s) → scale 1, rotation 0(0.3s, power3.out). 시드 없는 난수 금지. 0.35초·0.8초·1.5초 시점을 캡처해 움츠림, 절정 기울기, 종료 후 정렬 상태를 확인해." }, "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" } ], "runtime": "css", "variants": [ "tada" ], "oneLinerEn": "A celebratory shrink-then-grow with a small left-right shake before settling.", "conveysEn": "Announces an achievement or completion. It is short and cheerful and ends cleanly.", "useWhenEn": [ "When signalling a goal reached, a payment done or an upload finished", "When stressing a badge or trophy icon right after it appears" ], "promptsEn": { "claude": "Add a tada to the badge with GSAP. At 0.3s scale to 0.9 over 0.1s, then to 1.1 while rotating +3, -3, +3, -3 degrees at 0.1s each, and return to scale 1, rotation 0 over 0.3s. Never exceed 5 degrees. Use one paused timeline.", "codex": "Apply tada to in . 0.3s scale 0.9 (0.1s), then scale 1.1 with rotation +/-3 four times (0.1s each), then scale 1 and rotation 0 (0.3s, power3.out). No random values. Capture at 0.35s, 0.8s and 1.5s to check the squash, the peak tilt and the final alignment." }, "render": "planned", "files": {}, "no": "071" }, { "slug": "wobble", "family": "emphasis", "ko": "워블", "en": "Wobble", "aka": [ "큰 좌우 비틀림" ], "oneLiner": "요소가 좌우로 넓게 밀리며 기울다가 진폭을 줄이며 정착하는 흔들림", "conveys": "불안정함, 익살, 또는 입력이 거부된 느낌. 감쇠하며 멈추는 점이 핵심이다", "purposes": [ "분위기", "주목 끌기" ], "media": [ "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "귀여운 캐릭터·스티커가 반응할 때", "잘못된 입력이 가볍게 튕겨 나오는 순간을 보여 줄 때", "가볍고 장난스러운 톤의 숏폼 강조" ], "goodExample": "스티커가 x -25%→+20%→-15%→+10%→-5%→0으로 진폭을 줄이며 5도씩 기울고 정착한다", "badExample": "진폭이 줄지 않고 같은 폭으로 계속 흔들리거나, 텍스트 본문에 적용해 읽을 수 없다", "avoid": [ "진폭이 매번 60~70%로 줄어야 한다", "본문·표에는 사용 금지", "1.2초 안에 정지" ], "params": [ { "name": "이동 폭", "default": "25%", "range": "10~30%", "note": "요소 너비 기준 첫 번째 진폭" }, { "name": "회전", "default": "5deg", "range": "3~8deg", "note": "이동 방향과 반대로 기움" }, { "name": "반동 횟수", "default": "5회", "range": "3~6회", "note": "마지막은 0" }, { "name": "총 길이", "default": "1.0s", "range": "0.7~1.3s", "note": "감쇠 포함" } ], "ease": "power2.out", "snippet": "const amp = [-0.25, 0.2, -0.15, 0.1, -0.05, 0];\namp.forEach((a, i) => tl.to('.sticker', { xPercent: a * 100, rotation: -a * 20, duration: 0.17, ease: 'power2.out' }, 0.3 + i * 0.17));", "pairsWith": [ "jello", "error-shake", "follow-through" ], "engines": { "hyperframes": "진폭 배열을 코드에 고정해 결정론으로 만든다. 각 tween을 절대 시각에 배치해 seek 시 순서가 어긋나지 않게 한다", "reelforge": "브리프에 이동 폭·회전·횟수를 넣고 중심점은 transform-origin 50% 100%로 지정한다", "scrolline": "scrub에서는 감쇠 진폭 배열을 진행률 0~1에 나눠 매핑하고 정착 구간은 ease-out으로 닫는다" }, "prompts": { "claude": "GSAP으로 <대상>에 wobble을 넣어줘. 0.3초부터 0.17초 간격으로 xPercent가 -25, +20, -15, +10, -5, 0으로 줄어들고 rotation은 이동 반대 방향으로 진폭의 0.2배(최대 5도)로 기울어. 이징은 power2.out이고 배열은 코드에 고정해. paused 타임라인으로 만들어.", "codex": "<파일>의 <대상>에 wobble을 적용해. amp 배열 [-0.25,0.2,-0.15,0.1,-0.05,0]를 forEach로 돌려 xPercent=a*100, rotation=-a*20, duration 0.17, position 0.3+i*0.17로 tween을 건다. 0.4초·0.9초·1.5초를 캡처해 진폭이 줄어드는지, 1.4초 이후 x=0인지 확인해." }, "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "IanLunn/Hover", "url": "https://github.com/IanLunn/Hover", "license": "MIT personal/open-source + paid commercial" } ], "runtime": "css", "variants": [ "wobble", "wobble-horizontal", "wobble-vertical", "모서리 기준", "wobble-hor-bottom", "wobble-hor-top", "wobble-ver-left", "wobble-ver-right", "wobble-bottom", "wobble-skew", "wobble-to-bottom-right", "wobble-to-top-right", "wobble-top", "icon-wobble-horizontal", "icon-wobble-vertical" ], "oneLinerEn": "A wide left-right sway with tilt that shrinks in amplitude until it settles.", "conveysEn": "Reads as instability, playfulness or a soft rejection. Decay to rest is the key.", "useWhenEn": [ "When a cute character or sticker reacts", "When a bad input should bounce back lightly", "When adding a playful emphasis in short-form video" ], "promptsEn": { "claude": "Add a wobble to with GSAP. From 0.3s, every 0.17s set xPercent to -25, 20, -15, 10, -5, 0 and rotate opposite to the shift by 0.2x the amplitude (max 5 degrees). Ease power2.out, keep the array fixed in code, and use a paused timeline.", "codex": "Apply wobble to in . Loop the array [-0.25,0.2,-0.15,0.1,-0.05,0] with xPercent = a*100, rotation = -a*20, duration 0.17, position 0.3 + i*0.17. Capture at 0.4s, 0.9s and 1.5s to confirm the amplitude decays and x is 0 after 1.4s." }, "render": "planned", "files": {}, "no": "072" }, { "slug": "audio-reactive-pulse", "family": "emphasis", "ko": "오디오 반응 펄스", "en": "Audio Reactive Pulse", "aka": [ "Audio reactive pulse", "Audio Reactive Shape Pulse", "오디오 반응 도형 맥박" ], "oneLiner": "소리의 저음이나 고음 세기에 맞춰 도형의 크기와 빛의 세기가 함께 변한다", "conveys": "음악과 화면이 하나의 사건처럼 느껴진다. 소리를 눈으로 볼 수 있게 만든다", "purposes": [ "강조", "분위기" ], "media": [ "숏폼", "설명 영상", "웹 UI" ], "level": "중급", "useWhen": [ "뮤직 비디오나 로고 인트로에서 소리에 반응하는 요소가 필요할 때", "배경 글로우가 음악과 함께 호흡하게 할 때" ], "goodExample": "저음 밴드 값에 따라 로고가 최대 4%만 커지고 글로우가 30% 이하로 밝아진다. 30fps로 미리 뽑은 16밴드 배열을 사용한다", "badExample": "scale이 20% 이상 출렁여 로고가 튀고 글로우가 화면을 덮는다. 재생 중에 오디오를 실시간 분석해 seek가 안 된다", "avoid": [ "scale 변화 6% 초과 금지", "실시간 분석 금지(미리 뽑은 배열만)" ], "params": [ { "name": "프레임율", "default": "30fps", "range": "24~60fps", "note": "배열 샘플 간격 33ms" }, { "name": "주파수 밴드", "default": "16", "range": "8~32", "note": "저음은 밴드 0~2" }, { "name": "scale 변화", "default": "4% 이하", "range": "2~6%", "note": "1+amp*0.04" }, { "name": "glow 변화", "default": "30% 이하", "range": "15~40%", "note": "opacity로" } ], "ease": "none", "snippet": "const amp = bassEnvelope; // [0..1] 배열, 30fps로 사전 추출\ntl.to({}, { duration: amp.length / 30, ease: 'none', onUpdate() {\n const a = amp[Math.min(amp.length - 1, Math.floor(this.time() * 30))];\n gsap.set('.logo', { scale: 1 + a * 0.04 }); gsap.set('.glow', { opacity: 0.4 + a * 0.3 }); } }, 0);", "pairsWith": [ "beat-sync", "bloom-pulse", "breathing-loop" ], "engines": { "hyperframes": "음량 배열은 오프라인에서 ffmpeg 등으로 미리 뽑아 JSON으로 넣는다. onUpdate에서 time()으로 인덱싱하면 seek도 결정론적이다", "reelforge": "씬 워커 브리프에 음악 파일, 배열 경로, 밴드 번호, scale 4%, glow 30%를 싣는다", "scrolline": "음악이 없으므로 amp 대신 스크롤 속도나 진행률의 사인파를 입력으로 쓰거나 이 효과는 생략한다" }, "prompts": { "claude": "<대상> 로고가 저음에 반응하도록 만들어줘. 30fps로 미리 뽑아 둔 bass.json 배열(0~1)을 읽어 scale을 1+amp*0.04, 글로우 opacity를 0.4+amp*0.3으로 매 프레임 설정해. 재생 중 오디오 분석은 쓰지 말고 타임라인 time()으로 배열을 인덱싱해줘.", "codex": "<파일>에 audio reactive pulse를 구현해. 입력은 bass.json(30fps, 0~1). scale = 1+amp*0.04, glow opacity = 0.4+amp*0.3. onUpdate에서 index = floor(time*30). 킥이 있는 시점 두 곳과 무음 시점 한 곳을 캡처해 킥에서만 scale이 커지는지, 최대 scale이 1.04를 넘지 않는지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/beat-accent/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://github.com/heygen-com/hyperframes/blob/HEAD/skills/hyperframes-animation/techniques.md", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/beat-pulse-background/registry-item.json", "license": "Apache-2.0" }, { "title": "remotion-dev/skills", "url": "https://github.com/remotion-dev/skills/blob/HEAD/skills/remotion-best-practices/remotion-markup/audio-visualization.md", "license": "unknown" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/techniques.md", "license": "unknown" } ], "runtime": "gsap", "variants": [ "저음 맥박", "고음 광택", "배경 호흡" ], "oneLinerEn": "Shape size and glow intensity follow the low or high frequency strength of the sound.", "conveysEn": "Music and picture feel like one event, making sound visible.", "useWhenEn": [ "Need sound-reactive elements in a music video or logo intro.", "Let a background glow breathe with the music." ], "promptsEn": { "claude": "Make the logo in react to bass. Read a pre-extracted bass.json (30fps, values 0 to 1) and set scale to 1+amp*0.04 and glow opacity to 0.4+amp*0.3 every frame. Do not analyze audio at playback; index the array with timeline time().", "codex": "Implement audio reactive pulse in . Input is bass.json (30fps, 0 to 1). scale = 1+amp*0.04, glow opacity = 0.4+amp*0.3, index = floor(time*30) in onUpdate. Capture two kick moments and one silent moment to verify scale grows only on kicks and never exceeds 1.04." }, "render": "planned", "files": {}, "no": "073" }, { "slug": "blink", "family": "emphasis", "ko": "점멸", "en": "Blink", "aka": [ "Flash and Blink", "점멸 강조", "Visibility blink", "가시성 점멸" ], "oneLiner": "요소의 밝기나 투명도가 켜지고 꺼지며 반복된다", "conveys": "경고, 신호 존재, 진행 중 상태를 알린다. 가장 단순한 주의 신호다", "purposes": [ "주목 끌기", "피드백" ], "media": [ "웹 UI", "제품 시연", "숏폼" ], "level": "기본", "useWhen": [ "녹화 중, 라이브, 경고 표시가 필요한 작은 인디케이터에 쓸 때", "커서나 입력 위치를 알릴 때" ], "goodExample": "1000ms 동안 opacity가 1에서 0으로 내려갔다 돌아오는 점멸을 2번 한다. 전환은 부드러운 steps 없이 sine", "badExample": "초당 10회 이상 깜빡여 눈이 아프고 광과민성 우려가 있다. 본문 텍스트 전체를 깜빡인다", "avoid": [ "초당 3회 초과 금지", "본문 텍스트에는 쓰지 않는다" ], "params": [ { "name": "주기", "default": "500ms", "range": "400~800ms", "note": "2Hz 이하" }, { "name": "횟수", "default": "2회", "range": "2~3회", "note": "지속 1000ms" }, { "name": "최저 opacity", "default": "0", "range": "0~0.3", "note": "0.3도 가능" }, { "name": "이징", "default": "sine.inOut", "range": "none~sine", "note": "깜빡임이 뚝 끊기면 steps" } ], "ease": "sine.inOut", "snippet": "tl.to('.dot', { opacity: 0, duration: 0.25, ease: 'sine.inOut', yoyo: true, repeat: 3 }, 0.3); // 0.25s x 4 = 1.0s, 2회 점멸", "pairsWith": [ "hotspot-pulse", "pulse", "strobe-flash" ], "engines": { "hyperframes": "repeat 횟수를 명시해 타임라인 길이를 유한하게 한다. 커서 깜빡임은 steps(1)로 하드 점멸을 만든다", "reelforge": "브리프에 주기, 횟수, 최저 opacity, 대상 요소를 싣는다", "scrolline": "진행률의 지정 구간에서만 점멸하고 구간 밖에서는 opacity 1로 고정한다. scrub 방향에 상관없이 깜빡임은 무해하다" }, "prompts": { "claude": "<대상> 상태 표시 점에 점멸을 넣어줘. 0.3초부터 opacity 1에서 0까지 0.25초, sine.inOut으로 yoyo, repeat 3으로 총 1초, 2번 깜빡이고 끝은 opacity 1이야. 초당 3회를 넘지 않게 해.", "codex": "<파일>에 blink를 추가해. opacity 1→0, duration 0.25s, ease sine.inOut, yoyo, repeat 3, 시작 0.3초. 0.55초, 0.8초, 1.05초, 1.4초에 캡처해 opacity가 0, 1, 0, 1 순으로 나오는지 확인해." }, "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/indication.py", "license": "MIT" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "IanLunn/Hover", "url": "https://github.com/IanLunn/Hover", "license": "MIT personal/open-source + paid commercial" } ], "runtime": "css", "variants": [ "투명도 점멸", "밝기 점멸", "가시성 점멸" ], "oneLinerEn": "An element's brightness or opacity switches on and off repeatedly.", "conveysEn": "Signals warning, presence or in-progress state. It is the simplest attention cue.", "useWhenEn": [ "Use on small indicators such as recording, live or warnings.", "Show a cursor or input position." ], "promptsEn": { "claude": "Add a blink to the status dot in . From 0.3 seconds animate opacity 1 to 0 over 0.25s with sine.inOut, yoyo and repeat 3, for 1 second and 2 blinks total, ending at opacity 1. Stay under 3 blinks per second.", "codex": "Add blink to : opacity 1 to 0, duration 0.25s, ease sine.inOut, yoyo, repeat 3, start 0.3s. Capture at 0.55s, 0.8s, 1.05s and 1.4s and verify opacity reads 0, 1, 0, 1 in that order." }, "render": "planned", "files": {}, "no": "074" }, { "slug": "bounce-attention", "family": "emphasis", "ko": "통통 튀기", "en": "Bounce Attention", "aka": [], "oneLiner": "요소가 제자리에서 위아래로 튀다가 원래 위치로 돌아온다", "conveys": "활기와 선택 대상에 대한 주의를 만든다. 클릭해 달라는 신호로도 쓴다", "purposes": [ "주목 끌기", "피드백" ], "media": [ "웹 UI", "숏폼", "제품 시연" ], "level": "기본", "useWhen": [ "버튼이나 알림 아이콘을 눌러 보라고 유도할 때", "목록에서 새로 추가된 항목을 알릴 때" ], "goodExample": "900ms 동안 높이 24px로 3번 튀며 진폭이 줄고 원위치에 정확히 돌아온다. 3초 뒤 한 번 반복한다", "badExample": "1초 간격으로 무한 반복해 산만하고, 모든 버튼이 함께 튄다", "avoid": [ "동시에 튀는 요소는 하나만", "무한 반복 금지(3회 이하)" ], "params": [ { "name": "지속", "default": "900ms", "range": "700~1100ms", "note": "" }, { "name": "높이", "default": "24px", "range": "16~32px", "note": "첫 진폭" }, { "name": "튕김", "default": "3회", "range": "2~4회", "note": "진폭은 매번 0.55배" }, { "name": "이징", "default": "ease-out", "range": "power2.out", "note": "상승은 out, 하강은 in" } ], "ease": "power2.out", "snippet": "[24, 13, 7, 0].forEach((h, i) => {\n tl.to('.btn', { y: -h, duration: 0.12, ease: 'power2.out' }, 0.3 + i * 0.24)\n .to('.btn', { y: 0, duration: 0.12, ease: 'power2.in' }, 0.42 + i * 0.24);\n});", "pairsWith": [ "hotspot-pulse", "wobble", "tada" ], "engines": { "hyperframes": "상승/하강 tween을 쌍으로 쌓는다. repeat -1은 쓰지 않고 횟수를 명시한다", "reelforge": "브리프에 높이, 튕김 횟수, 대상 요소, 반복 시각을 싣는다", "scrolline": "진행률의 한 구간에서만 재생하고 구간 밖에서는 y 0으로 고정한다. scrub 중 하강 시 ease-in이 어색하니 linear를 허용한다" }, "prompts": { "claude": "<대상> 버튼에 주의를 끄는 통통 튀기를 넣어줘. 0.3초부터 높이 24px, 13px, 7px 순으로 3번 튀고, 각 상승은 0.12초 power2.out, 하강은 0.12초 power2.in으로 하고 마지막은 y 0에 정확히 멈추게 해.", "codex": "<파일>에 bounce attention을 구현해. 높이 [24, 13, 7], 상승 0.12s power2.out, 하강 0.12s power2.in, 시작 0.3초. 0.42초, 0.66초, 0.9초, 1.2초를 캡처해 높이가 감소하는지, 1.2초에 y가 0인지 확인해." }, "sources": [ { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "IanLunn/Hover", "url": "https://github.com/IanLunn/Hover", "license": "MIT personal/open-source + paid commercial" } ], "runtime": "css", "variants": [ "bounce", "bounce-top", "bounce-bottom", "icon-bounce", "bounce-left", "bounce-right", "bounce-to-bottom", "bounce-to-left", "bounce-to-right", "bounce-to-top" ], "oneLinerEn": "An element bounces up and down in place, then returns to its original position.", "conveysEn": "Creates liveliness and draws attention to a selectable target. It also works as a cue to click.", "useWhenEn": [ "Nudge people to press a button or notification icon.", "Announce a newly added list item." ], "promptsEn": { "claude": "Add an attention bounce to the button in . From 0.3 seconds bounce three times with heights 24px, 13px and 7px, each rise 0.12s power2.out and each fall 0.12s power2.in, ending exactly at y 0.", "codex": "Implement bounce attention in : heights [24, 13, 7], rise 0.12s power2.out, fall 0.12s power2.in, start 0.3s. Capture at 0.42s, 0.66s, 0.9s and 1.2s and verify the heights decrease and y is 0 at 1.2s." }, "render": "planned", "files": {}, "no": "075" }, { "slug": "circumscribe", "family": "emphasis", "ko": "둘레선 강조", "en": "Circumscribe", "aka": [ "Temporary circumscription", "임시 둘레선 강조", "CircleIndicate", "ShowCreationThenDestructionAround", "ShowCreationThenFadeAround" ], "oneLiner": "대상 바깥에 원이나 사각 테두리가 그려졌다가 지워지는 임시 강조", "conveys": "설명하는 대상의 범위를 분명히 한다. 잠깐 두르고 물러나 화면을 어지럽히지 않는다", "purposes": [ "강조", "설명" ], "media": [ "설명 영상", "발표", "웹 UI" ], "level": "기본", "useWhen": [ "수식·코드 조각·차트 영역을 잠깐 짚어 설명할 때", "화면에서 여러 개 중 하나를 지정할 때" ], "goodExample": "수치 주위 여백 12px에 선 3px 사각형이 0.6초에 그려지고 0.4초 유지된 뒤 0.3초에 지워진다", "badExample": "테두리를 영구히 남겨 다음 강조와 겹치거나, 대상에 붙어 글자를 자른다", "avoid": [ "여백 8~16px", "유지 후 반드시 지운다", "한 번에 하나만" ], "params": [ { "name": "그리기", "default": "0.6s", "range": "0.4~0.8s", "note": "stroke 그리기" }, { "name": "유지", "default": "0.4s", "range": "0.3~0.8s", "note": "읽는 시간" }, { "name": "여백", "default": "12px", "range": "8~16px", "note": "대상 경계 바깥" }, { "name": "선 두께", "default": "3px", "range": "2~4px", "note": "1920x1080 기준" } ], "ease": "power2.inOut", "snippet": "tl.fromTo('.box', { strokeDashoffset: 1 }, { strokeDashoffset: 0, duration: 0.6, ease: 'power2.inOut' }, 0.4)\n .to('.box', { opacity: 0, duration: 0.3 }, 1.4);", "pairsWith": [ "border-reveal", "underline-draw", "spotlight" ], "engines": { "hyperframes": "rect에 pathLength=1을 주고 dashoffset으로 그린다. 유지 시간은 빈 tween이 아니라 지운 시작 시각으로 표현한다", "reelforge": "브리프에 대상 bbox, 여백, 색, 모양(원/사각)을 싣는다", "scrolline": "scrub에서는 그리기를 진행률 0~0.4, 지우기를 0.7~1로 나누고 중간은 유지" }, "prompts": { "claude": "GSAP으로 <대상> 주위에 둘레선 강조를 넣어줘. 대상 bbox에서 12px 바깥의 사각형 stroke 3px을 0.4초에 시작해 0.6초 동안 strokeDashoffset 1에서 0으로 그리고, 1.4초에 0.3초 동안 opacity 0으로 지워. pathLength=1, 색은 <강조색>. paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 circumscribe를 적용해. .box rect(margin 12, stroke 3, pathLength 1)를 fromTo(dashoffset 1→0, 0.6s, power2.inOut, position 0.4), 이후 opacity 0(0.3s, position 1.4). 0.7초·1.2초·2.0초를 캡처해 그리는 중, 완성, 소멸 상태를 확인해." }, "sources": [ { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/indication.py", "license": "MIT" }, { "title": "3b1b/manim", "url": "https://github.com/3b1b/manim/blob/master/manimlib/animation/indication.py", "license": "MIT" } ], "runtime": "svg", "variants": [ "원 둘레", "사각 둘레", "역순 지우기" ], "oneLinerEn": "A circle or rectangle is drawn around a target and then erased.", "conveysEn": "Makes the extent of what is being explained clear, then steps back.", "useWhenEn": [ "When briefly circling a formula, code piece or chart region", "When picking one item out of several on screen" ], "promptsEn": { "claude": "Add a circumscribe around with GSAP. A rectangle 12px outside the bbox with stroke 3px starts at 0.4s and draws strokeDashoffset 1 to 0 over 0.6s, then fades to opacity 0 over 0.3s at 1.4s. pathLength=1, color . Paused timeline.", "codex": "Apply circumscribe to in . fromTo on the .box rect (margin 12, stroke 3, pathLength 1) dashoffset 1 to 0, 0.6s, power2.inOut, position 0.4, then opacity 0 over 0.3s at 1.4. Capture at 0.7s, 1.2s and 2.0s to check drawing, complete and erased states." }, "render": "done", "files": { "html": "effects/circumscribe/index.html", "mp4": "effects/circumscribe/clip.mp4", "gif": "effects/circumscribe/preview.gif", "poster": "effects/circumscribe/poster.jpg" }, "no": "076" }, { "slug": "color-transition", "family": "emphasis", "ko": "색 전환", "en": "Color Transition", "aka": [ "Color-state transition", "상태 색 변화", "FadeToColor" ], "oneLiner": "대상의 색이 기존 색에서 다음 색으로 부드럽게 바뀌는 전환", "conveys": "활성화, 역할 변화, 상태 전이가 일어났음을 알린다", "purposes": [ "강조", "피드백" ], "media": [ "웹 UI", "설명 영상", "데이터 스토리" ], "level": "기본", "useWhen": [ "버튼·노드가 대기에서 활성 상태로 바뀔 때", "차트 막대가 임계값을 넘으며 경고색으로 바뀔 때" ], "goodExample": "막대가 0.45초 동안 청색 #2f6bff에서 노랑 #ffc83d로 바뀌고 옆 라벨도 같은 시각에 색이 바뀐다", "badExample": "색만 바뀌고 다른 단서가 없어 색맹 시청자가 변화를 못 읽거나, 색 전환이 0.1초 미만이라 깜빡임으로 보인다", "avoid": [ "색만으로 의미 전달 금지(아이콘·라벨 병행)", "길이 0.25초 미만 금지", "전후 색의 대비비 3:1 이상" ], "params": [ { "name": "길이", "default": "0.45s", "range": "0.3~0.8s", "note": "색 보간" }, { "name": "이징", "default": "sine.inOut", "range": "sine~power2.inOut", "note": "부드럽게" }, { "name": "전 색", "default": "#2f6bff", "range": "브랜드 토큰", "note": "시작" }, { "name": "후 색", "default": "#ffc83d", "range": "브랜드 토큰", "note": "도착" } ], "ease": "sine.inOut", "snippet": "tl.to('.bar', { backgroundColor: '#ffc83d', duration: 0.45, ease: 'sine.inOut' }, 0.4)\n .to('.bar-label', { color: '#7a5a00', duration: 0.45, ease: 'sine.inOut' }, 0.4);", "pairsWith": [ "state-tween", "threshold-pulse", "chart-filter-fade" ], "engines": { "hyperframes": "색은 hex나 rgb로 통일해 GSAP 코어 보간이 안정되게 한다. fromTo로 시작 색을 고정한다", "reelforge": "브리프에 전후 색 토큰, 길이, 변화 이유 문구(라벨)를 싣는다", "scrolline": "scrub에서는 진행률 0.3~0.6 구간에서만 전환하고 나머지는 유지한다. 색 전환은 ease-out 또는 linear" }, "prompts": { "claude": "GSAP으로 <대상> 막대의 색 전환을 넣어줘. 0.4초부터 0.45초 동안 backgroundColor를 #2f6bff에서 #ffc83d로 sine.inOut으로 바꾸고 같은 시각에 라벨 글자색도 #7a5a00으로 바꿔. 색만으로 의미를 전하지 않게 라벨에 상태 문구를 함께 넣어줘. paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 color-transition을 적용해. backgroundColor #2f6bff→#ffc83d, 0.45s, sine.inOut, position 0.4, 라벨 color #7a5a00 동시. 0.4초·0.6초·1.0초를 캡처해 중간 색이 보간되는지, 전후 대비비가 3:1 이상인지 계산해." }, "sources": [ { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/transform.py", "license": "MIT" } ], "runtime": "gsap", "variants": [ "채움 색", "윤곽 색", "부분 색" ], "oneLinerEn": "An element's color blends smoothly from its current color to the next.", "conveysEn": "Signals activation, a role change or a state transition.", "useWhenEn": [ "When a button or node goes from idle to active", "When a chart bar crosses a threshold and turns to a warning color" ], "promptsEn": { "claude": "Add a color transition to the bar with GSAP. From 0.4s over 0.45s change backgroundColor from #2f6bff to #ffc83d with sine.inOut, and change the label text color to #7a5a00 at the same time. Do not rely on color alone, so include a state phrase in the label. Paused timeline.", "codex": "Apply color-transition to in . backgroundColor #2f6bff to #ffc83d, 0.45s, sine.inOut, position 0.4, label color #7a5a00 in parallel. Capture at 0.4s, 0.6s and 1.0s to check the mid color interpolates and compute that before and after contrast is at least 3:1." }, "render": "planned", "files": {}, "no": "077" }, { "slug": "focus-contraction", "family": "emphasis", "ko": "초점 원판 축소", "en": "Focus Contraction", "aka": [ "Contracting focus disk", "축소 원판 초점 신호", "FocusOn" ], "oneLiner": "넓고 반투명한 원판이 목표 위치로 줄어들며 사라져 정확한 지점을 알려 주는 신호", "conveys": "지금 볼 곳은 여기라는 조준. 바깥에서 안으로 시선이 모인다", "purposes": [ "주목 끌기", "강조" ], "media": [ "설명 영상", "제품 시연", "웹 UI" ], "level": "기본", "useWhen": [ "화면 안의 작은 버튼·수치를 정확히 짚어야 할 때", "긴 화면 전환 직후 시선을 목표로 모을 때" ], "goodExample": "반지름 400px, opacity 0.25 원판이 0.7초 동안 목표 위치의 반지름 0으로 줄어들며 사라진다", "badExample": "원판 불투명도가 0.6 이상이라 뒤 내용을 가리거나, 목표가 원판 중심이 아니다", "avoid": [ "시작 opacity 0.35 이하", "원판 중심을 목표 중심에 정확히 맞춘다", "반복 금지(1회)" ], "params": [ { "name": "길이", "default": "0.7s", "range": "0.5~1.0s", "note": "수축과 소멸 합산" }, { "name": "시작 반지름", "default": "400px", "range": "250~600px", "note": "1920x1080 기준" }, { "name": "opacity", "default": "0.25→0", "range": "0.15~0.35", "note": "가려짐 방지" }, { "name": "이징", "default": "power2.out", "range": "power2~power3.out", "note": "목표 근처에서 느리게" } ], "ease": "power2.out", "snippet": "tl.fromTo('.focus', { attr: { r: 400 }, opacity: 0.25 }, { attr: { r: 0 }, opacity: 0, duration: 0.7, ease: 'power2.out' }, 0.4);", "pairsWith": [ "spotlight", "circumscribe", "zoom-callout" ], "engines": { "hyperframes": "원판 cx, cy를 목표 좌표로 미리 지정한다. fromTo로 시작 상태를 정의해 paused seek 시 튀지 않게 한다", "reelforge": "브리프에 목표 좌표, 시작 반지름, 길이를 싣고 색은 브랜드 강조색으로 지정한다", "scrolline": "scrub에서는 진행률 0~0.5에 수축하고 0.5 이후 opacity 0으로 둔다. 되감기에서도 같은 궤적" }, "prompts": { "claude": "GSAP으로 <대상> 위치에 초점 원판 축소를 넣어줘. 0.4초부터 0.7초 동안 원판(cx, cy는 <좌표>) 반지름을 400에서 0으로, opacity를 0.25에서 0으로 power2.out으로 줄여. 원판 색은 <강조색>. 반복은 없고 paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 focus-contraction을 적용해. #focus에 cx, cy를 목표 좌표로 두고 fromTo(r 400→0, opacity 0.25→0, 0.7s, power2.out, position 0.4). 0.4초·0.75초·1.2초를 캡처해 원판이 목표 중심으로 수렴하는지, 1.2초에 화면에 남지 않는지 확인해." }, "sources": [ { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/indication.py", "license": "MIT" } ], "runtime": "svg", "variants": [ "원형", "타원형" ], "oneLinerEn": "A wide, translucent disk shrinks onto the target and vanishes.", "conveysEn": "A sighting cue that says look here. Attention converges from outside in.", "useWhenEn": [ "When a small button or number must be pinpointed", "When pulling the eye to the target after a long screen change" ], "promptsEn": { "claude": "Add a focus contraction at with GSAP. From 0.4s over 0.7s shrink a disk (cx, cy at ) from radius 400 to 0 and opacity 0.25 to 0 with power2.out. Disk color . No repeat, on a paused timeline.", "codex": "Apply focus-contraction to in . Set cx and cy of #focus to the target center and fromTo (r 400 to 0, opacity 0.25 to 0, 0.7s, power2.out, position 0.4). Capture at 0.4s, 0.75s and 1.2s to check the disk converges on the target center and is gone by 1.2s." }, "render": "planned", "files": {}, "no": "078" }, { "slug": "focus-handoff", "family": "emphasis", "ko": "강조점 순회", "en": "Focus Handoff", "aka": [ "Accent Walk", "gradient-text-sweep", "Narration-synchronized signaling", "해설 동기 신호 주기", "Focus handoff", "초점 이전", "Code Range Focus", "코드 범위 초점 이동" ], "oneLiner": "이미 배치된 여러 항목 중 하나씩 색이나 빛이 켜졌다가 다음 항목으로 옮겨 간다", "conveys": "지금 설명 중인 항목이 어디인지, 읽는 순서가 어떻게 되는지 한눈에 알게 한다", "purposes": [ "강조", "순서·흐름" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "4개 항목을 차례로 설명하며 현재 항목만 밝히고 싶을 때", "해설과 화면이 동기되는 목록형 장면을 만들 때" ], "goodExample": "항목 4개가 모두 보이고 강조는 하나씩 500ms 홀드 후 150ms 전환으로 넘어간다. 꺼진 항목은 opacity 0.35", "badExample": "항목마다 다른 색을 쓰고 전환이 0.6초 이상 걸려 어디가 켜졌는지 헷갈린다. 두 항목이 동시에 켜진다", "avoid": [ "동시 강조는 하나만", "항목 6개 초과 시 스크롤이나 분할을 쓴다" ], "params": [ { "name": "항목 수", "default": "4", "range": "3~6", "note": "전부 미리 배치" }, { "name": "홀드", "default": "500ms", "range": "400~1200ms", "note": "해설 길이에 맞춤" }, { "name": "전환", "default": "150ms", "range": "100~250ms", "note": "교차 페이드" }, { "name": "비활성 opacity", "default": "0.35", "range": "0.25~0.5", "note": "읽을 수 있는 최소" } ], "ease": "power2.inOut", "snippet": "items.forEach((el, i) => {\n const t = 0.4 + i * 0.65; // 홀드 500 + 전환 150\n tl.to(el, { opacity: 1, scale: 1.04, duration: 0.15, ease: 'power2.inOut' }, t);\n tl.to(el, { opacity: 0.35, scale: 1, duration: 0.15, ease: 'power2.inOut' }, t + 0.65);\n});", "pairsWith": [ "highlight-sweep", "narration-sync", "selection-travel" ], "engines": { "hyperframes": "항목별 on/off tween을 타임라인 위치로 배치한다. 해설 시각 배열을 그대로 시작 시각에 쓰면 narration-sync와도 맞물린다", "reelforge": "브리프에 항목 수, 홀드, 전환, 비활성 opacity를 싣고 해설 문장 길이로 홀드를 정한다", "scrolline": "진행률 구간을 항목 수로 등분한다. 스크럽 중 두 항목이 겹치지 않도록 전환 폭은 구간의 20% 이하" }, "prompts": { "claude": "<대상> 목록 4개 항목이 처음부터 모두 보이되 0.35 opacity로 흐리게 두고, 0.4초부터 항목이 하나씩 opacity 1, scale 1.04로 켜지게 해줘. 각 항목은 500ms 홀드 후 150ms에 다음 항목으로 넘어가고 이전 항목은 다시 0.35로 돌아가게 해. 이징은 power2.inOut.", "codex": "<파일>에 focus handoff를 구현해. 항목 4개, 홀드 500ms, 전환 150ms, 비활성 opacity 0.35, 활성 scale 1.04, ease power2.inOut. 0.6초, 1.25초, 1.9초, 2.55초에 캡처해 그 시점에 켜진 항목이 정확히 하나인지 확인해." }, "sources": [ { "title": "gongnyang/awesome-html-scrolline-deck", "url": "gongnyang/awesome-html-scrolline-deck:references/techniques.md#kinetic-titles", "license": "MIT" }, { "title": "Richard E. Mayer / Wiley", "url": "https://onlinelibrary.wiley.com/doi/10.1111/jcal.12197", "license": "unknown" }, { "title": "motion dictionary 1-principles.md#7. 연속성·공간 모델·시선 유도", "url": "", "license": "own" }, { "title": "gongnyang/awesome-html-scrolline-deck", "url": "gongnyang/awesome-html-scrolline-deck:templates/scenes/kinetic-titles/scene.css", "license": "MIT" }, { "title": "Cambridge University Press", "url": "https://www.cambridge.org/highereducation/books/multimedia-learning/FB7E79A165D24D47CEACEB4D2C426ECD/signaling-principle/9E37E775874EC1D93763620B8A296DD4", "license": "unknown" }, { "title": "motion-canvas/motion-canvas", "url": "https://motioncanvas.io/docs/code", "license": "MIT" }, { "title": "motion-canvas/motion-canvas", "url": "https://github.com/motion-canvas/motion-canvas", "license": "MIT" } ], "runtime": "gsap", "variants": [ "항목 순회", "해설 큐 동기", "색 강조 교대", "줄 선택", "식 선택", "복수 구간 선택" ], "oneLinerEn": "Among items already laid out, one lights up at a time and the highlight hands off to the next.", "conveysEn": "Viewers see which item is being explained and the reading order at a glance.", "useWhenEn": [ "Explain four items in turn and light only the current one.", "Build a list scene synced with narration." ], "promptsEn": { "claude": "Make all 4 list items in visible from the start at 0.35 opacity, then from 0.4 seconds light them one at a time with opacity 1 and scale 1.04. Each holds 500ms then hands off over 150ms while the previous returns to 0.35. Use power2.inOut.", "codex": "Implement focus handoff in : 4 items, hold 500ms, transition 150ms, inactive opacity 0.35, active scale 1.04, ease power2.inOut. Capture at 0.6s, 1.25s, 1.9s and 2.55s and verify exactly one item is lit at each time." }, "render": "done", "files": { "html": "effects/focus-handoff/index.html", "mp4": "effects/focus-handoff/clip.mp4", "gif": "effects/focus-handoff/preview.gif", "poster": "effects/focus-handoff/poster.jpg" }, "no": "079" }, { "slug": "outline-pulse", "family": "emphasis", "ko": "윤곽 펄스", "en": "Outline Pulse", "aka": [ "Outline and Edge Pulse", "윤곽과 에지 맥박" ], "oneLiner": "이미지나 물체의 경계선이 나타나고 두께와 밝기가 잠깐 커졌다 줄어드는 맥동", "conveys": "복잡한 화면에서 선택한 형태를 분명히 드러낸다", "purposes": [ "강조", "주목 끌기" ], "media": [ "설명 영상", "제품 시연", "숏폼" ], "level": "고급", "useWhen": [ "사진 속 제품·인물의 윤곽을 짚을 때", "3D 물체나 아이콘을 선택했다는 표시를 줄 때" ], "goodExample": "윤곽이 강도 0에서 0.8로 올라가고 두께가 1px에서 3px로 커졌다가 1.4초에 1px로 돌아온다", "badExample": "윤곽이 지나치게 두꺼워 형태가 뭉개지거나, 배경과 같은 색이라 보이지 않는다", "avoid": [ "두께 4px 초과 금지", "윤곽 색은 배경과 3:1 이상", "선택된 대상만 윤곽" ], "params": [ { "name": "길이", "default": "1.4s", "range": "1.0~2.0s", "note": "상승과 복귀 합산" }, { "name": "두께", "default": "1→3px", "range": "1~4px", "note": "1920x1080 기준" }, { "name": "강도", "default": "0→0.8", "range": "0.5~1.0", "note": "알파 또는 밝기" }, { "name": "이징", "default": "sine.inOut", "range": "sine~power2.inOut", "note": "맥동" } ], "ease": "sine.inOut", "snippet": "tl.to('.shape', { attr: { 'stroke-width': 3 }, strokeOpacity: 0.8, duration: 0.7, ease: 'sine.inOut', yoyo: true, repeat: 1 }, 0.4);", "pairsWith": [ "bloom-pulse", "border-reveal", "spotlight" ], "engines": { "hyperframes": "SVG 윤곽 path의 stroke-width와 strokeOpacity를 tween한다. 이미지 윤곽은 미리 추출한 path를 사용해 결정론을 보장", "reelforge": "브리프에 윤곽 path나 마스크, 두께 범위, 색, 길이를 싣는다", "scrolline": "scrub에서는 진행률 0~0.5에 굵어지고 0.5~1에 얇아지는 매핑. ease-out" }, "prompts": { "claude": "GSAP으로 <대상> 윤곽 path에 윤곽 펄스를 넣어줘. 0.4초부터 0.7초 동안 stroke-width 1에서 3px, strokeOpacity 0에서 0.8로 sine.inOut으로 올라갔다가 같은 시간에 1px, 0으로 돌아와. 색은 <강조색>, 배경과 3:1 이상. paused 타임라인에 넣어.", "codex": "<파일>의 <대상>에 outline-pulse를 적용해. .shape의 stroke-width 1→3, strokeOpacity 0→0.8, 0.7s, sine.inOut, yoyo true, repeat 1, position 0.4. 0.4초·1.1초·1.9초를 캡처해 최대 두께 3px, 종료 시 1px인지와 윤곽 대비 3:1을 확인해." }, "sources": [ { "title": "mrdoob/three.js", "url": "https://threejs.org/examples/#webgl_postprocessing_outline", "license": "MIT" }, { "title": "mrdoob/three.js", "url": "https://threejs.org/examples/#webgl_postprocessing_sobel", "license": "MIT" } ], "runtime": "webgl", "variants": [ "선택 윤곽", "스케치 에지", "전체 경계 강조" ], "oneLinerEn": "A shape's edge line appears and briefly thickens and brightens before easing back.", "conveysEn": "Reveals the selected form clearly on a busy screen.", "useWhenEn": [ "When tracing the outline of a product or person in a photo", "When marking a 3D object or icon as selected" ], "promptsEn": { "claude": "Add an outline pulse to the outline path with GSAP. From 0.4s over 0.7s raise stroke-width 1 to 3px and strokeOpacity 0 to 0.8 with sine.inOut, then return to 1px and 0 in the same time. Color , at least 3:1 against the background. Paused timeline.", "codex": "Apply outline-pulse to in . Tween .shape stroke-width 1 to 3 and strokeOpacity 0 to 0.8, 0.7s, sine.inOut, yoyo true, repeat 1, position 0.4. Capture at 0.4s, 1.1s and 1.9s to check max width 3px, 1px at the end, and outline contrast 3:1." }, "render": "planned", "files": {}, "no": "080" }, { "slug": "radial-speed-lines", "family": "emphasis", "ko": "방사 속도선", "en": "Radial Speed Lines", "aka": [ "Radial speed lines", "radial-burst-lines", "Radial flash", "방사형 섬광", "Flash" ], "oneLiner": "중심에서 바깥으로 향하는 선들이 짧게 뻗었다가 사라진다", "conveys": "충격과 빠른 확대의 에너지를 한순간에 전한다. 만화의 집중선처럼 시선을 중심으로 모은다", "purposes": [ "강조", "주목 끌기" ], "media": [ "숏폼", "설명 영상", "제품 시연" ], "level": "기본", "useWhen": [ "핵심 단어나 제품이 화면에 꽝 하고 나타나는 순간을 강조할 때", "컷 전환 직후 시선을 중심으로 모으고 싶을 때" ], "goodExample": "선 16개가 반경 60px에서 400px까지 300ms 동안 뻗고 마지막 100ms에 투명해진다. 중앙 제목은 같은 시점에 나타난다", "badExample": "선을 100개 이상 촘촘히 넣고 1.5초 동안 유지해 배경이 지저분해진다. 매 장면마다 반복해 효과가 무뎌진다", "avoid": [ "한 장면에 한 번만 쓴다", "지속 500ms 초과 금지(정보가 가려짐)" ], "params": [ { "name": "선 개수", "default": "16", "range": "12~24", "note": "각도 22.5도 간격" }, { "name": "반경 범위", "default": "60~400px", "range": "시작 40~80, 끝 350~500", "note": "1920x1080 중심 기준" }, { "name": "지속", "default": "300ms", "range": "200~400ms", "note": "마지막 1/3에 페이드" }, { "name": "선 두께", "default": "3px", "range": "2~4px", "note": "끝으로 가늘어짐" } ], "ease": "power2.out", "snippet": "const N = 16;\nfor (let i = 0; i < N; i++) {\n const a = i / N * Math.PI * 2;\n tl.fromTo(`.ray${i}`, { attr: { x1: 60 * Math.cos(a), y1: 60 * Math.sin(a), x2: 60 * Math.cos(a), y2: 60 * Math.sin(a) }, opacity: 1 },\n { attr: { x1: 240 * Math.cos(a), y1: 240 * Math.sin(a), x2: 400 * Math.cos(a), y2: 400 * Math.sin(a) }, opacity: 0, duration: 0.3, ease: 'power2.out' }, 0.5);\n}", "pairsWith": [ "zoom-flash", "attention-lines", "screen-shake" ], "engines": { "hyperframes": "SVG 선 attr를 paused 타임라인으로 보간한다. 컷 프레임과 같은 시각에 시작해 첫 프레임에 이미 보이게 한다", "reelforge": "씬 워커 브리프에 중심 좌표, 선 개수, 반경 범위, 지속, 색을 싣는다", "scrolline": "진행률 구간 2~3%에 걸쳐 재생한다. scrub 중에는 짧은 효과가 스치기 쉬워 5% 폭으로 넓힌다" }, "prompts": { "claude": "<대상> 제목이 나타나는 순간에 방사 속도선을 넣어줘. SVG 선 16개, 시작 반경 60px에서 끝 반경 400px까지 0.3초 동안 power2.out으로 뻗고 opacity가 1에서 0으로 사라지게 해. 시작 시각은 제목 등장과 같은 0.5초이고, 선 두께는 3px 흰색이야.", "codex": "<파일>에 radial speed lines를 추가해. 선 16개, 중심 (960,540), 반경 60에서 400px, 0.3초, ease power2.out, 시작 0.5초. 0.5초, 0.65초, 0.8초를 캡처해 선이 대칭으로 균일하게 뻗는지, 0.8초에는 완전히 사라져 잔상이 없는지 확인해." }, "sources": [ { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/rules/css-marker-patterns.md", "license": "unknown" }, { "title": "local/music-to-video", "url": "claude-skill:music-to-video/references/motion-primitive-catalog.md#radial-burst-lines", "license": "unknown" }, { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/indication.py", "license": "MIT" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:music-to-video/references/motion-primitive-catalog.md", "license": "unknown" }, { "title": "local/music-to-video", "url": "claude-skill:music-to-video/references/motion-primitives/radial-burst-lines/scene.html", "license": "unknown" } ], "runtime": "svg", "variants": [ "긴 속도선", "짧은 방사 섬광", "폭발 광선" ], "oneLinerEn": "Lines from the center burst outward briefly and vanish.", "conveysEn": "Delivers impact and rapid expansion in one moment. Like manga focus lines, it gathers the eye at the center.", "useWhenEn": [ "Emphasize the instant a key word or product slams onto the screen.", "Pull attention to the center right after a cut." ], "promptsEn": { "claude": "Add radial speed lines when the title in appears. Use 16 SVG lines extending from radius 60px to 400px over 0.3 seconds with power2.out while opacity goes from 1 to 0. Start at 0.5 seconds with the title; lines are 3px white.", "codex": "Add radial speed lines to : 16 lines, center (960,540), radius 60 to 400px, 0.3s, ease power2.out, start at 0.5s. Capture at 0.5s, 0.65s and 0.8s and verify the lines extend symmetrically and uniformly and are fully gone by 0.8s with no residue." }, "render": "planned", "files": {}, "no": "081" }, { "slug": "selection-travel", "family": "emphasis", "ko": "선택 영역 이동", "en": "Selection Travel", "aka": [ "Code selection travel", "코드 선택 영역 이동", "Chart focus band sweep", "차트 구간 강조 띠" ], "oneLiner": "반투명 선택 띠가 한 범위에서 다음 범위로 이동하며 폭과 높이를 맞추는 강조", "conveys": "설명하는 대상의 범위를 읽게 한다. 같은 띠가 이동해 연속성을 준다", "purposes": [ "설명", "강조" ], "media": [ "설명 영상", "발표", "데이터 스토리" ], "level": "중급", "useWhen": [ "코드 줄이나 토큰을 차례로 짚으며 설명할 때", "차트의 기간 구간을 한 구간씩 이동하며 강조할 때" ], "goodExample": "코드 3행에 있던 선택 띠가 0.4초 동안 7행으로 내려가며 높이가 1행에서 3행으로 늘어난다", "badExample": "띠가 매번 새로 나타났다 사라져 연속성이 없거나, 불투명도가 높아 글자를 가린다", "avoid": [ "띠 opacity 0.15~0.3", "띠는 대상 경계보다 2~4px 여유", "이동 중에도 대상 글자가 읽히게" ], "params": [ { "name": "이동 길이", "default": "0.4s", "range": "0.3~0.6s", "note": "범위 간 이동" }, { "name": "opacity", "default": "0.25", "range": "0.15~0.3", "note": "글자를 가리지 않게" }, { "name": "여백", "default": "2px", "range": "2~4px", "note": "대상 경계 바깥" }, { "name": "이징", "default": "power3.inOut", "range": "power2~power3.inOut", "note": "도착이 정확" } ], "ease": "power3.inOut", "snippet": "const spans = [{ y: 120, h: 36 }, { y: 300, h: 108 }, { y: 516, h: 36 }];\nspans.forEach((s, i) => tl.to('.sel', { y: s.y, height: s.h, duration: 0.4, ease: 'power3.inOut' }, 0.3 + i * 1.2));", "pairsWith": [ "highlight-sweep", "spotlight", "zoom-callout" ], "engines": { "hyperframes": "띠 하나를 y와 height만 tween한다. 범위 좌표는 미리 측정해 배열로 고정하고 폰트 로딩 후 값을 얻는다", "reelforge": "브리프에 범위 좌표 배열, 이동 길이, 각 범위 유지 시간(0.8초 기준)을 싣는다", "scrolline": "scrub에서는 범위 i를 진행률 i/(n-1)에 매핑하고 사이는 ease-out 이동. 스프링 없음" }, "prompts": { "claude": "GSAP으로 <대상> 코드에서 반투명 선택 띠를 이동시켜줘. 띠는 opacity 0.25 하나를 재사용하고 spans 배열 [{y:120,h:36},{y:300,h:108},{y:516,h:36}]로 0.3초부터 1.2초마다 y와 height를 0.4초 power3.inOut으로 옮겨. 범위마다 0.8초 유지. paused 타임라인 하나로 만들어.", "codex": "<파일>의 <대상>에 selection-travel을 적용해. .sel 하나를 spans.forEach로 y, height를 0.4s, power3.inOut, position 0.3+i*1.2에 tween. 각 범위 좌표는 getBoundingClientRect로 측정한 값이어야 한다. 0.5초·1.5초·2.7초를 캡처해 띠 경계가 대상 줄 경계와 2px 이내인지 확인해." }, "sources": [ { "title": "motion-canvas/motion-canvas", "url": "https://motioncanvas.io/docs/code/", "license": "MIT" }, { "title": "chartjs/Chart.js", "url": "https://www.chartjs.org/docs/latest/configuration/animations.html", "license": "MIT" }, { "title": "fnando/sparkline", "url": "https://github.com/fnando/sparkline", "license": "MIT" }, { "title": "Flourish", "url": "https://app.flourish.studio/@flourish/scatter", "license": "unknown" } ], "runtime": "gsap", "variants": [ "코드 선택 범위", "차트 기간 범위" ], "oneLinerEn": "A translucent selection band moves from one range to the next, resizing to fit.", "conveysEn": "Reads the grammatical range of what is being explained. One band moving keeps continuity.", "useWhenEn": [ "When walking through code lines or tokens one by one", "When a chart's period range is moved along section by section" ], "promptsEn": { "claude": "Move a translucent selection band over code with GSAP. Reuse one band at opacity 0.25 and, from 0.3s every 1.2s, tween y and height to the next entry of spans [{y:120,h:36},{y:300,h:108},{y:516,h:36}] over 0.4s with power3.inOut. Hold 0.8s per range. One paused timeline.", "codex": "Apply selection-travel to in . Tween one .sel over spans.forEach with y and height, 0.4s, power3.inOut, position 0.3 + i*1.2. Range coordinates must come from getBoundingClientRect. Capture at 0.5s, 1.5s and 2.7s and check the band edges are within 2px of the target line edges." }, "render": "planned", "files": {}, "no": "082" }, { "slug": "shake", "family": "emphasis", "ko": "셰이크", "en": "Shake", "aka": [ "Wiggle oscillation", "좌우 흔들림", "CustomWiggle", "Wobble", "Shake and Vibrate", "흔들기와 진동", "Wiggle indication", "좌우 흔들기 강조", "Wiggle", "WiggleOutThenIn", "사용자 진동" ], "oneLiner": "요소가 기준 위치나 각도의 양쪽을 오가다가 멈춘다", "conveys": "주의를 끌거나 거부, 불안정, 오류를 표현한다. 작은 요소에 국소적으로 쓰는 시선 신호다", "purposes": [ "주목 끌기", "피드백" ], "media": [ "웹 UI", "제품 시연", "숏폼" ], "level": "기본", "useWhen": [ "입력 오류나 거부 상태를 폼 필드가 흔들려 알릴 때", "알림 아이콘이 주의를 끌어야 할 때" ], "goodExample": "600ms 동안 위치 진폭 6px로 4번 왕복하고 감쇠 0.6으로 줄며 멈춘다", "badExample": "감쇠 없이 계속 진동하거나 진폭 30px로 흔들어 화면이 무너진 것처럼 보인다", "avoid": [ "진폭 10px 초과 금지", "한 화면에서 동시에 여러 요소가 흔들리지 않게 한다" ], "params": [ { "name": "지속", "default": "600ms", "range": "400~800ms", "note": "" }, { "name": "위치 진폭", "default": "6px", "range": "4~10px", "note": "또는 회전 6도" }, { "name": "왕복", "default": "4회", "range": "3~5회", "note": "" }, { "name": "감쇠", "default": "0.6", "range": "0.5~0.7", "note": "다음 진폭 배율" } ], "ease": "sine.inOut", "snippet": "const amps = [6, -3.6, 2.2, -1.3, 0]; // 감쇠 0.6\namps.forEach((x, i) => tl.to('.field', { x, duration: 0.12, ease: 'sine.inOut' }, 0.5 + i * 0.12));", "pairsWith": [ "error-shake", "swing", "screen-shake" ], "engines": { "hyperframes": "감쇠 진폭 배열을 순차 tween으로 쌓는다. 마지막 값 0으로 위치가 복귀한다", "reelforge": "브리프에 진폭, 왕복 횟수, 감쇠, 대상 요소를 싣는다", "scrolline": "진행률 3% 구간에 재생한다. 배열 진폭이 0에 수렴하므로 스크럽이 멈춰도 위치가 어긋나지 않는다" }, "prompts": { "claude": "<대상> 입력 필드에 셰이크를 넣어줘. x를 6, -3.6, 2.2, -1.3, 0px 순으로 0.12초씩 sine.inOut으로 옮겨 총 0.6초 동안 감쇠 진동하게 하고 시작은 0.5초야. 끝은 정확히 x 0.", "codex": "<파일>의 오류 표시에 shake를 적용해. amps [6, -3.6, 2.2, -1.3, 0], 구간 0.12초, ease sine.inOut, 시작 0.5초. 0.5초, 0.62초, 0.74초, 1.1초를 캡처해 좌우 진폭이 줄고 1.1초에 x가 정확히 0인지 확인해." }, "sources": [ { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Eases/CustomWiggle/", "license": "GSAP Standard License" }, { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/indication.py", "license": "MIT" }, { "title": "motion dictionary 1-principles.md#4.1 곡선의 읽는 법", "url": "", "license": "own" }, { "title": "CodePen GreenSock", "url": "https://codepen.io/GreenSock/pen/wzkBYZ", "license": "unknown" } ], "runtime": "gsap", "variants": [ "위치 진동", "회전 진동", "감쇠 진동" ], "oneLinerEn": "An element oscillates on both sides of its rest position or angle, then stops.", "conveysEn": "Draws attention or signals refusal, instability or error. A local eye-catching cue for small elements.", "useWhenEn": [ "Shake a form field to signal an input error.", "Get a notification icon noticed." ], "promptsEn": { "claude": "Add a shake to the input field in . Move x through 6, -3.6, 2.2, -1.3, 0px in 0.12-second sine.inOut steps for a damped 0.6-second oscillation starting at 0.5 seconds. End at exactly x 0.", "codex": "Apply shake to the error state in : amps [6, -3.6, 2.2, -1.3, 0], segment 0.12s, ease sine.inOut, start 0.5s. Capture at 0.5s, 0.62s, 0.74s and 1.1s and verify the amplitude decays and x is exactly 0 at 1.1s." }, "render": "done", "files": { "html": "effects/shake/index.html", "mp4": "effects/shake/clip.mp4", "gif": "effects/shake/preview.gif", "poster": "effects/shake/poster.jpg" }, "no": "083" }, { "slug": "swing", "family": "emphasis", "ko": "스윙", "en": "Swing", "aka": [ "Corner Swing", "모서리 피벗 스윙", "anchor-corner-swing", "Pendulum Decay", "진자 감쇠", "pendulum-decay-swing", "Swing Attention", "매달린 흔들기" ], "oneLiner": "고정된 축에 매달린 요소가 좌우로 회전하다 진폭이 줄며 제자리로 돌아온다", "conveys": "카드가 걸려 있거나 힘을 받은 듯한 물성을 느끼게 한다. 진자의 감쇠로 자연스러운 멈춤을 만든다", "purposes": [ "강조", "피드백" ], "media": [ "웹 UI", "숏폼", "설명 영상" ], "level": "기본", "useWhen": [ "걸린 카드나 간판이 등장한 뒤 자연스럽게 흔들려 자리 잡을 때", "알림이나 변경을 흔들림으로 알릴 때" ], "goodExample": "좌상단 피벗의 카드가 6도에서 시작해 550ms 동안 -3.6도, 2도, 0으로 진폭이 줄며 멈춘다", "badExample": "진폭이 15도 이상이라 카드가 천장에 걸린 광고판처럼 흔들리고, 감쇠 없이 계속 왕복해 멈추지 않는다", "avoid": [ "초기 회전 10도 초과 금지", "표나 긴 문단 본문에는 쓰지 않는다" ], "params": [ { "name": "초기 회전", "default": "6도", "range": "4~10도", "note": "첫 진폭" }, { "name": "총 길이", "default": "550ms", "range": "400~800ms", "note": "감쇠 후 정지" }, { "name": "피벗", "default": "좌상", "range": "transformOrigin", "note": "top left 또는 top center" }, { "name": "감쇠 비", "default": "0.6", "range": "0.5~0.7", "note": "다음 진폭 = 이전 x 감쇠 비" } ], "ease": "power2.out", "snippet": "gsap.set('.card', { transformOrigin: '0% 0%' });\ntl.to('.card', { rotation: 6, duration: 0.12, ease: 'power2.out' }, 0.3)\n .to('.card', { rotation: -3.6, duration: 0.15, ease: 'power2.inOut' })\n .to('.card', { rotation: 2, duration: 0.15, ease: 'power2.inOut' })\n .to('.card', { rotation: 0, duration: 0.13, ease: 'power2.out' });", "pairsWith": [ "follow-through", "shake", "wobble" ], "engines": { "hyperframes": "transformOrigin을 set으로 미리 지정하고 감쇠 진폭을 순차 tween으로 쌓는다. paused 타임라인에서 seek해도 각도가 같다", "reelforge": "오브젝트 씬 브리프에 피벗 위치, 초기 회전, 감쇠 비, 총 길이를 파라미터로 싣는다", "scrolline": "스프링 대신 감쇠 tween 순서를 진행률 4구간으로 나눈다. scrub 중에는 2회 왕복만 남기고 ease-out을 쓴다" }, "prompts": { "claude": "<대상> 카드에 스윙을 넣어줘. transformOrigin은 좌상단, 6도에서 -3.6도, 2도, 0도 순으로 진폭이 줄게 하고 총 0.55초, 각 구간은 power2.inOut, 마지막은 power2.out이야. 시작은 등장 완료 직후 0.3초에 걸어줘.", "codex": "<파일>의 카드에 swing을 적용해. 피벗 top left, rotation 6 → -3.6 → 2 → 0, 구간 duration 0.12, 0.15, 0.15, 0.13초, ease power2.inOut(마지막 power2.out). 0.42초, 0.57초, 0.72초, 0.85초 시점을 캡처해 각도 부호가 번갈아 바뀌고 진폭이 줄어드는지 확인해." }, "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/01-anchor-transform.md#anchor-corner-swing", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/01-anchor-transform.md#pendulum-decay-swing", "license": "Apache-2.0" }, { "title": "animate-css/animate.css", "url": "https://github.com/animate-css/animate.css", "license": "Hippocratic-2.1" }, { "title": "gongnyang/awesome-html-scrolline-deck", "url": "gongnyang/awesome-html-scrolline-deck:references/techniques.md#tilt-card", "license": "MIT" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/animating-text/text-animation/animating-text.html", "license": "unknown" }, { "title": "airbnb/lottie-web", "url": "https://github.com/airbnb/lottie-web", "license": "MIT" }, { "title": "jschr/textillate", "url": "https://github.com/jschr/textillate", "license": "MIT" } ], "runtime": "gsap", "variants": [ "모서리 피벗", "상단 피벗", "진자 감쇠", "상단 매달림", "하단 피벗", "글자별 진자", "단어별 진자" ], "oneLinerEn": "An element hung from a fixed pivot rotates side to side, shrinking in amplitude until it rests.", "conveysEn": "Gives cards a hanging, force-driven physicality. Pendulum damping produces a natural stop.", "useWhenEn": [ "A hanging card or sign settles with a gentle sway after entering.", "Signal a notification or change with a swing." ], "promptsEn": { "claude": "Add a swing to the card in . Set transformOrigin to top left and step rotation 6, -3.6, 2, 0 degrees over 0.55 seconds, each segment power2.inOut and the last power2.out. Start at 0.3 seconds right after the entrance finishes.", "codex": "Apply swing to the card in : pivot top left, rotation 6 to -3.6 to 2 to 0, segment durations 0.12, 0.15, 0.15, 0.13s, ease power2.inOut (last power2.out). Capture at 0.42s, 0.57s, 0.72s and 0.85s and verify the angle sign alternates and the amplitude shrinks." }, "render": "planned", "files": {}, "no": "084" }, { "slug": "vignette-pulse", "family": "emphasis", "ko": "비네트 펄스", "en": "Vignette Pulse", "aka": [ "비네트 압력 펄스", "vignette-impact-pulse" ], "oneLiner": "화면 가장자리의 어둠이 순간 안쪽으로 조여졌다가 풀린다", "conveys": "충돌이나 결정 순간의 압력을 느끼게 한다. 중앙에 시선을 모으는 짧은 긴장을 만든다", "purposes": [ "강조", "분위기" ], "media": [ "숏폼", "설명 영상", "제품 시연" ], "level": "기본", "useWhen": [ "충격이나 중요한 발표 순간에 시선을 중앙으로 모을 때", "카운트다운이 끝나는 순간의 긴장을 표현할 때" ], "goodExample": "비네트가 opacity 0.4, scale 0.9까지 100ms에 조여졌다 120ms에 풀려 총 220ms에 끝난다", "badExample": "opacity 0.9로 화면이 검게 가려지거나 1초 이상 길게 유지되어 화면이 어둡게 눌린다", "avoid": [ "opacity 0.6 초과 금지", "한 컷에 한 번만 쓴다" ], "params": [ { "name": "opacity", "default": "0.4", "range": "0.25~0.55", "note": "최대 어둠" }, { "name": "scale", "default": "0.9", "range": "0.85~0.95", "note": "조이는 정도" }, { "name": "총 시간", "default": "220ms", "range": "150~300ms", "note": "조임 100 풀림 120" }, { "name": "색", "default": "#000", "range": "", "note": "브랜드 어두운 색도 가능" } ], "ease": "power2.out", "snippet": "tl.fromTo('.vignette', { opacity: 0, scale: 1.15 }, { opacity: 0.4, scale: 0.9, duration: 0.1, ease: 'power2.out' }, 1.2)\n .to('.vignette', { opacity: 0, scale: 1.15, duration: 0.12, ease: 'power2.in' });\n// .vignette: radial-gradient(transparent 55%, #000 100%) 전면 오버레이", "pairsWith": [ "radial-speed-lines", "screen-shake", "zoom-flash" ], "engines": { "hyperframes": "오버레이 div 하나에 radial-gradient를 두고 opacity와 scale만 tween한다. 220ms는 6.6프레임이므로 30fps에서 7프레임으로 잡는다", "reelforge": "브리프에 비네트 색, opacity, scale, 시작 시각을 싣는다", "scrolline": "진행률의 2~3% 구간에 걸쳐 재생한다. scrub 방향이 뒤집혀도 0으로 돌아와 잔상이 없다" }, "prompts": { "claude": "<대상> 장면에 비네트 펄스를 넣어줘. 전면 오버레이에 radial-gradient(transparent 55%, #000 100%)를 두고 1.2초에 opacity 0.4, scale 0.9로 0.1초 조였다가 0.12초에 풀어 0으로 돌아오게 해.", "codex": "<파일>에 vignette pulse를 추가해. 오버레이 opacity 0에서 0.4, scale 1.15에서 0.9로 0.1초, 이어서 0.12초에 원상 복귀, 시작 1.2초. 1.2초, 1.3초, 1.5초를 캡처해 1.3초에 가장자리가 가장 어둡고 1.5초에는 완전히 사라지는지 확인해." }, "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/07-stylize-time.md#vignette-impact-pulse", "license": "Apache-2.0" } ], "runtime": "gsap", "variants": [ "충돌 압력", "카운트 완료" ], "oneLinerEn": "The dark edge of the frame tightens inward for an instant and then releases.", "conveysEn": "Conveys the pressure of impact or a decisive moment, with a brief tension that focuses the eye on the center.", "useWhenEn": [ "Focus attention on the center at an impact or key announcement.", "Express tension at the end of a countdown." ], "promptsEn": { "claude": "Add a vignette pulse to the scene in . Put a full overlay with radial-gradient(transparent 55%, #000 100%) and at 1.2 seconds tighten it to opacity 0.4 and scale 0.9 over 0.1 seconds, then release over 0.12 seconds back to 0.", "codex": "Add a vignette pulse to : overlay opacity 0 to 0.4 and scale 1.15 to 0.9 over 0.1s, then back over 0.12s, starting at 1.2s. Capture at 1.2s, 1.3s and 1.5s and verify the edges are darkest at 1.3s and fully gone at 1.5s." }, "render": "planned", "files": {}, "no": "085" }, { "slug": "text-color-wipe", "family": "emphasis", "ko": "글자 색 와이프", "en": "Text Color Wipe", "aka": [ "Progressive text fill", "진행률 텍스트 채움", "Text Color Wipe Replacement", "텍스트 색 와이프 교체", "Colorama soft wipe", "색 번짐 와이프" ], "oneLiner": "옅은 글자 위로 밝은 글자가 한쪽에서부터 채워지는 효과", "conveys": "읽는 위치와 진행 정도를 동시에 알린다. 문구가 곧 진행 표시가 된다", "purposes": [ "순서·흐름", "강조" ], "media": [ "설명 영상", "스크롤덱", "웹 UI" ], "level": "기본", "useWhen": [ "로딩·진행률 문구를 글자 채움으로 보여줄 때", "긴 문장을 스크롤에 맞춰 읽어 나갈 때" ], "goodExample": "옅은 회색 문장 위에 흰 문장이 왼쪽에서 2초 동안 0에서 100%로 채워지고 경계가 200ms로 부드럽다", "badExample": "두 레이어의 글꼴·줄바꿈이 달라 채워지는 글자가 아래 글자와 어긋난다", "avoid": [ "두 레이어는 같은 글꼴·크기·줄바꿈을 쓴다", "옅은 층 opacity 0.25 미만 금지(아직 안 읽은 글자가 보이지 않음)", "여러 줄은 줄 단위로 나눠 채운다" ], "params": [ { "name": "채움 시간", "default": "2s", "range": "1.2~3s", "note": "0→100%" }, { "name": "경계 부드러움", "default": "200ms", "range": "0~300ms", "note": "clip 가장자리" }, { "name": "옅은 층 opacity", "default": "0.3", "range": "0.2~0.4", "note": "읽히는 하한" }, { "name": "방향", "default": "좌→우", "range": "좌우/상하", "note": "읽는 방향" } ], "ease": "none", "snippet": "/* .base(옅은 글자) 위에 .fill(밝은 글자, 같은 텍스트) */\ntl.fromTo('.fill', { clipPath: 'inset(0 100% 0 0)' },\n { clipPath: 'inset(0 0% 0 0)', duration: 2, ease: 'none' }, 0);", "pairsWith": [ "highlight-sweep", "karaoke-caption", "clip-reveal" ], "engines": { "hyperframes": "clip-path inset을 tween한다. 두 레이어를 같은 컨테이너에 absolute로 겹치고 폰트를 preload한다", "reelforge": "타이포 씬에 문구, 채움 방향, 진행률 소스(시간 또는 음성 타이밍)를 실어 보낸다", "scrolline": "clip 값을 진행률 0..1에 선형 매핑하는 것이 이 효과의 정석이다. 스크롤 위치가 곧 읽은 위치가 된다" }, "prompts": { "claude": "<대상> 문장을 색 와이프로 채워줘. 옅은 회색(opacity 0.3) 문장 위에 같은 글꼴·크기의 흰 문장을 겹치고, clip-path inset(0 100% 0 0)에서 inset(0 0% 0 0)까지 2초 동안 이징 none으로 채워. 경계는 200ms 정도로 부드럽게. 여러 줄이면 줄마다 순서대로 채워.", "codex": "<파일>의 문장에 두 겹 텍스트를 만들고 상단층 clipPath를 2초 tween해. 0초, 1초, 2초 캡처로 채움이 0%, 50%, 100%이고 두 레이어 글자가 픽셀 단위로 겹치는지 확인해." }, "sources": [ { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-loading-fill-text", "license": "unknown" }, { "title": "codrops/TextStylesHoverEffects", "url": "https://github.com/codrops/TextStylesHoverEffects", "license": "unknown" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/colorama-wipe/registry-item.json", "license": "Apache-2.0" } ], "runtime": "css", "variants": [ "진행률 채움", "색 번짐 와이프", "좌우 채움", "상하 채움", "스크롤 읽기", "단색 교체", "윤곽에서 채움 색 교체" ], "oneLinerEn": "Bright text fills over dim text from one side.", "conveysEn": "Communicates reading position and progress at once; the phrase itself becomes the progress indicator.", "useWhenEn": [ "Show a loading or progress phrase as a letter fill.", "Read a long sentence along with scrolling." ], "promptsEn": { "claude": "Fill the sentence in with a color wipe. Overlay a white copy of the same font and size over a dim gray copy (opacity 0.3) and animate clip-path from inset(0 100% 0 0) to inset(0 0% 0 0) over 2 seconds with ease none, softening the edge by about 200ms. For multiple lines, fill line by line in order.", "codex": "In build the two-layer text and tween the top layer clipPath over 2s. Capture at 0s, 1s and 2s to confirm the fill is at 0%, 50% and 100%, and check that both layers overlap pixel for pixel." }, "render": "planned", "files": {}, "no": "086" }, { "slug": "letter-drop-pile", "family": "type", "ko": "글자 낙하와 쌓임", "en": "Letter Drop Pile", "aka": [ "Falling Letters", "글자 낙하", "Physical Letter Drop", "물리 글자 낙하" ], "oneLiner": "문장의 글자들이 떨어져 바닥에 부딪히고 쌓이는 효과", "conveys": "문구가 무너지거나 해체되는 사건을 무게감 있게 보여준다. 쌓인 글자 더미가 결과를 남긴다", "purposes": [ "주목 끌기", "피드백" ], "media": [ "숏폼", "설명 영상" ], "level": "고급", "useWhen": [ "'무너진다, 쏟아진다'는 뜻의 문구를 시각으로 뒷받침할 때", "오류·실패 장면에서 텍스트를 해체할 때" ], "goodExample": "'FALL' 네 글자가 0.06초 간격으로 떨어져 900px/s² 중력, 반발 0.3으로 두세 번 튀고 서로 겹치지 않은 채 쌓인다", "badExample": "글자가 바닥을 뚫거나 화면 밖으로 튕겨 나가고 매번 결과가 달라진다", "avoid": [ "물리는 고정 스텝(1/60초)과 시드로만 계산한다", "글자 수 12 초과 금지", "읽어야 하는 핵심 문구에는 사용하지 않는다" ], "params": [ { "name": "중력", "default": "900px/s²", "range": "600~1400", "note": "낙하 가속도" }, { "name": "반발", "default": "0.3", "range": "0.2~0.45", "note": "튕김 크기" }, { "name": "낙하 간격", "default": "60ms", "range": "30~120ms", "note": "글자별 출발" }, { "name": "초기 회전", "default": "±30deg", "range": "±10~±45", "note": "도착 자세" } ], "ease": "bounce.out", "snippet": "const floor = 900;\nchars.forEach((el, i) => {\n const rot = (i % 2 ? 1 : -1) * (10 + (i * 7) % 20);\n tl.fromTo(el, { y: -600, rotation: 0 }, { y: floor - el.offsetTop, rotation: rot,\n duration: 0.9, ease: 'bounce.out' }, i * 0.06);\n});", "pairsWith": [ "bounce-landing", "text-particle-dissolve", "stagger" ], "engines": { "hyperframes": "엄밀한 충돌 대신 bounce.out으로 근사하고 회전값은 인덱스 기반 결정식으로 둔다. 진짜 물리는 고정 스텝으로 사전 베이크해 배열로 seek한다", "reelforge": "물리 씬 브리프에 중력, 반발, 시드를 싣고 캔버스 시뮬레이션은 프레임별 결과를 미리 굽는다", "scrolline": "낙하 진행을 진행률 0..1에 베이크된 궤적으로 매핑한다. 되감기에서 물리가 다시 계산되지 않도록 시뮬레이션은 시간 기반이 아닌 인덱스 기반으로 한다" }, "prompts": { "claude": "<대상> 문구 '<문구>'를 글자(한글은 음절) 단위로 나눠 위에서 떨어뜨려줘. 글자마다 0.06초 간격, 화면 위 -600px에서 바닥 y까지 0.9초 bounce.out으로 떨어지고, 인덱스에 따라 ±10~30도로 기울어져 쌓이게 해. 난수는 쓰지 말고 결정식으로 계산해. 마지막 글자 도착 뒤 1초 정지.", "codex": "<파일>의 문구를 span으로 분리해 i*0.06초 간격의 tl.fromTo(y -600→바닥, rotation 0→±(10~30), 0.9s, bounce.out)로 낙하시켜. Math.random 금지. 0.5초와 2.0초를 캡처해 글자가 바닥 아래로 뚫지 않고 서로 겹치지 않는지 확인해." }, "sources": [ { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" }, { "title": "Aqro/Physics-menu-threejs-cannonjs", "url": "https://github.com/Aqro/Physics-menu-threejs-cannonjs", "license": "unknown" } ], "runtime": "canvas", "variants": [ "2D 글자 낙하", "3D 물리 낙하", "FallingText", "낱글자 투하", "문구 붕괴", "글자 더미" ], "oneLinerEn": "The letters of a sentence detach, fall, hit the ground, and pile up.", "conveysEn": "Shows a phrase breaking apart with weight, and leaves a heap of letters as the result.", "useWhenEn": [ "Back up a phrase that means collapse or spill with a visual.", "Break text apart in an error or failure scene." ], "promptsEn": { "claude": "Drop the phrase '' in letter by letter (per syllable for Korean). 0.06 seconds apart, each falls from -600px to the floor in 0.9 seconds with bounce.out and lands tilted between 10 and 30 degrees by index. No random values, use deterministic formulas. Hold 1 second after the last letter lands.", "codex": "In split the phrase into spans and drop each with tl.fromTo (y -600 to floor, rotation 0 to +/-(10 to 30), 0.9s, bounce.out) at i*0.06. No Math.random. Capture at 0.5s and 2.0s and verify no letter passes below the floor or overlaps another." }, "render": "planned", "files": {}, "no": "087" }, { "slug": "char-stagger", "family": "type", "ko": "글자별 스태거", "en": "Per-character Rise", "aka": [ "글자 상승", "Character stagger", "글자 시간차 등장", "글자 순차 등장", "Text Scale Cascade", "글자 스케일 캐스케이드", "scale-jump-stairstep", "word-pop" ], "oneLiner": "글자들이 아래에서 위로 시간차를 두고 올라오며 불투명해지는 효과", "conveys": "제목의 읽는 방향과 순차적 등장", "purposes": [ "주목 끌기", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "짧은 제목을 읽는 순서대로 등장시킬 때", "문장이 한 덩어리로 나타나는 것보다 리듬을 주고 싶을 때" ], "goodExample": "각 글자가 아래 64px에서 0.035초 간격으로 출발해 같은 기준선에 정렬된다", "badExample": "글자 간격이 너무 커 문장이 서로 떨어진 덩어리로 읽힌다", "avoid": [ "긴 본문을 글자마다 움직이지 않는다", "출발 거리와 간격을 동시에 크게 늘리지 않는다" ], "params": [ { "name": "글자 간격", "default": "0.035s", "range": "0.02~0.06s", "note": "공백 span을 포함한다" }, { "name": "상승 거리", "default": "64px", "range": "32~80px", "note": "양의 y에서 0으로 이동한다" }, { "name": "글자 지속", "default": "0.62s", "range": "0.40~0.80s", "note": "불투명도와 이동을 같이 끝낸다" }, { "name": "시작 시각", "default": "0.30s", "range": "0.20~0.40s", "note": "첫 글자 출발" } ], "ease": "power3.out", "snippet": "tl.fromTo('.char', {y:64, opacity:0}, {\n y:0, opacity:1, duration:.62,\n stagger:.035, ease:'power3.out'\n}, .3);", "pairsWith": [ "stagger", "mask-reveal", "typewriter" ], "engines": { "hyperframes": "단일 paused GSAP 타임라인으로 구현하고 프레임 시각에서 seek한다. 폰트 로딩 후 고정한 글자 배치를 사용한다.", "reelforge": "텍스트 씬 안의 span에 이 효과의 시간과 간격을 적용하고 마지막 완성 상태를 0.6초 이상 유지한다.", "scrolline": "스크롤 진행률을 0~3초 타임라인 시각으로 매핑하고 역방향 seek에서도 같은 문자와 상태를 복원한다." }, "prompts": { "claude": "<대상>에 이 효과를 적용하라. AI는 다음 말을 고른다를 공백을 보존한 개별 span으로 나누어라. 0.30초부터 글자마다 0.035초 간격으로 y 64px와 opacity 0에서 y 0과 opacity 1로 0.62초 동안 power3.out으로 상승시켜라. 3초 타임라인 안에서 마지막 0.6초는 완성 상태로 정지하라.", "codex": "<파일>의 텍스트 장면에 적용하라. AI는 다음 말을 고른다를 공백을 보존한 개별 span으로 나누어라. 0.30초부터 글자마다 0.035초 간격으로 y 64px와 opacity 0에서 y 0과 opacity 1로 0.62초 동안 power3.out으로 상승시켜라. 0.43초에 글자별 높이와 불투명도가 다른지, 0.75초에 앞 글자가 먼저 도착하는지, 2.7초에 전체 정렬된 문장이 유지되는지 확인하라." }, "sources": [ { "title": "GSAP Timeline 공식 문서", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 개념 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/bottom-up-letters/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/top-down-letters/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/logo-brand-close/registry-item.json", "license": "Apache-2.0" }, { "title": "pixel-point/animate-text", "url": "https://raw.githubusercontent.com/pixel-point/animate-text/master/skills/animate-text/assets/specs/per-character-rise.json", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/char-stagger/index.html", "mp4": "effects/char-stagger/clip.mp4", "gif": "effects/char-stagger/preview.gif", "poster": "effects/char-stagger/poster.jpg" }, "no": "088", "oneLinerEn": "Characters rise from below and fade in with a staggered delay.", "conveysEn": "Communicates the reading direction and sequential entrance of a title.", "useWhenEn": [ "Reveal a short title in reading order.", "Add rhythm to a sentence's entrance." ], "promptsEn": { "claude": "Apply this effect to . Split \"AI picks the next word\" into individual spans while preserving spaces. Starting at 0.30 seconds, stagger characters by 0.035 seconds and animate from y 64px and opacity 0 to y 0 and opacity 1 over 0.62 seconds with power3.out. Within a 3-second timeline, hold the completed state for the final 0.6 seconds.", "codex": "Apply this to the text scene in . Split \"AI picks the next word\" into individual spans while preserving spaces. Starting at 0.30 seconds, stagger characters by 0.035 seconds and animate from y 64px and opacity 0 to y 0 and opacity 1 over 0.62 seconds with power3.out. Verify different character heights and opacities at 0.43 seconds, that earlier characters arrive first at 0.75 seconds, and that the fully aligned sentence holds at 2.7 seconds." }, "variants": [ "animate-text/per-character-rise", "animate-text/bottom-up-letters", "animate-text/top-down-letters", "animate-text/stagger-from-center", "animate-text/stagger-from-edges", "상승", "하강", "중앙 공개", "양끝 공개", "로고 글자 조립" ] }, { "slug": "highlight-sweep", "family": "type", "ko": "하이라이트 스윕", "en": "Highlight Sweep", "aka": [ "형광펜 강조", "Marker sweep", "Highlight sweep", "하이라이트 쓸기", "빛 하이라이트 스윕", "light-sweep-sheen", "gradient-text-sweep", "chrome-sweep" ], "oneLiner": "핵심 구절 뒤의 형광펜 띠를 왼쪽에서 오른쪽으로 칠하는 효과", "conveys": "문장 안에서 주목할 구절을 분명하게 지정한다", "purposes": [ "강조", "설명" ], "media": [ "설명 영상", "스크롤덱", "발표" ], "level": "기본", "useWhen": [ "문장에서 핵심 구절을 짚을 때", "이미 읽힌 문장의 의미를 강조할 때" ], "goodExample": "AI는 다음 말을 고른다에서 다음 말 뒤에만 연주홍 띠를 칠한다", "badExample": "문장 전체를 진한 색으로 덮어 글자가 읽히지 않는다", "avoid": [ "여러 구절을 동시에 칠하지 않는다", "텍스트 위로 띠를 덮지 않는다" ], "params": [ { "name": "지속", "default": "1.35s", "range": "0.7~1.5s", "note": "칠하는 방향을 읽을 시간" }, { "name": "시작", "default": "0.3s", "range": "0.2~0.5s", "note": "문장을 먼저 보여준다" }, { "name": "띠 높이", "default": "66px", "range": "40~75px", "note": "84px 제목의 아래쪽을 받친다" }, { "name": "좌우 여백", "default": "8px", "range": "4~12px", "note": "핵심어 양끝 여유" } ], "ease": "power1.inOut", "snippet": "const tl = Motion.timeline();\ntl.fromTo('.mark', {scaleX:0}, {scaleX:1, duration:1.35, ease:'power1.inOut'}, 0.3);\ntl.to('.lead', {opacity:1, duration:0.25}, 1.85);\nMotion.ready();", "pairsWith": [ "word-emphasis", "underline-draw", "annotation-callout" ], "engines": { "hyperframes": "핵심어 내부에 띠를 절대 배치하고 transform-origin을 왼쪽에 두어 scaleX만 움직인다", "reelforge": "강조 비트에서 텍스트는 고정하고 뒤쪽 띠의 가로 배율을 1.35초 동안 늘린다", "scrolline": "핵심어가 뷰포트에 들어올 때 띠의 scaleX를 스크롤 진행률에 연결한다" }, "prompts": { "claude": "<대상>의 문장 중 다음 말 뒤에 하이라이트 스윕을 넣어줘. 글자는 84px로 고정하고 연주홍 띠는 높이 66px, 좌우 여백 8px, transform-origin 0 50%로 배치해. 0.3초부터 scaleX 0에서 1로 1.35초 동안 power1.inOut으로 칠하고 3초까지 유지해.", "codex": "<파일>의 핵심어 span 뒤에 .mark를 배치하고 isolation과 z-index로 글자가 띠 위에 보이게 해. 0.3초부터 duration 1.35초, ease power1.inOut으로 scaleX 0에서 1로 움직여. 0.23초, 0.73초, 1.23초, 2.9초를 캡처해 띠의 왼쪽 고정, 오른쪽 확장, 글자 가독성과 완성 홀드를 확인해." }, "sources": [ { "title": "GSAP CSSPlugin", "url": "https://gsap.com/docs/v3/GSAP/CorePlugins/CSS/", "license": "공식 문서 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/marker-highlight/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/inline-highlight/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/caption-highlight/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/mk-callout-highlight/registry-item.json", "license": "Apache-2.0" }, { "title": "Vox", "url": "https://www.youtube.com/watch?v=kIID5FDi2JQ", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/highlight-sweep/index.html", "mp4": "effects/highlight-sweep/clip.mp4", "gif": "effects/highlight-sweep/preview.gif", "poster": "effects/highlight-sweep/poster.jpg" }, "no": "089", "oneLinerEn": "A highlighter band sweeps from left to right behind a key phrase.", "conveysEn": "Clearly identifies the phrase that deserves attention within a sentence.", "useWhenEn": [ "Call out a key phrase in a sentence.", "Emphasize the meaning of a sentence that has already been read." ], "promptsEn": { "claude": "Add a highlight sweep behind \"next word\" in the sentence in . Keep the text at 84px. Place a pale vermilion band with height 66px, horizontal padding 8px, and transform-origin 0 50%. Starting at 0.3 seconds, animate scaleX from 0 to 1 over 1.35 seconds with power1.inOut, and hold until 3 seconds.", "codex": "Place .mark behind the key phrase span in , using isolation and z-index to keep the text above the band. Starting at 0.3 seconds, animate scaleX from 0 to 1 with duration 1.35 seconds and ease power1.inOut. Capture at 0.23, 0.73, 1.23, and 2.9 seconds to check the fixed left edge, rightward expansion, text legibility, and completed hold." }, "variants": [ "금속 광택", "카드 셰인", "글자 그라데이션", "단어별 빛", "단어별", "문장별", "색 채움 유지", "Animated Shiny Text", "ShinyText", "Text Shimmer" ] }, { "slug": "kinetic-beats", "family": "type", "ko": "키네틱 비트", "en": "Kinetic Beats", "aka": [ "단어 비트", "Kinetic typography", "Kinetic beats", "박자 타이포", "비트 타이포", "kinetic-beat-slam", "Word Grid Burst", "단어 격자 축적" ], "oneLiner": "단어를 일정한 박자마다 중앙에 크게 찍고 빠르게 정착시키는 효과", "conveys": "세 단계의 리듬과 마지막 행동의 중요성을 전한다", "purposes": [ "주목 끌기", "순서·흐름", "강조" ], "media": [ "숏폼", "설명 영상", "발표" ], "level": "기본", "useWhen": [ "짧은 동사로 순서를 압축할 때", "마지막 핵심 행동에 시선을 모을 때" ], "goodExample": "읽고, 매기고, 고른다를 0.5초 간격으로 중앙에 찍고 마지막 단어만 주홍으로 남긴다", "badExample": "긴 문장을 빠르게 교체해 읽기 전에 사라지게 한다", "avoid": [ "긴 문장에는 쓰지 않는다", "교체 순간에 두 단어를 겹치지 않는다", "모든 단어에 강조색을 넣지 않는다" ], "params": [ { "name": "비트 간격", "default": "0.5s", "range": "0.4~0.8s", "note": "단어별 출발 간격" }, { "name": "시작 배율", "default": "1.4", "range": "1.2~1.5", "note": "큰 단어가 빠르게 정착한다" }, { "name": "정착 시간", "default": "0.25s", "range": "0.18~0.3s", "note": "다음 비트 전 읽기 시간을 남긴다" }, { "name": "시작", "default": "0.3s", "range": "0.2~0.4s", "note": "빈 준비 구간" } ], "ease": "power3.out", "snippet": "['#read','#score','#choose'].forEach((s,i) => {\n const at = 0.3 + i * 0.5;\n tl.set(s, {opacity:1, scale:1.4}, at);\n tl.to(s, {scale:1, duration:0.25, ease:'power3.out'}, at);\n if(i < 2) tl.set(s, {opacity:0}, at + 0.5);\n});", "pairsWith": [ "timing-spacing", "word-emphasis", "mask-reveal" ], "engines": { "hyperframes": "한 타임라인에서 단어의 등장과 교체를 0.5초 간격으로 예약하고 scale을 1.4에서 1로 줄인다", "reelforge": "세 단어를 세 비트로 나누고 마지막 비트에 주홍색과 긴 홀드를 배정한다", "scrolline": "단어 교체를 세 진행률 구간으로 나누고 각 구간 시작에서 확대 후 정착시킨다" }, "prompts": { "claude": "<대상>의 세 단어 읽고, 매기고, 고른다를 화면 중앙에 한 단어씩 보여줘. 0.3초, 0.8초, 1.3초에 opacity 1과 scale 1.4로 찍고 0.25초 동안 power3.out으로 scale 1에 정착시켜. 다음 비트에서 앞 단어를 즉시 숨기고 마지막 고른다만 주홍으로 3초까지 유지해.", "codex": "<파일>의 .scene 중앙에 세 단어를 각각 독립된 span으로 배치해. 0.3초부터 0.5초 간격으로 opacity 1, scale 1.4를 설정하고 0.25초 동안 power3.out으로 scale 1에 정착시켜. 0.3초와 0.43초 캡처로 축소를, 0.73초와 1.23초로 단어 교체를, 2.9초로 마지막 주홍 단어 홀드와 겹침 없음을 확인해." }, "sources": [ { "title": "GSAP Timeline", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/caption-kinetic-slam/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/headline-slam/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/shutter-slam/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/logo-sting/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/kinetic-beats/index.html", "mp4": "effects/kinetic-beats/clip.mp4", "gif": "effects/kinetic-beats/preview.gif", "poster": "effects/kinetic-beats/poster.jpg" }, "no": "090", "oneLinerEn": "Words hit the center at a large scale on regular beats and quickly settle.", "conveysEn": "Communicates a three-step rhythm and emphasizes the final action.", "useWhenEn": [ "Compress a sequence into short verbs.", "Focus attention on the final key action." ], "promptsEn": { "claude": "Show the three words \"Read\", \"Score\", and \"Pick\" one at a time in the center of . At 0.3, 0.8, and 1.3 seconds, make each word hit with opacity 1 and scale 1.4, then settle to scale 1 over 0.25 seconds with power3.out. Immediately hide the previous word on the next beat. Keep only the final word \"Pick\" in vermilion until 3 seconds.", "codex": "Place the three words \"Read\", \"Score\", and \"Pick\" in separate spans at the center of .scene in . Starting at 0.3 seconds with 0.5-second intervals, set opacity 1 and scale 1.4, then settle to scale 1 over 0.25 seconds with power3.out. Capture at 0.3 and 0.43 seconds to check the scale reduction, at 0.73 and 1.23 seconds to check word replacement, and at 2.9 seconds to verify the final vermilion word holds without overlap." }, "variants": [ "animate-text/shared-axis-y", "구절 슬램", "단어 하드컷", "1프레임 burn-in", "전면 타이틀", "파형식 단어 컷", "스케일 슬램", "문구 교체", "비트 카운터", "kinetic-titles" ] }, { "slug": "mask-reveal", "family": "type", "ko": "마스크 리빌", "en": "Mask Reveal", "aka": [ "줄 마스크", "Line reveal", "Directional Menu Image Reveal", "메뉴 이미지 방향 리빌", "Mask text reveal", "마스크 글자 공개", "Line mask typography", "줄 마스크 타이포" ], "oneLiner": "숨겨진 창 아래의 제목을 위로 올려 줄마다 드러내는 효과", "conveys": "문장이 차례로 등장하며 제목의 시작을 알린다", "purposes": [ "주목 끌기", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "기본", "useWhen": [ "두 줄 제목을 순서대로 소개할 때", "문단 시작에 시선을 모을 때" ], "goodExample": "AI는 다음 말을 고른다를 두 줄로 나눠 0.35초 간격으로 올린다", "badExample": "마스크 없이 글자를 움직여 다음 줄과 겹친다", "avoid": [ "본문 전체에 반복 적용하지 않는다", "줄 높이보다 작은 창으로 글자를 자르지 않는다" ], "params": [ { "name": "시작 이동", "default": "110%", "range": "100~120%", "note": "줄 높이를 기준으로 완전히 숨긴다" }, { "name": "지속", "default": "1.05s", "range": "0.6~1.2s", "note": "한 줄이 올라오는 시간" }, { "name": "줄 간격", "default": "0.35s", "range": "0.15~0.45s", "note": "두 번째 줄의 출발 지연" }, { "name": "시작", "default": "0.3s", "range": "0.2~0.4s", "note": "준비 구간" } ], "ease": "power3.out", "snippet": "const tl = Motion.timeline();\ntl.fromTo('.line', {yPercent:110}, {yPercent:0, duration:1.05, stagger:0.35, ease:'power3.out'}, 0.3);\ntl.to('.lead', {opacity:1, duration:0.25}, 1.8);\nMotion.ready();", "pairsWith": [ "stagger", "char-stagger", "underline-draw" ], "engines": { "hyperframes": "줄마다 overflow hidden 창을 두고 내부 span의 yPercent를 하나의 타임라인으로 움직인다", "reelforge": "제목 비트의 줄마다 마스크를 분리하고 0.35초 출발 간격을 유지한다", "scrolline": "스크롤 진행률에 두 줄의 yPercent를 연결하고 완료 이후 정지 구간을 둔다" }, "prompts": { "claude": "<대상>의 두 줄 제목에 마스크 리빌을 적용해. 각 줄을 높이 116px의 overflow hidden 창에 넣고 내부 글자를 0.3초부터 yPercent 110에서 0으로 1.05초 동안 power3.out으로 이동해. 두 번째 줄은 0.35초 늦게 시작하고 3초까지 완성 상태를 유지해.", "codex": "<파일>의 제목 영역에 줄마다 overflow hidden 창과 내부 .line을 배치해. 0.3초부터 yPercent 110에서 0으로 duration 1.05초, stagger 0.35초, ease power3.out을 적용해. 0.23초, 0.73초, 1.23초, 2.9초를 캡처해 첫 준비 상태, 줄별 마스크 경계, 완성 홀드와 글자 잘림을 확인해." }, "sources": [ { "title": "GSAP fromTo()", "url": "https://gsap.com/docs/v3/GSAP/Timeline/fromTo()", "license": "공식 문서 참조" }, { "title": "tympanus.net/codrops", "url": "https://tympanus.net/codrops/2020/07/01/creating-a-menu-image-animation-on-hover/", "license": "unknown" }, { "title": "ui.aceternity.com", "url": "https://ui.aceternity.com/components/direction-aware-hover", "license": "unknown" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/caption-clip-wipe/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/line-by-line-slide/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/svg-mask-reveal/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/testimonial-card/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/mask-reveal/index.html", "mp4": "effects/mask-reveal/clip.mp4", "gif": "effects/mask-reveal/preview.gif", "poster": "effects/mask-reveal/poster.jpg" }, "no": "091", "oneLinerEn": "A title rises into clipped windows to reveal one line at a time.", "conveysEn": "Introduces a title through a sequential reveal of its lines.", "useWhenEn": [ "Introduce a two-line title in sequence.", "Focus attention on the start of a paragraph." ], "promptsEn": { "claude": "Apply a mask reveal to the two-line title in . Place each line inside a 116px-high overflow hidden window. Starting at 0.3 seconds, animate the inner text from yPercent 110 to 0 over 1.05 seconds with power3.out. Start the second line 0.35 seconds later and hold the completed state until 3 seconds.", "codex": "In the title area of , give each line an overflow hidden window and an inner .line element. Starting at 0.3 seconds, animate yPercent from 110 to 0 with duration 1.05 seconds, stagger 0.35 seconds, and ease power3.out. Capture at 0.23, 0.73, 1.23, and 2.9 seconds to check the initial state, per-line mask boundaries, completed hold, and text clipping." }, "variants": [ "메뉴 이미지 리빌", "진입 방향별 리빌", "리빌 후 포인터 추종", "animate-text/mask-reveal-up", "animate-text/line-by-line-slide", "단어 와이프", "줄 공개", "좌우 줄 슬라이드", "SVG 글자 마스크", "위에서 내려옴" ] }, { "slug": "text-scramble", "family": "type", "ko": "스크램블", "en": "Text Scramble", "aka": [ "텍스트 해독", "문자 셔플", "Text scramble", "글자 해독", "Matrix decode", "Binary decrypt", "문자 디코드", "hacker-flip-3d" ], "oneLiner": "자리마다 기호가 바뀌다가 왼쪽부터 정답 글자로 고정되는 효과", "conveys": "불확실한 문자 배열이 읽을 수 있는 문장으로 수렴하는 과정", "purposes": [ "주목 끌기", "분위기" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "짧은 제목에 해독되는 느낌을 줄 때", "정답이나 결과가 점차 확정되는 과정을 연출할 때" ], "goodExample": "고정 폭 자리의 기호가 0.05초마다 바뀌고 왼쪽 글자부터 원문으로 고정된다", "badExample": "매 프레임 새 난수를 생성해 seek할 때 같은 시점의 기호가 달라진다", "avoid": [ "난수를 업데이트 함수 안에서 생성하지 않는다", "기호 폭 차이로 문장의 자리 위치가 흔들리지 않게 한다" ], "params": [ { "name": "기호 갱신 간격", "default": "0.05s", "range": "0.04~0.10s", "note": "미리 생성한 표의 행을 선택한다" }, { "name": "글자 고정 간격", "default": "0.085s", "range": "0.06~0.12s", "note": "공백 자리는 보존한다" }, { "name": "첫 글자 고정", "default": "0.70s", "range": "0.50~0.90s", "note": "기호 순환을 먼저 보여준다" }, { "name": "난수 시드", "default": "603", "range": "1~9999", "note": "동일 시점은 동일한 기호를 보여준다" } ], "ease": "none", "snippet": "const random = Motion.rand(603);\nconst table = Array.from({length:61}, () => text.map(c => c === ' ' ? ' ' : symbols[Math.floor(random()*symbols.length)]));\nconst state = {time:0};\ntl.to(state, {time:3, duration:3, ease:'none', onUpdate:paint}, 0);\n// paint는 표를 선택하고 왼쪽부터 정답으로 치환한다.\nconst tick = Math.min(60, Math.max(0, Math.floor((state.time-.3)/.05)));\nchars.forEach((c,i) => c.textContent = state.time >= .7+i*.085 ? text[i] : table[tick][i]);", "pairsWith": [ "typewriter", "next-token", "token-split" ], "engines": { "hyperframes": "단일 paused GSAP 타임라인으로 구현하고 프레임 시각에서 seek한다. 폰트 로딩 후 고정한 글자 배치를 사용한다.", "reelforge": "텍스트 씬 안의 span에 이 효과의 시간과 간격을 적용하고 마지막 완성 상태를 0.6초 이상 유지한다.", "scrolline": "스크롤 진행률을 0~3초 타임라인 시각으로 매핑하고 역방향 seek에서도 같은 문자와 상태를 복원한다." }, "prompts": { "claude": "<대상>에 이 효과를 적용하라. AI는 다음 말을 고른다의 글자 자리를 원문 폭으로 고정하고 공백을 보존하라. Motion.rand(603)으로 기호 표를 미리 생성해 0.30초부터 0.05초 간격으로 바꾸고 0.70초부터 왼쪽 글자를 0.085초 간격으로 정답에 고정하라. 3초 타임라인 안에서 마지막 0.6초는 완성 상태로 정지하라.", "codex": "<파일>의 텍스트 장면에 적용하라. AI는 다음 말을 고른다의 글자 자리를 원문 폭으로 고정하고 공백을 보존하라. Motion.rand(603)으로 기호 표를 미리 생성해 0.30초부터 0.05초 간격으로 바꾸고 0.70초부터 왼쪽 글자를 0.085초 간격으로 정답에 고정하라. 0.43초에 기호가 보이는지, 1.23초에 왼쪽은 정답이고 오른쪽은 기호인지, 2.7초에 완성 문장인지 확인하라. 1.23초를 앞뒤로 반복 seek해 기호가 동일한지도 확인하라." }, "sources": [ { "title": "GSAP Timeline 공식 문서", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 개념 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/matrix-decode/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/scramble-reveal/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/caption-matrix-decode/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/text-scramble/index.html", "mp4": "effects/text-scramble/clip.mp4", "gif": "effects/text-scramble/preview.gif", "poster": "effects/text-scramble/poster.jpg" }, "no": "092", "oneLinerEn": "Symbols cycle in each character position before resolving into the final text from left to right.", "conveysEn": "Shows an uncertain sequence of characters converging into a readable sentence.", "useWhenEn": [ "Give a short title a decoding effect.", "Reveal an answer or result as it becomes progressively resolved." ], "promptsEn": { "claude": "Apply this effect to . Fix each character slot in \"AI picks the next word\" to its original text width and preserve spaces. Precompute a symbol table with Motion.rand(603), cycle symbols at 0.05-second intervals starting at 0.30 seconds, and lock characters to the final text from left to right at 0.085-second intervals starting at 0.70 seconds. Within a 3-second timeline, hold the completed state for the final 0.6 seconds.", "codex": "Apply this to the text scene in . Fix each character slot in \"AI picks the next word\" to its original text width and preserve spaces. Precompute a symbol table with Motion.rand(603), cycle symbols at 0.05-second intervals starting at 0.30 seconds, and lock characters to the final text from left to right at 0.085-second intervals starting at 0.70 seconds. Verify symbols at 0.43 seconds, resolved text on the left and symbols on the right at 1.23 seconds, and the completed sentence at 2.7 seconds. Repeatedly seek forward and backward to 1.23 seconds to verify identical symbols." }, "variants": [ "이진 디코드", "해커 3D 플립", "순차 고정", "Hyper Text", "Encrypted Text", "DecryptedText", "ScrambledText", "TechText", "hyper-text", "text-scramble" ] }, { "slug": "typewriter", "family": "type", "ko": "타자기", "en": "Typewriter", "aka": [ "타이핑 효과", "Type-on", "타자기 리빌", "typewriter-vs-fade-sequence", "discrete-text-sequence", "gsap-effects", "typewriter-reveal", "타자기 텍스트" ], "oneLiner": "문장을 한 글자씩 일정 간격으로 드러내고 커서가 입력 위치를 따라가는 효과", "conveys": "글이 작성되는 과정과 순차적 입력", "purposes": [ "순서·흐름", "피드백" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "짧은 입력 문장이 만들어지는 과정을 보여줄 때", "대화형 UI에서 작성 중이라는 상태를 보여줄 때" ], "goodExample": "AI는 다음 말을 고른다가 0.06초 간격으로 완성되고 커서가 마지막 글자 뒤에 멈춘다", "badExample": "긴 본문 전체를 느리게 입력해 내용을 읽기까지 기다리게 한다", "avoid": [ "긴 본문에는 적용하지 않는다", "커서 점멸을 완성 홀드까지 계속하지 않는다" ], "params": [ { "name": "글자 간격", "default": "0.06s", "range": "0.04~0.10s", "note": "공백도 한 자리로 센다" }, { "name": "시작 시각", "default": "0.30s", "range": "0.20~0.40s", "note": "시작 상태를 먼저 보여준다" }, { "name": "커서 점멸 간격", "default": "0.24s", "range": "0.20~0.40s", "note": "2.4초 이후 켜진 채 정지한다" } ], "ease": "none", "snippet": "const state = {time:0};\ntl.to(state, {time:3, duration:3, ease:'none', onUpdate:paint}, 0);\n// paint에서 글자 수와 커서 위치를 매번 재계산한다.\nconst n = Math.min(chars.length, Math.max(0, Math.floor((state.time-.3+1e-7)/.06)+1));\nchars.forEach((c,i) => c.style.opacity = i < n ? 1 : 0);\ngsap.set('#caret', {x:-widths.slice(n).reduce((w,v)=>w+v,0)});", "pairsWith": [ "typing-input", "answer-stream", "char-stagger" ], "engines": { "hyperframes": "단일 paused GSAP 타임라인으로 구현하고 프레임 시각에서 seek한다. 폰트 로딩 후 고정한 글자 배치를 사용한다.", "reelforge": "텍스트 씬 안의 span에 이 효과의 시간과 간격을 적용하고 마지막 완성 상태를 0.6초 이상 유지한다.", "scrolline": "스크롤 진행률을 0~3초 타임라인 시각으로 매핑하고 역방향 seek에서도 같은 문자와 상태를 복원한다." }, "prompts": { "claude": "<대상>에 이 효과를 적용하라. AI는 다음 말을 고른다를 글자 span으로 나누고 0.30초부터 0.06초 간격으로 즉시 드러내라. 주홍 커서는 입력 끝을 따라가며 0.24초 간격으로 점멸하고 2.4초부터 켜진 채 유지하라. 3초 타임라인 안에서 마지막 0.6초는 완성 상태로 정지하라.", "codex": "<파일>의 텍스트 장면에 적용하라. AI는 다음 말을 고른다를 글자 span으로 나누고 0.30초부터 0.06초 간격으로 즉시 드러내라. 주홍 커서는 입력 끝을 따라가며 0.24초 간격으로 점멸하고 2.4초부터 켜진 채 유지하라. 0.23초에 입력 전 커서, 0.75초에 부분 입력, 2.7초에 완성 문장과 고정 커서를 확인하라." }, "sources": [ { "title": "GSAP Timeline 공식 문서", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 개념 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/typewriter/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/typed-prompt/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/code-typing/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/notes-typing/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/notes-reveal/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/typewriter/index.html", "mp4": "effects/typewriter/clip.mp4", "gif": "effects/typewriter/preview.gif", "poster": "effects/typewriter/poster.jpg" }, "no": "093", "oneLinerEn": "A sentence appears one character at a time at regular intervals while a cursor follows the insertion point.", "conveysEn": "Communicates the process of writing and sequential text input.", "useWhenEn": [ "Show a short input sentence being composed.", "Show a typing state in a conversational UI." ], "promptsEn": { "claude": "Apply this effect to . Split \"AI picks the next word\" into character spans and reveal each instantly at 0.06-second intervals starting at 0.30 seconds. Make a vermilion cursor follow the end of the input and blink at 0.24-second intervals, then remain on from 2.4 seconds. Within a 3-second timeline, hold the completed state for the final 0.6 seconds.", "codex": "Apply this to the text scene in . Split \"AI picks the next word\" into character spans and reveal each instantly at 0.06-second intervals starting at 0.30 seconds. Make a vermilion cursor follow the end of the input and blink at 0.24-second intervals, then remain on from 2.4 seconds. Check the cursor before typing at 0.23 seconds, partial input at 0.75 seconds, and the completed sentence with a steady cursor at 2.7 seconds." }, "variants": [ "animate-text/typewriter", "균일 타자", "사람 입력 리듬", "코드", "메모", "터미널", "문자 타이핑", "단어 타이핑", "백스페이스", "오타 수정" ] }, { "slug": "underline-draw", "family": "type", "ko": "밑줄 드로우", "en": "Underline Draw", "aka": [ "밑줄 그리기", "Stroke reveal", "Underline stroke draw", "Animated Underline Draw", "텍스트 밑줄 그리기", "Underline draw", "마커 밑줄 그리기", "underline-emphasis-sweep" ], "oneLiner": "핵심어 아래의 SVG 경로를 왼쪽부터 한 획으로 그리는 효과", "conveys": "읽어야 할 단어를 손으로 짚듯 강조한다", "purposes": [ "강조", "설명" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "핵심어를 한 번 짚을 때", "문장에 필기하는 듯한 강조를 넣을 때" ], "goodExample": "다음 말 아래의 조금 휜 주홍 선을 1.35초에 걸쳐 그린다", "badExample": "밑줄 전체를 갑자기 켜서 그리는 방향이 보이지 않는다", "avoid": [ "글자 획에 밑줄을 겹치지 않는다", "dashoffset을 정수로 반올림하지 않는다" ], "params": [ { "name": "지속", "default": "1.35s", "range": "0.7~1.5s", "note": "부분 획이 보이는 속도" }, { "name": "선 두께", "default": "6px", "range": "3~8px", "note": "84px 제목 기준" }, { "name": "정규화 길이", "default": "1", "range": "1", "note": "pathLength와 dasharray를 1로 맞춘다" }, { "name": "시작", "default": "0.3s", "range": "0.2~0.5s", "note": "단어를 먼저 읽는다" } ], "ease": "none", "snippet": "const tl = Motion.timeline();\ntl.fromTo('.underline path', {strokeDashoffset:1}, {strokeDashoffset:0, autoRound:false, duration:1.35, ease:'none'}, 0.3);\ntl.to('.lead', {opacity:1, duration:0.25}, 1.85);\nMotion.ready();", "pairsWith": [ "highlight-sweep", "word-emphasis", "line-draw" ], "engines": { "hyperframes": "pathLength 1인 SVG 경로에 dasharray 1을 설정하고 autoRound false로 dashoffset을 연속 보간한다", "reelforge": "핵심어 아래에 SVG 경로를 두고 필기 비트의 진행률을 dashoffset에 연결한다", "scrolline": "스크롤 진행률이 0에서 1로 갈 때 정규화 dashoffset을 1에서 0으로 줄인다" }, "prompts": { "claude": "<대상>의 다음 말 아래에 주홍 SVG 밑줄을 한 획으로 그려줘. pathLength 1, stroke-dasharray 1, 두께 6px, 둥근 끝과 약간 휜 경로를 사용해. 0.3초부터 strokeDashoffset 1에서 0으로 1.35초 동안 ease none, autoRound false로 움직이고 3초까지 완성 상태를 유지해.", "codex": "<파일>의 핵심어 span 아래에 viewBox 0 0 270 32의 SVG 경로 M 5 20 C 72 11 172 13 264 17을 배치해. pathLength와 dasharray는 1이고 0.3초부터 dashoffset 1에서 0으로 duration 1.35초, ease none, autoRound false를 적용해. 0.23초, 0.73초, 1.23초, 2.9초 캡처로 선 없음, 부분 획, 길이 증가, 완성 홀드를 확인해." }, "sources": [ { "title": "MDN SVG pathLength", "url": "https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/pathLength", "license": "공식 문서 참조" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Plugins/DrawSVGPlugin/", "license": "GSAP Standard License" }, { "title": "motiondivision/motion", "url": "https://motion.dev/docs/react-svg-animation", "license": "MIT" }, { "title": "codrops/LetterEffects", "url": "https://github.com/codrops/LetterEffects", "license": "unknown" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/hw-underline/registry-item.json", "license": "Apache-2.0" }, { "title": "IanLunn/Hover", "url": "https://github.com/IanLunn/Hover", "license": "MIT personal/open-source + paid commercial" }, { "title": "motion dictionary 3-type-data-ui.md#07. 밑줄 드로우 · Underline draw", "url": "", "license": "own" }, { "title": "3b1b/manim", "url": "https://github.com/3b1b/manim/blob/master/manimlib/animation/indication.py", "license": "MIT" } ], "runtime": "svg", "render": "done", "files": { "html": "effects/underline-draw/index.html", "mp4": "effects/underline-draw/clip.mp4", "gif": "effects/underline-draw/preview.gif", "poster": "effects/underline-draw/poster.jpg" }, "no": "094", "oneLinerEn": "An SVG path draws beneath a key phrase from left to right in a single stroke.", "conveysEn": "Emphasizes the words to read as if pointing to them by hand.", "useWhenEn": [ "Call attention to a key phrase once.", "Add a handwritten emphasis to a sentence." ], "promptsEn": { "claude": "Draw a vermilion SVG underline beneath \"next word\" in in a single stroke. Use pathLength 1, stroke-dasharray 1, a 6px stroke, rounded caps, and a slightly curved path. Starting at 0.3 seconds, animate strokeDashoffset from 1 to 0 over 1.35 seconds with ease none and autoRound false. Hold the completed state until 3 seconds.", "codex": "Place an SVG path with viewBox 0 0 270 32 and path M 5 20 C 72 11 172 13 264 17 beneath the key phrase span in . Set pathLength and dasharray to 1. Starting at 0.3 seconds, animate dashoffset from 1 to 0 with duration 1.35 seconds, ease none, and autoRound false. Capture at 0.23, 0.73, 1.23, and 2.9 seconds to check no line, a partial stroke, increasing length, and the completed hold." }, "variants": [ "직선", "손그림 곡선", "양끝 확장", "직선 밑줄", "손그림 밑줄", "이중 밑줄", "밑줄", "취소선", "동그라미", "이중선" ] }, { "slug": "word-emphasis", "family": "type", "ko": "단어 강조", "en": "Word Emphasis", "aka": [ "키워드 강조", "Keyword focus", "Word emphasis", "발화 단어 강조", "selective-emphasis-pop", "asr-keyword-glow", "Word stagger emphasis", "단어 순차 강조" ], "oneLiner": "정지한 문장에서 한 구절만 색과 크기를 바꾸고 주변 글을 옅게 만드는 효과", "conveys": "읽어야 할 핵심 구절과 정보의 우선순위", "purposes": [ "강조", "설명" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "기본", "useWhen": [ "한 문장의 핵심 구절로 주의를 모을 때", "내레이션의 강조 시점에 시각적 초점을 맞출 때" ], "goodExample": "다음 말만 주홍과 1.06배 크기로 강조되고 AI는과 을 고른다는 옅어진다", "badExample": "여러 단어를 동시에 주홍으로 바꾸어 초점이 분산된다", "avoid": [ "주홍 초점을 여러 곳에 두지 않는다", "크기 변화로 인접 글자를 겹치게 하지 않는다" ], "params": [ { "name": "강조 배율", "default": "1.06", "range": "1.03~1.10", "note": "강조 구절 중심을 기준으로 확대한다" }, { "name": "주변 불투명도", "default": "0.32", "range": "0.25~0.50", "note": "문장은 계속 읽을 수 있어야 한다" }, { "name": "강조 지속", "default": "0.65s", "range": "0.40~0.90s", "note": "색과 크기를 함께 바꾼다" }, { "name": "강조 시작", "default": "0.65s", "range": "0.40~0.90s", "note": "먼저 온전한 문장을 보여준다" } ], "ease": "power2.inOut", "snippet": "const verm = getComputedStyle(document.documentElement).getPropertyValue('--verm').trim();\ntl.to('#focus', {color:verm, scale:1.06, duration:.65, ease:'power2.inOut'}, .65);\ntl.to('.rest', {opacity:.32, duration:.65, ease:'power2.inOut'}, .65);", "pairsWith": [ "highlight-sweep", "underline-draw", "motion-hierarchy" ], "engines": { "hyperframes": "단일 paused GSAP 타임라인으로 구현하고 프레임 시각에서 seek한다. 폰트 로딩 후 고정한 글자 배치를 사용한다.", "reelforge": "텍스트 씬 안의 span에 이 효과의 시간과 간격을 적용하고 마지막 완성 상태를 0.6초 이상 유지한다.", "scrolline": "스크롤 진행률을 0~3초 타임라인 시각으로 매핑하고 역방향 seek에서도 같은 문자와 상태를 복원한다." }, "prompts": { "claude": "<대상>에 이 효과를 적용하라. AI는 다음 말을 고른다를 먼저 온전한 문장으로 보여주고 다음 말만 별도 span으로 감싸라. 0.65초에 다음 말을 주홍 토큰 색과 scale 1.06으로 0.65초 동안 power2.inOut으로 바꾸며 나머지 문장은 opacity 0.32로 낮춰라. 3초 타임라인 안에서 마지막 0.6초는 완성 상태로 정지하라.", "codex": "<파일>의 텍스트 장면에 적용하라. AI는 다음 말을 고른다를 먼저 온전한 문장으로 보여주고 다음 말만 별도 span으로 감싸라. 0.65초에 다음 말을 주홍 토큰 색과 scale 1.06으로 0.65초 동안 power2.inOut으로 바꾸며 나머지 문장은 opacity 0.32로 낮춰라. 0.23초에 문장 전체가 먹색인지, 1.0초에 강조가 진행되는지, 2.7초에 다음 말만 주홍이고 주변과 겹치지 않는지 확인하라." }, "sources": [ { "title": "GSAP Timeline 공식 문서", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 개념 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/mk-callout-highlight/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/caption-editorial-emphasis/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/caption-neon-accent/registry-item.json", "license": "Apache-2.0" }, { "title": "Cambridge University Press", "url": "https://www.cambridge.org/core/books/abs/cambridge-handbook-of-multimedia-learning/principles-for-managing-essential-processing-in-multimedia-learning-segmenting-pretraining-and-modality-principles/DD24C2F48B9B1277CE59F78276110258", "license": "unknown" }, { "title": "Richard E. Mayer / Wiley", "url": "https://onlinelibrary.wiley.com/doi/10.1111/jcal.12197", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/word-emphasis/index.html", "mp4": "effects/word-emphasis/clip.mp4", "gif": "effects/word-emphasis/preview.gif", "poster": "effects/word-emphasis/poster.jpg" }, "no": "095", "oneLinerEn": "A phrase changes color and scale within a stationary sentence while surrounding text fades.", "conveysEn": "Communicates the key phrase to read and the priority of information.", "useWhenEn": [ "Focus attention on a key phrase within a sentence.", "Align visual focus with an emphasized moment in narration." ], "promptsEn": { "claude": "Apply this effect to . First show \"AI picks the next word\" as a complete sentence, wrapping only \"next word\" in a separate span. At 0.65 seconds, animate \"next word\" to the vermilion color token and scale 1.06 over 0.65 seconds with power2.inOut, while lowering the rest of the sentence to opacity 0.32. Within a 3-second timeline, hold the completed state for the final 0.6 seconds.", "codex": "Apply this to the text scene in . First show \"AI picks the next word\" as a complete sentence, wrapping only \"next word\" in a separate span. At 0.65 seconds, animate \"next word\" to the vermilion color token and scale 1.06 over 0.65 seconds with power2.inOut, while lowering the rest of the sentence to opacity 0.32. Verify that the whole sentence is ink-black at 0.23 seconds, emphasis is in progress at 1.0 seconds, and only \"next word\" is vermilion without overlapping surrounding text at 2.7 seconds." }, "variants": [ "고정 키워드", "발화 동기 글로우", "카라오케 강조", "강조색", "확대", "흐림 해제", "읽기 진행", "TrueFocus", "색 강조", "크기 강조" ] }, { "slug": "kinetic-type-sweep", "family": "type", "ko": "대형 키네틱 타이포 스윕", "en": "Kinetic Type Sweep", "aka": [ "타이포 스윕", "역방향 텍스트 띠", "Kinetic typography", "Marquee sweep", "Type slam", "Kinetic Type Curtain Transition", "키네틱 타이포 커튼 전환" ], "oneLiner": "200px 넘는 세리프 문장 세 줄이 서로 반대 방향, 다른 속도로 화면을 가로지르고 마지막에 핵심어 하나만 정중앙에 급정지한다.", "conveys": "많은 말이 흘러가는 가운데 하나가 선택된다는 느낌. 흐름의 속도감이 정지 순간의 무게를 키운다.", "purposes": [ "주목 끌기", "강조", "브랜딩" ], "media": [ "숏폼", "설명 영상", "발표" ], "level": "중급", "useWhen": [ "영상 첫 3초에 주제어 하나를 강하게 박을 때", "여러 후보 가운데 하나가 뽑히는 장면을 글자만으로 보여줄 때", "섹션 전환에서 큰 제목을 소개할 때" ], "goodExample": "세 줄이 각각 초속 약 1000px, 1700px, 500px로 엇갈려 흐르다가 가운데 줄의 \"다음 말\"만 0.3초 만에 멈추며 주홍으로 바뀌고 나머지는 13% 농도로 가라앉는다.", "badExample": "세 줄이 같은 방향, 같은 속도로 흘러 한 덩어리 슬라이드처럼 보이고, 핵심어가 서서히 감속해 멈춘 순간이 느껴지지 않는다.", "avoid": [ "줄마다 속도비를 1.5배 이상 벌린다(비슷하면 한 장이 미끄러지는 것처럼 보임)", "급정지 뒤 흔들림·반복 튕김 금지", "본문 크기 글자에는 쓰지 않는다(글자 높이 150px 이상에서만 흐름이 읽힘)" ], "params": [ { "name": "글자 크기", "default": "200px", "range": "160~260px", "note": "줄 높이 193px에 세 줄이 580px 무대를 채움" }, { "name": "줄별 속도비", "default": "1 : 1.6 : 0.5", "range": "0.4~1.8", "note": "위·가운데·아래 줄, 방향은 좌·우·좌" }, { "name": "질주 구간", "default": "1.95s 등속", "range": "1.4~2.4s", "note": "ease none, 가운데 줄만" }, { "name": "급정지", "default": "0.30s power4.out", "range": "0.2~0.4s", "note": "직전 등속과 첫 속도를 맞춰 끊김 없이 멈춤" }, { "name": "나머지 줄 농도", "default": "0.13", "range": "0.1~0.2", "note": "정지 순간부터 0.5초에 걸쳐 가라앉힘" } ], "ease": "none → power4.out", "snippet": "tl.fromTo('#R1', {x: 1300}, {x: -1500, duration: 2.6, ease: 'power2.out'}, 0.1);\ntl.fromTo('#R3', {x: 1300}, {x: -60, duration: 2.6, ease: 'power2.out'}, 0.1);\nconst xEnd = 640 - (hit.offsetLeft + hit.offsetWidth / 2), xStart = -r2.offsetWidth - 40;\nconst s = (xEnd - xStart) * 0.037; // 등속 끝 속도 = power4.out 첫 속도\ntl.fromTo('#R2', {x: xStart}, {x: xEnd - s, duration: 1.95, ease: 'none'}, 0.15);\ntl.to('#R2', {x: xEnd, duration: 0.3, ease: 'power4.out'}, 2.1);\ntl.to('#hit', {color: 'var(--verm)', duration: 0.12}, 2.12);\ntl.to('#R1, #R3', {opacity: 0.13, duration: 0.5}, 2.15);", "pairsWith": [ "kinetic-beats", "word-emphasis", "whip-pan", "char-stagger" ], "engines": { "hyperframes": "줄 세 개를 각각 transform x만 tween하고, 핵심어 좌표는 글꼴 로드 뒤 offsetLeft로 재서 타임라인을 만든다. 등속 1.95초 뒤 power4.out 0.3초로 이어 붙인다", "reelforge": "타이틀 씬 비트에 줄 문구 3개·속도비(1:1.6:0.5)·정지 단어를 파라미터로 노출하고 정지 시각을 비트 박자에 맞춘다", "scrolline": "스크럽에서는 급정지가 스크롤 속도에 묻힌다. 진행률 0~0.7은 흐름, 0.7~0.8에 정지를 몰고 0.8 이후는 홀드로 둔다" }, "prompts": { "claude": "<파일>에 대형 키네틱 타이포 스윕을 만들어줘. 200px 세리프 문장 세 줄을 위·가운데·아래에 두고 좌·우·좌로 속도비 1 : 1.6 : 0.5로 흘려. 가운데 줄은 1.95초 등속으로 달리다 마지막 0.3초 power4.out으로 급정지하고, 핵심어 <대상>의 중심이 화면 가로 중앙에 오게 해. 정지 순간 핵심어만 주홍으로, 나머지 두 줄은 0.5초에 걸쳐 opacity 0.13으로 가라앉히고 0.6초 이상 홀드해.", "codex": "<파일>의 오프닝 장면에 kinetic type sweep을 적용해. 줄 3개(font-size 200px, 줄 높이 193px)를 transform x만으로 움직이고, 가운데 줄은 xEnd = 640 - (핵심어 offsetLeft + 폭/2)로 계산해 등속 1.95s(ease none) 뒤 0.3s power4.out으로 멈추게 해. 등속 끝 속도와 감속 첫 속도가 같도록 등속 구간 끝을 전체 이동량의 3.7% 앞에 둔다. 0.9초·2.0초·3.3초를 캡처해 세 줄이 엇갈려 흐르는지, 핵심어가 정중앙에 멈췄는지, 주홍이 한 곳뿐인지 확인해." }, "sources": [ { "title": "Kinetic typography (Wikipedia)", "url": "https://en.wikipedia.org/wiki/Kinetic_typography", "license": "개념 인용" }, { "title": "GSAP Eases", "url": "https://gsap.com/docs/v3/Eases", "license": "문서 참고" }, { "title": "codrops/KineticTypePageTransition", "url": "https://github.com/codrops/KineticTypePageTransition", "license": "MIT" } ], "runtime": "gsap", "render": "done", "oneLinerEn": "Three lines of 200px+ serif type sweep across the frame in opposite directions at different speeds, then one key word slams to a stop dead center.", "conveysEn": "Many words stream past and one gets chosen. The speed of the flow gives the stop its weight.", "useWhenEn": [ "Stamp a single theme word in the first three seconds of a video.", "Show one choice being picked from many candidates with type alone.", "Introduce a large section title at a transition." ], "promptsEn": { "claude": "Build a large kinetic type sweep in . Place three lines of 200px serif text at top, middle, and bottom, moving left, right, left at a speed ratio of 1 : 1.6 : 0.5. Run the middle line at constant speed for 1.95s, then stop it hard with power4.out over 0.3s so the center of lands at the horizontal center. On the stop, turn only the key word vermilion, fade the other two lines to opacity 0.13 over 0.5s, and hold for at least 0.6s.", "codex": "Apply a kinetic type sweep to the opening scene in . Move three lines (font-size 200px, line height 193px) with transform x only. For the middle line compute xEnd = 640 - (key word offsetLeft + width/2), run 1.95s with ease none, then stop over 0.3s with power4.out. End the constant segment 3.7% of the total distance early so the exit speed matches the deceleration's start speed. Capture at 0.9s, 2.0s, 3.3s to verify the lines cross in opposite directions, the key word stops dead center, and vermilion appears in one place only." }, "files": { "html": "effects/kinetic-type-sweep/index.html", "mp4": "effects/kinetic-type-sweep/clip.mp4", "gif": "effects/kinetic-type-sweep/preview.gif", "poster": "effects/kinetic-type-sweep/poster.jpg" }, "no": "096", "variants": [ "역회전 복귀", "대형 문구 덮개" ] }, { "slug": "context-caret", "family": "type", "ko": "문맥 반응 커서", "en": "Context Caret", "aka": [ "Context caret", "문맥 커서", "context-sensitive-cursor" ], "oneLiner": "입력 중인 구간의 성격에 따라 커서의 색이 바뀌고 깜빡임이 이어지는 텍스트 커서", "conveys": "지금 어디를 쓰고 있는지가 커서 색만으로 읽힌다. 입력이 살아 있는 화면처럼 느껴진다", "purposes": [ "설명", "피드백" ], "media": [ "제품 시연", "웹 UI", "설명 영상" ], "level": "기본", "useWhen": [ "입력창에 브랜드어나 검증 결과처럼 구간별 의미가 다른 글을 타이핑할 때", "AI가 답을 쓰는 화면에서 현재 초점 구간을 조용히 알려 줄 때" ], "goodExample": "\"승인\" 두 글자를 칠 때 커서가 초록으로 바뀌고 입력이 멈추면 0.2초 뒤 다시 흰색으로 깜빡인다", "badExample": "구간마다 색을 6가지 이상 쓰거나 깜빡임을 0.3초 이하로 줄여 글자보다 커서가 더 눈에 띈다", "avoid": [ "색 전환은 즉시(0ms)로 한다. 부드럽게 바꾸면 이전 구간 색이 잔상처럼 남는다", "타이핑 중에는 깜빡임을 멈추고 고정 점등한다. 깜빡이는 커서는 읽기를 방해한다" ], "params": [ { "name": "깜빡임 주기", "default": "0.9s", "range": "0.6~1.2s", "note": "켜짐 50%, 꺼짐 50%" }, { "name": "입력 유예", "default": "0.2s", "range": "0.1~0.4s", "note": "마지막 글자 후 이 시간이 지나야 깜빡임 재개" }, { "name": "색 전환", "default": "0ms", "range": "0ms 고정", "note": "구간 경계에서 즉시 교체" }, { "name": "커서 두께", "default": "3px", "range": "2~4px", "note": "1920x1080, 글자 크기 48px 기준" } ], "ease": "steps(1)", "snippet": "const caret = document.querySelector('.caret');\nconst seg = t => t < 1.2 ? '#fff' : t < 2.0 ? '#3ddc84' : '#fff';\nfunction render(t){\n const typing = (t % 0.15) < 0.15 && t < 2.6;\n const blink = Math.floor((t - 2.6) / 0.45) % 2 === 0;\n caret.style.background = seg(t);\n caret.style.opacity = typing || blink ? 1 : 0;\n}\ntl.eventCallback('onUpdate', () => render(tl.time()));", "pairsWith": [ "typewriter", "text-scramble", "typing-input" ], "engines": { "hyperframes": "시간 t 하나에서 커서 색과 점멸을 계산하는 순수 함수로 만든다. paused 타임라인의 onUpdate에서 호출하면 seek 시점마다 같은 상태가 나온다", "reelforge": "씬 브리프에 구간별 색표와 유예 0.2초, 깜빡임 0.9초를 파라미터로 싣는다. 입력 문자열은 브리프 텍스트를 그대로 쓴다", "scrolline": "진행률 0~1을 글자 수로 환산해 입력 위치를 정한다. 스크롤을 멈추면 커서만 깜빡이므로 점멸은 CSS steps 애니메이션으로 분리한다" }, "prompts": { "claude": "<대상> 입력창에 문맥 반응 커서를 넣어줘. 커서 두께 3px, 깜빡임 0.9초 주기(켜짐 50%), 타이핑 중에는 고정 점등, 마지막 글자 뒤 0.2초에 깜빡임 재개. 구간 경계에서 색은 0ms로 즉시 바꾸고, 시간 t의 함수로만 그려 seek해도 같은 결과가 나오게 해.", "codex": "<파일>의 타이핑 장면에 문맥 반응 커서를 적용해. 커서 색은 t<1.2s 흰색, 1.2~2.0s 초록, 이후 흰색으로 하고 깜빡임은 0.9초 주기, 유예 0.2초로 계산한다. Math.random 없이 t의 함수만 쓰고, 0.6초·1.5초·3.0초 시점을 캡처해 색이 흰색, 초록, 깜빡임 상태로 나오는지 확인해." }, "sources": [ { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/rules/context-sensitive-cursor.md", "license": "unknown" } ], "runtime": "css", "variants": [ "브랜드 구간", "성공 구간", "타이핑 중 고정" ], "oneLinerEn": "A text caret that changes color with the kind of text being typed, then keeps blinking.", "conveysEn": "The caret alone tells the viewer which segment is active, so the input feels alive.", "useWhenEn": [ "When typing text whose segments carry different meaning, such as a brand word or a verification result", "When an AI answer streams and you want to quietly mark the active segment" ], "promptsEn": { "claude": "Add a context-reactive caret to . Use these settings: caret width 3px; blink period 0.9s at 50% duty; solid while typing; blinking resumes 0.2s after the last character. Switch color at segment boundaries in 0ms. Draw it as a pure function of time t so seeking gives identical results.", "codex": "Apply a context caret to the typing scene in . Caret color: white for t<1.2s, green from 1.2 to 2.0s, white afterwards; blink period 0.9s with a 0.2s idle delay. No Math.random, only functions of t. Capture at 0.6s, 1.5s and 3.0s and confirm white, green, and blinking states." }, "render": "planned", "files": {}, "no": "097" }, { "slug": "flip-decode-text", "family": "type", "ko": "3D 회전 해독", "en": "3D Flip Decode", "aka": [ "3D flip decode" ], "oneLiner": "글자가 회전하며 임의 문자를 몇 번 거친 뒤 실제 글자로 착지하는 해독 효과", "conveys": "암호가 풀리는 기계적 진행. 정보가 확정되는 순간이 뚜렷하다", "purposes": [ "주목 끌기", "설명" ], "media": [ "숏폼", "제품 시연", "설명 영상" ], "level": "중급", "useWhen": [ "보안·해독·분석 결과를 밝히는 타이틀", "AI가 추론 결과를 확정해 내놓는 장면" ], "goodExample": "\"접근 승인\"이 글자마다 0.6초 동안 rotateX 90도를 돌며 임의 문자 3개를 거치고 왼쪽부터 0.04초씩 늦게 착지한다", "badExample": "임의 문자가 매 프레임 바뀌고 Math.random이 쓰여 캡처할 때마다 다른 글자가 나온다", "avoid": [ "임의 문자열은 시드 난수로 미리 뽑아 둔다. Math.random은 쓰지 않는다", "착지 글자는 정확한 글자로 끝나야 한다. 마지막 0.1초는 흔들림 없이 정지한다" ], "params": [ { "name": "글자당 시간", "default": "0.6s", "range": "0.4~0.8s", "note": "회전과 교체 포함" }, { "name": "글자 간 시작 차", "default": "0.04s", "range": "0.02~0.08s", "note": "왼쪽부터" }, { "name": "회전각", "default": "90deg", "range": "90~180deg", "note": "rotateX" }, { "name": "중간 임의 문자", "default": "3개", "range": "2~5개", "note": "시드 고정" } ], "ease": "power2.out", "snippet": "let seed = 7; const rnd = () => (seed = (seed * 16807) % 2147483647) / 2147483647;\nconst pool = 'ABCDEFGHJKLMNPQRSTUVWXYZ0123456789';\nchars.forEach((c, i) => {\n const frames = [0,1,2].map(() => pool[Math.floor(rnd() * pool.length)]);\n const o = {p:0};\n tl.to(o, {p:1, duration:0.6, ease:'power2.out', onUpdate(){ const k = Math.min(3, Math.floor(o.p * 4)); node(i).textContent = k < 3 ? frames[k] : c; node(i).style.transform = `rotateX(${(1 - o.p) * 90}deg)`; }}, 0.2 + i * 0.04);\n});", "pairsWith": [ "text-scramble", "split-flap", "letter-flip-3d" ], "engines": { "hyperframes": "시드 난수 결과를 배열로 굳히고 onUpdate에서 progress로 인덱스를 고른다. seek해도 같은 문자가 나오도록 상태를 저장하지 않는다", "reelforge": "브리프에 목표 문자열, 임의 문자 풀, 시드값 7, 글자당 0.6초, 시작 차 0.04초를 싣는다", "scrolline": "진행률을 글자별 구간으로 나눠 착지 글자가 확정된 뒤에는 되돌려도 유지되도록 한다. 임의 문자는 진행률 기반이라 뒤로 감아도 같다" }, "prompts": { "claude": "<대상> 문구를 해독 효과로 공개해줘. 글자마다 rotateX 90도에서 0도로 0.6초 도는 동안 임의 문자 3개(시드 7 고정)를 거쳐 정답 글자에 착지하고, 시작 차는 왼쪽부터 0.04초. 착지 전 마지막 0.1초는 정답 글자로 고정해.", "codex": "<파일>에 flip decode text를 적용해. 시드 7의 선형 합동 난수로 글자별 임의 문자 3개를 미리 뽑고, progress 0~1에서 인덱스를 선택, rotateX (1-p)*90deg, duration 0.6, stagger 0.04. 0.4초·0.9초·1.6초를 캡처해 임의 문자 상태와 완료 상태를 확인하고 두 번 렌더한 프레임이 동일한지 비교해." }, "sources": [ { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/rules/hacker-flip-3d.md", "license": "unknown" } ], "runtime": "css", "variants": [], "oneLinerEn": "Each character rotates through a few random symbols and lands on its true glyph.", "conveysEn": "A mechanical decoding progression, with the moment of certainty clearly visible.", "useWhenEn": [ "A title that reveals a security, decoding or analysis result", "A scene where an AI locks in its reasoning result" ], "promptsEn": { "claude": "Reveal the text in as a decode. Each glyph rotates rotateX 90deg to 0 over 0.6s while cycling 3 random symbols (seed fixed at 7) before landing on the correct glyph, starting 0.04s apart left to right. Hold the correct glyph for the last 0.1s.", "codex": "Apply flip decode text in . Pre-draw 3 random symbols per glyph with a seed-7 LCG; pick index from progress 0..1; rotateX (1-p)*90deg; duration 0.6; stagger 0.04. Capture at 0.4s, 0.9s and 1.6s, verify scramble and settled states, and render twice to confirm identical frames." }, "render": "planned", "files": {}, "no": "098" }, { "slug": "font-shuffle", "family": "type", "ko": "글꼴 셔플", "en": "Font Shuffle", "aka": [ "Font shuffle", "Typeface Shuffle", "키워드 글꼴 셔플" ], "oneLiner": "문장은 고정하고 핵심 단어의 글꼴만 박자마다 바뀌는 효과", "conveys": "같은 말이 여러 성격을 가진다는 것과 음악의 박자를 함께 보여준다. 글꼴 변화가 리듬 악기가 된다", "purposes": [ "분위기", "주목 끌기" ], "media": [ "숏폼", "발표" ], "level": "중급", "useWhen": [ "음악 영상에서 키워드가 박자마다 다른 표정을 가질 때", "브랜드 가치 단어를 다양한 서체로 훑을 때" ], "goodExample": "'FREE'가 박자 250ms마다 4가지 글꼴로 바뀌고 1초 뒤 최종 글꼴에서 멈추며 문장의 나머지는 움직이지 않는다", "badExample": "글꼴마다 글자 폭이 달라 문장 전체가 좌우로 출렁인다", "avoid": [ "슬롯 폭을 가장 넓은 글꼴 기준으로 고정한다", "글꼴 4종 초과 금지", "최종 정지 글꼴은 가독성이 가장 높은 것으로" ], "params": [ { "name": "박자 간격", "default": "250ms", "range": "150~400ms", "note": "글꼴 교체 간격" }, { "name": "글꼴 수", "default": "4", "range": "3~5", "note": "교체 후보" }, { "name": "최종 정지", "default": "1000ms", "range": "600~1500ms", "note": "마지막 글꼴 유지" }, { "name": "슬롯 폭", "default": "최대 글꼴 폭", "range": "-", "note": "레이아웃 고정" } ], "ease": "steps(1)", "snippet": "const fonts = ['Playfair Display', 'Bebas Neue', 'Space Mono', 'Pretendard'];\nfonts.forEach((f, i) => {\n tl.set('.kw', { fontFamily: f }, i * 0.25);\n});\n/* .kw는 min-width로 슬롯 폭을 고정 */", "pairsWith": [ "kinetic-beats", "word-slot-cycle", "text-scramble" ], "engines": { "hyperframes": "모든 글꼴을 preload하고 tl.set으로 교체한다. 로드 전 대체 글꼴이 렌더되지 않도록 document.fonts.ready 이후에 캡처한다", "reelforge": "타이포 씬 브리프에 글꼴 4종 이름과 박자 배열(초)을 명시한다. beat 분석 결과를 그대로 쓴다", "scrolline": "글꼴 인덱스를 진행률 구간 4등분으로 정한다. scrub에서도 계단식 교체라 되감기에 안전하다" }, "prompts": { "claude": "<대상> 문장에서 핵심 단어 '<단어>'만 글꼴이 바뀌게 해줘. 0.25초마다 Playfair Display, Bebas Neue, Space Mono, Pretendard 순으로 교체하고 마지막 글꼴에서 1초 정지. 단어 슬롯 폭은 가장 넓은 글꼴 기준 min-width로 고정해 나머지 문장이 흔들리지 않게 하고, 글꼴은 preload해.", "codex": "<파일>에 .kw의 fontFamily를 tl.set으로 0, 0.25, 0.5, 0.75초에 교체하고 min-width를 고정해. 각 시점을 캡처해 글꼴이 실제로 바뀌었는지, 주변 문장 x 좌표가 동일한지 확인해." }, "sources": [ { "title": "local/HyperFrames-skills", "url": "claude-skill:music-to-video/references/template-catalog.md", "license": "unknown" }, { "title": "local/music-to-video", "url": "claude-skill:music-to-video/references/template-catalog.md#typewriter-phrase-keyword-shuffle", "license": "unknown" } ], "runtime": "css", "variants": [ "키워드 서체 순환", "변하는 목소리", "typewriter-phrase-keyword-shuffle" ], "oneLinerEn": "The sentence stays put while a key word changes typeface on every beat.", "conveysEn": "Shows one word taking on different personalities in time with the music; the font change acts as a rhythm instrument.", "useWhenEn": [ "Give a keyword a different face on each beat in a music video.", "Sweep a brand-value word through several typefaces." ], "promptsEn": { "claude": "In the sentence of change only the key word '' typeface: every 0.25 seconds cycle Playfair Display, Bebas Neue, Space Mono, Pretendard and hold the last one for 1 second. Fix the word slot to a min-width based on the widest face so the rest of the sentence never shifts, and preload the fonts.", "codex": "In switch .kw fontFamily with tl.set at 0, 0.25, 0.5 and 0.75s and fix min-width. Capture each moment to confirm the face actually changed and that the x position of the surrounding text is identical." }, "render": "planned", "files": {}, "no": "099" }, { "slug": "glyph-roll", "family": "type", "ko": "글자 롤링 교체", "en": "Glyph Roll", "aka": [ "Rolling Glyphs", "굴러 바뀌는 글자", "Rolling glyph replacement" ], "oneLiner": "글자마다 세로 슬롯이 굴러 다음 글자로 정착하는 교체 효과", "conveys": "정보가 정교하게 갱신되는 느낌. 기계식 카운터나 예약판처럼 믿음직하게 읽힌다", "purposes": [ "피드백", "데이터 증명" ], "media": [ "웹 UI", "제품 시연", "숏폼" ], "level": "중급", "useWhen": [ "버튼 문구나 상태 라벨이 다른 값으로 바뀔 때", "짧은 코드·번호·상태어가 갱신되는 순간" ], "goodExample": "\"대기\" 두 글자가 0.7초 동안 세로로 굴러 \"완료\"로 바뀌고 글자 사이 시작 차는 0.05초다", "badExample": "롤링 거리를 3em 이상 주어 글자가 번쩍이는 것처럼 보이고 무슨 글자인지 읽히지 않는다", "avoid": [ "슬롯은 overflow hidden으로 한 글자 높이만 보이게 한다. 삐져나오면 롤링이 지저분하다", "5글자를 넘기면 전체 시간이 길어진다. 짧은 문자열에만 쓴다" ], "params": [ { "name": "글자당 시간", "default": "0.7s", "range": "0.5~0.9s", "note": "ease-out" }, { "name": "글자 간 시작 차", "default": "0.05s", "range": "0.03~0.08s", "note": "왼쪽부터" }, { "name": "슬롯 이동", "default": "1em", "range": "1~1.2em", "note": "한 글자 높이" }, { "name": "방향", "default": "위로", "range": "위/아래", "note": "선택지 순서에 맞춤" } ], "ease": "power3.out", "snippet": "chars.forEach((c, i) => {\n // 각 슬롯: .slot > .col(이전 글자, 다음 글자 세로 배열)\n tl.fromTo(slot(i).querySelector('.col'), {yPercent:0}, {yPercent:-50, duration:0.7, ease:'power3.out'}, 0.3 + i * 0.05);\n});\n// .slot { height:1.2em; overflow:hidden } .col { display:flex; flex-direction:column }", "pairsWith": [ "flip-decode-text", "split-flap", "count-up" ], "engines": { "hyperframes": "슬롯마다 두 글자를 세로로 쌓고 yPercent -50만 움직인다. 한국어는 grapheme 단위로 나눠 완성형 음절을 쪼개지 않는다", "reelforge": "브리프에 이전 문자열과 다음 문자열, 글자 수, 시간 0.7초, 시작 차 0.05초를 싣는다. 글자 수가 다르면 앞쪽을 공백으로 채운다", "scrolline": "진행률 0~1을 글자별 지연으로 나눠 yPercent에 연결한다. 스크롤을 멈춰도 글자가 반쯤 걸치지 않게 진행률 스냅을 둔다" }, "prompts": { "claude": "<대상> 라벨을 글자별 세로 롤링으로 바꿔줘. 각 글자는 한 글자 높이의 슬롯(overflow hidden) 안에서 이전 글자가 위로 나가고 다음 글자가 아래에서 올라오며, 0.7초 power3.out, 글자 사이 시작 차 0.05초. 한국어는 음절 단위로 나눠.", "codex": "<파일>의 상태 라벨에 glyph roll을 적용해. Intl.Segmenter로 grapheme 분할, 슬롯 height 1.2em overflow hidden, .col yPercent 0에서 -50, duration 0.7, stagger 0.05, power3.out. 0.5초·0.8초·1.3초를 캡처해 중간에 두 글자가 슬롯 안에 걸쳐 보이고 끝에는 새 글자만 보이는지 확인해." }, "sources": [ { "title": "ibelick/motion-primitives", "url": "https://motion-primitives.com/docs/text-roll", "license": "MIT" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" }, { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-rolling-text-button", "license": "unknown" }, { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-rolling-text-button-stagger", "license": "unknown" } ], "runtime": "css", "variants": [ "Text Roll", "Shuffle", "CodeSlots", "Counter", "text-roll", "버튼 문구", "순차 롤링", "반대 방향 롤링" ], "oneLinerEn": "Each character sits in a vertical slot that rolls until the next glyph lands.", "conveysEn": "Information updates with mechanical precision, like a counter or a booking board.", "useWhenEn": [ "When a button label or status word changes to another value", "When a short code, number or status word refreshes" ], "promptsEn": { "claude": "Convert the label in to a per-glyph vertical roll. Each character lives in a one-glyph-tall slot with overflow hidden; the old glyph exits up and the new one rises from below over 0.7s power3.out, starting 0.05s apart. Split Korean by syllable.", "codex": "Apply glyph roll to the status label in . Split by grapheme with Intl.Segmenter; slot height 1.2em with overflow hidden; .col yPercent 0 to -50, duration 0.7, stagger 0.05, power3.out. Capture at 0.5s, 0.8s and 1.3s and confirm two glyphs straddle the slot mid-roll and only the new glyph remains at the end." }, "render": "planned", "files": {}, "no": "100" }, { "slug": "handwriting-write-on", "family": "type", "ko": "손글씨 쓰기", "en": "Handwriting Write On", "aka": [ "Handwritten stroke", "Handwritten write and erase", "손글씨 쓰기와 지우기", "Write", "Unwrite", "SVG Stroke Text Draw", "SVG 획 텍스트 그리기", "Stitched text" ], "oneLiner": "펜 끝이 글자의 실제 획 순서대로 움직이며 필체가 드러나는 손글씨 쓰기", "conveys": "글을 직접 쓰는 사람의 손길과 속도. 개인적이고 정성스러운 인상을 준다", "purposes": [ "분위기", "브랜딩", "설명" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "고급", "useWhen": [ "서명·인사말·손글씨 제목이 쓰이는 장면", "작은 단어를 감성적으로 강조할 때" ], "goodExample": "\"고맙습니다\"가 획마다 300~600ms, 획 사이 80ms로 곡선에서 감속하며 필기 순서대로 그려진다", "badExample": "글자 폴리곤을 왼쪽에서 오른쪽으로 마스크 와이프해 획 순서와 무관하게 드러난다", "avoid": [ "획 경로는 폰트 외곽선이 아니라 중심선을 쓴다. 외곽선 dash는 두 겹 테두리가 생긴다", "작은 크기 글자에는 쓰지 않는다. 획이 보여야 손글씨로 읽힌다" ], "params": [ { "name": "획 시간", "default": "0.4s", "range": "0.3~0.6s", "note": "길이에 비례" }, { "name": "획 사이", "default": "80ms", "range": "60~120ms", "note": "펜 이동" }, { "name": "곡선 감속", "default": "power2.inOut", "range": "power1~power3", "note": "곡선부에서 느려짐" }, { "name": "선 굵기", "default": "8px", "range": "4~12px", "note": "stroke-linecap round" } ], "ease": "power2.inOut", "snippet": "// SVG path의 중심선, pathLength=1로 정규화\nstrokes.forEach((p, i) => {\n p.style.strokeDasharray = 1; p.style.strokeDashoffset = 1;\n tl.to(p, {strokeDashoffset:0, duration:0.4, ease:'power2.inOut'}, 0.2 + i * 0.48);\n});", "pairsWith": [ "text-on-path", "typewriter", "analog-medium-text" ], "engines": { "hyperframes": "pathLength=1 속성으로 dash를 정규화하면 획 길이와 무관하게 같은 코드가 된다. 획 순서는 배열 순서로 고정한다", "reelforge": "브리프에 SVG 획 경로 배열(순서 포함), 획 0.4초, 획 사이 80ms, 굵기 8px를 싣는다. 획 SVG는 사전 준비 자산으로 받는다", "scrolline": "진행률을 전체 획 길이에 비례해 나눠 각 획의 dashoffset에 매핑한다. 획 사이 정지는 진행률 구간에 그대로 포함한다" }, "prompts": { "claude": "<대상> 문구를 손글씨로 쓰이게 해줘. 중심선 SVG 획들을 pathLength 1로 정규화하고 stroke-dashoffset을 획 순서대로 1에서 0으로 애니메이션해. 획당 0.4초 power2.inOut, 획 사이 80ms, 선 굵기 8px round cap. 획 순서는 배열 순서로 고정해.", "codex": "<파일>에 handwriting write-on을 적용해. 각 path의 pathLength=1, strokeDasharray 1, strokeDashoffset 1에서 0을 0.4초 power2.inOut, 시작 0.2+i*0.48. 0.5초·1.0초·끝을 캡처해 획이 순서대로 진행하는지, 완료 시 dashoffset 0으로 모든 획이 이어지는지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/hw-write-title/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/hw-path-text/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/hw-title/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:embedded-captions/CATALOG.md", "license": "unknown" }, { "title": "ManimCommunity/manim", "url": "https://github.com/ManimCommunity/manim/blob/main/manim/animation/creation.py", "license": "MIT" } ], "runtime": "svg", "variants": [ "획 순서 쓰기", "쓰기와 지우기", "실땀 글자", "획 쓰기", "경로 위 글자", "서예", "손글씨 제목", "획별 필기" ], "oneLinerEn": "A pen tip travels along the real stroke order so the handwriting is revealed.", "conveysEn": "The hand and pace of a person writing: personal and careful.", "useWhenEn": [ "Signatures, greetings and handwritten titles", "Emotionally emphasizing a small word" ], "promptsEn": { "claude": "Make the phrase in write itself by hand. Normalize centerline SVG strokes with pathLength 1 and animate stroke-dashoffset 1 to 0 in stroke order: 0.4s power2.inOut per stroke, 80ms between strokes, 8px round caps. Fix the order by array order.", "codex": "Apply handwriting write-on in . For each path set pathLength=1, strokeDasharray 1, strokeDashoffset 1 to 0 over 0.4s power2.inOut, start 0.2+i*0.48. Capture at 0.5s, 1.0s and the end to verify strokes progress in order and dashoffset reaches 0 for all." }, "render": "planned", "files": {}, "no": "101" }, { "slug": "inline-image-expand", "family": "type", "ko": "문장 속 이미지 확장", "en": "Inline Image Expand", "aka": [ "Inline Image Typography Expansion" ], "oneLiner": "문장 속 작은 이미지가 커지며 앞뒤 문구를 밀어내는 효과", "conveys": "글이 설명하던 대상이 화면의 주인공으로 바뀐다. 문장 안에서 자연스럽게 시각으로 넘어간다", "purposes": [ "강조", "전환" ], "media": [ "숏폼", "발표", "스크롤덱" ], "level": "중급", "useWhen": [ "'우리는 [이미지]를 만듭니다'처럼 문장 속 대상을 크게 보여줄 때", "텍스트 장면에서 사진 장면으로 넘어갈 때" ], "goodExample": "문장 속 1em 크기 사진이 1.6초에 70vw까지 커지며 앞뒤 단어가 양옆으로 밀려 화면 밖으로 사라진다", "badExample": "이미지만 커지고 문구는 그대로여서 글자와 겹치거나, 밀려나는 속도가 제각각이다", "avoid": [ "텍스트 이동은 이미지 폭 변화와 같은 이징·지속을 쓴다", "이미지 비율 유지(object-fit cover로 왜곡 금지)", "한 문장에 확장 이미지 1개만" ], "params": [ { "name": "이미지 폭", "default": "1em → 70vw", "range": "50~85vw", "note": "확장 폭" }, { "name": "지속", "default": "1600ms", "range": "1200~2000ms", "note": "확장 시간" }, { "name": "이징", "default": "power2.inOut", "range": "power2~power3.inOut", "note": "텍스트와 동일" }, { "name": "문구 퇴장", "default": "x ±40vw", "range": "±30~±60vw", "note": "양옆으로 밀림" } ], "ease": "power2.inOut", "snippet": "tl.to('.img', { width: '70vw', height: '40vw', duration: 1.6, ease: 'power2.inOut' }, 0)\n .to('.left', { x: '-40vw', opacity: 0, duration: 1.6, ease: 'power2.inOut' }, 0)\n .to('.right', { x: '40vw', opacity: 0, duration: 1.6, ease: 'power2.inOut' }, 0);", "pairsWith": [ "scroll-grid-expand", "zoom-to-detail", "text-cutout-zoom" ], "engines": { "hyperframes": "width/height tween은 레이아웃을 흔들 수 있어 flex 컨테이너 안에서 고정 중심으로 두고 좌우 텍스트를 x로 동시에 밀어 seek 결과를 안정시킨다", "reelforge": "씬 브리프에 문장, 이미지 경로, 확장 목표 폭(70vw), 지속 1.6s를 싣는다. 이미지는 비율 지정 필수", "scrolline": "확장 폭을 진행률 0..1에 매핑하고 이미지가 화면을 채우는 지점을 홀드로 둔다. 스프링 대신 power2.out" }, "prompts": { "claude": "<대상>의 문장 '<앞> [이미지] <뒤>'에서 단어 사이 1em 크기의 <이미지>가 1.6초 동안 70vw x 40vw로 커지게 해줘. 앞 문구는 왼쪽으로 -40vw, 뒤 문구는 오른쪽으로 +40vw 밀리며 사라지고, 셋 모두 power2.inOut. 이미지 비율은 object-fit: cover로 유지하고 홀드 1초.", "codex": "<파일>의 인라인 이미지에 width/height tween(1em→70vw, 1.6s, power2.inOut)을 걸고 좌우 텍스트 x를 동시에 이동시켜. 0.8초 시점에 문구와 이미지가 겹치지 않는지, 2.6초에 이미지가 70vw인지 캡처와 bbox 값으로 확인해." }, "sources": [ { "title": "codrops/ImageExpansionTypography", "url": "https://github.com/codrops/ImageExpansionTypography", "license": "MIT" } ], "runtime": "gsap", "variants": [ "한 줄 분리", "여러 줄 확장" ], "oneLinerEn": "A small inline image between words grows and pushes the surrounding text aside.", "conveysEn": "The subject the sentence described takes over the frame, moving naturally from text to picture inside the line.", "useWhenEn": [ "Enlarge the subject in a sentence like 'We make [image]'.", "Move from a text scene to a photo scene." ], "promptsEn": { "claude": "In the sentence ' [image] ' of , grow the 1em inline to 70vw by 40vw over 1.6 seconds. The leading text moves -40vw left and the trailing text +40vw right while fading out, all with power2.inOut. Keep the image ratio with object-fit: cover and hold for 1 second.", "codex": "In add a width/height tween (1em to 70vw, 1.6s, power2.inOut) on the inline image and move the side text on x simultaneously. Capture at 0.8s to confirm text and image do not overlap and at 2.6s to confirm the image bbox is 70vw wide." }, "render": "planned", "files": {}, "no": "102" }, { "slug": "letter-anagram-shift", "family": "type", "ko": "글자 재배치 조립", "en": "Letter Anagram Shift", "aka": [ "Letter Shuffle Reassembly" ], "oneLiner": "한 단어의 글자들이 옮겨 가 새 단어나 목록의 머리글자가 되는 효과", "conveys": "같은 재료가 새 구조로 이어진다는 관계를 보여준다. 글자가 사라지지 않고 이동해 연속성이 느껴진다", "purposes": [ "설명", "전환" ], "media": [ "설명 영상", "발표", "웹 UI" ], "level": "고급", "useWhen": [ "단어에서 항목 목록으로 넘어가며 각 글자가 항목의 머리글자가 될 때", "메뉴 제목이 개별 항목 라벨로 분해될 때" ], "goodExample": "'MOTION' 여섯 글자가 800ms에 걸쳐 세로 목록 여섯 줄의 첫 글자 위치로 이동하고 나머지 글자가 뒤따라 나타난다", "badExample": "글자마다 도착 시각이 제각각이고 경로가 교차해 어느 글자가 어디로 가는지 알 수 없다", "avoid": [ "시작·도착 좌표는 실측하고 추정하지 않는다", "이동 중 글자 크기 변화를 크게 두지 않는다(150% 초과 금지)", "같은 글자가 중복되면 순서 규칙을 미리 정한다" ], "params": [ { "name": "이동 시간", "default": "800ms", "range": "600~1000ms", "note": "글자당" }, { "name": "글자 간 지연", "default": "40ms", "range": "20~60ms", "note": "왼쪽부터" }, { "name": "이징", "default": "power3.inOut", "range": "power2~power4.inOut", "note": "출발과 도착 모두 보임" }, { "name": "도착 후 대기", "default": "500ms", "range": "300~800ms", "note": "나머지 글자 등장 전" } ], "ease": "power3.inOut", "snippet": "const from = [...src].map(e => e.getBoundingClientRect());\nconst to = [...dst].map(e => e.getBoundingClientRect());\nsrc.forEach((el, i) => {\n tl.fromTo(el, { x: 0, y: 0 }, { x: to[i].left - from[i].left, y: to[i].top - from[i].top,\n duration: 0.8, ease: 'power3.inOut' }, i * 0.04);\n});", "pairsWith": [ "text-scatter-assemble", "stagger", "split-flap" ], "engines": { "hyperframes": "레이아웃 측정은 타임라인 구성 시점 한 번만 하고 결과를 tween에 고정한다. 렌더 중 getBoundingClientRect를 다시 부르지 않는다", "reelforge": "씬 워커 브리프에 출발 문구, 도착 목록, 대응표(글자 인덱스 쌍)를 명시한다. 대응표 없이는 임의 매칭이 생긴다", "scrolline": "이동량을 진행률 0..1에 매핑하고 도착 근처는 ease-out으로 눌러 스크롤이 멈춰도 글자가 겹치지 않게 한다" }, "prompts": { "claude": "<대상>에서 단어 '<원문>'의 각 글자가 목록 '<항목들>'의 머리글자 자리로 이동하게 해줘. 글자는 span으로 나누고 시작·도착 좌표를 미리 측정해 tween에 넣어. 글자당 0.8초, 40ms 간격, power3.inOut, 도착 후 0.5초 대기하고 나머지 글자를 페이드인. 한글은 음절 단위로 나눠.", "codex": "<파일>에서 원문 글자 span과 목록 머리글자 span을 짝지어 좌표 차이를 계산한 뒤 tl.fromTo로 이동시켜. 좌표 계산은 빌드 시 1회만 하고 렌더 중 재측정 금지. 0.4초에 글자가 이동 중이고 1.4초에 각 글자 bbox가 목표 bbox와 2px 이내로 일치하는지 캡처와 스크립트로 확인해." }, "sources": [ { "title": "codrops/LetterShuffleMenu", "url": "https://github.com/codrops/LetterShuffleMenu", "license": "MIT" }, { "title": "codrops/LettersAnimationLayout", "url": "https://github.com/codrops/LettersAnimationLayout", "license": "MIT" } ], "runtime": "gsap", "variants": [ "목록 머리글 변환", "패널 분산 배치", "원래 문장 복귀" ], "oneLinerEn": "The letters of one word travel to become the initials of a new word or list.", "conveysEn": "Shows the same material reconnecting into a new structure; letters move instead of vanishing, which keeps continuity.", "useWhenEn": [ "Move from a word to a list where each letter becomes an item initial.", "Break a menu title into individual item labels." ], "promptsEn": { "claude": "In , make each letter of the word '' travel to the initial position of the items in ''. Split into spans, pre-measure the start and end coordinates and bake them into the tweens. 0.8 seconds each, 40ms apart, power3.inOut, wait 0.5 seconds after arrival and then fade in the remaining letters. Split Korean by syllable.", "codex": "In pair each source-letter span with its target initial span, compute the offset, and animate with tl.fromTo. Measure once at build time and never re-measure during rendering. Capture at 0.4s (letters in flight) and 1.4s, and verify by script that each letter bbox is within 2px of its target." }, "render": "planned", "files": {}, "no": "103" }, { "slug": "phrase-push-build", "family": "type", "ko": "문장 밀어 쌓기", "en": "Phrase Push Build", "aka": [ "Kinetic center build", "중앙 정렬 문장 쌓기", "Centered Phrase Push Build", "중앙 문장 밀어 쌓기", "Vertical phrase build", "세로 문장 쌓기", "Kinetic Type Layout Relay", "키네틱 타이포 배치 릴레이" ], "oneLiner": "새 단어가 오른쪽에서 들어오면 이미 쓴 단어들이 왼쪽으로 밀려 전체 문장이 항상 중앙에 놓이는 효과", "conveys": "문장이 자라면서도 시선은 화면 중앙 한 곳에 머문다. 말이 이어지는 호흡이 만들어진다", "purposes": [ "순서·흐름", "설명", "주목 끌기" ], "media": [ "숏폼", "설명 영상", "발표" ], "level": "중급", "useWhen": [ "짧은 슬로건을 단어 단위로 쌓아 완성 문장으로 끝낼 때", "내레이션 한 문장을 리듬 있게 타이포로 보여 줄 때" ], "goodExample": "\"작게 / 시작해서 / 크게 / 배운다\" 네 마디가 0.18초 간격으로 들어오고 매번 묶음이 0.35초 안에 중앙으로 재정렬된다", "badExample": "단어를 밀 때 폭 측정을 하지 않아 문장이 오른쪽으로 치우친 채 끝난다", "avoid": [ "마디는 6개 이하로 한다. 길어지면 최종 폭이 화면을 넘는다", "밀리는 단어에 이징을 따로 걸지 않는다. 묶음 전체가 같은 이징으로 함께 움직여야 한다" ], "params": [ { "name": "마디 등장", "default": "0.35s", "range": "0.3~0.45s", "note": "오른쪽 80px에서 진입" }, { "name": "마디 간격", "default": "0.18s", "range": "0.12~0.3s", "note": "내레이션 속도에 맞춤" }, { "name": "최종 정지", "default": "1.0s", "range": "0.8~1.5s", "note": "완성 문장 읽기" }, { "name": "진입 거리", "default": "80px", "range": "60~120px", "note": "1920x1080 기준" } ], "ease": "cubic-bezier(0.2,0.8,0.2,1)", "snippet": "const parts = ['작게','시작해서','크게','배운다'];\nconst widths = parts.map((_, i) => el(i).offsetWidth + gap);\nlet sum = 0;\nparts.forEach((p, i) => {\n sum += widths[i];\n const shift = -sum / 2 + widths[i] / 2; // 묶음을 중앙에 맞추는 이동\n tl.fromTo(el(i), {x: shift + 80, opacity:0}, {x: shift, opacity:1, duration:0.35, ease:'expo.out'}, 0.3 + i * 0.18);\n for (let j = 0; j < i; j++) tl.to(el(j), {x: '-=' + widths[i] / 2, duration:0.35, ease:'expo.out'}, 0.3 + i * 0.18);\n});", "pairsWith": [ "kinetic-beats", "word-relay", "text-scatter-assemble" ], "engines": { "hyperframes": "폭은 fonts.ready 뒤 미리 측정해 x 목표를 배열로 굳힌다. 측정을 렌더 중에 하지 않아야 seek가 결정론이 된다", "reelforge": "브리프에 마디 배열과 마디 간격, 정지 시간을 싣는다. 내레이션 타이밍이 있으면 마디 시작 시각을 초 단위로 받는다", "scrolline": "마디 수로 진행률을 나눠 x 목표를 보간한다. 스크롤을 되돌리면 문장이 줄어들며 중앙에 다시 맞는다" }, "prompts": { "claude": "<대상> 문장을 마디(<마디들>) 단위로 쌓아줘. 새 마디는 오른쪽 80px에서 0.35초 expo.out으로 들어오고, 이미 놓인 마디들은 새 마디 폭의 절반만큼 왼쪽으로 함께 밀려 묶음이 항상 화면 중앙에 오게 해. 마디 간격 0.18초, 마지막 정지 1초.", "codex": "<파일>에 phrase push build를 적용해. fonts.ready 뒤 각 마디 offsetWidth를 측정해 x 목표를 배열로 계산하고 tween은 duration 0.35, ease expo.out, 간격 0.18초. 각 단계 직후(0.7초, 1.0초, 최종) 캡처해 묶음의 좌우 여백 차이가 4px 이하인지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/kinetic-center-build/registry-item.json", "license": "MIT" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/adapters/animate-text.md", "license": "unknown" }, { "title": "pixel-point/animate-text", "url": "https://raw.githubusercontent.com/pixel-point/animate-text/master/skills/animate-text/assets/specs/kinetic-center-build.json", "license": "unknown" }, { "title": "pixel-point/animate-text", "url": "https://github.com/pixel-point/animate-text", "license": "unknown" }, { "title": "pixel-point/animate-text", "url": "https://raw.githubusercontent.com/pixel-point/animate-text/master/skills/animate-text/assets/specs/short-slide-down.json", "license": "unknown" } ], "runtime": "gsap", "variants": [ "세로 쌓기", "교대 방향 입장", "배치 릴레이", "animate-text/kinetic-center-build", "kinetic-center-build", "animate-text/short-slide-down", "단어 밀기", "회전 문장" ], "oneLinerEn": "Each new word enters from the right and pushes earlier words left, keeping the whole phrase centered.", "conveysEn": "The sentence grows while the eye stays on one center point, matching spoken rhythm.", "useWhenEn": [ "When building a short slogan word by word into a finished sentence", "When showing one line of narration as rhythmic typography" ], "promptsEn": { "claude": "Build the phrase in from chunks (). Each new chunk enters from 80px right in 0.35s with expo.out, and earlier chunks shift left by half the new chunk width so the group stays centered. 0.18s between chunks, 1s final hold.", "codex": "Apply phrase push build in . After fonts.ready, measure each chunk offsetWidth and precompute x targets; tweens use duration 0.35, expo.out, gap 0.18s. Capture right after each step (0.7s, 1.0s, final) and check that left and right margins of the group differ by 4px or less." }, "render": "planned", "files": {}, "no": "104" }, { "slug": "quote-card-build", "family": "type", "ko": "인용 카드 등장", "en": "Quote Card Build", "aka": [ "Quote Card Editorial Build", "인용 카드 편집 등장" ], "oneLiner": "인용 부호와 문장이 차례로 나타나고 마지막에 출처가 붙는 등장 효과", "conveys": "문장과 발언자의 관계를 분명히 하고 읽을 시간을 준다. 인용의 무게가 순서에서 나온다", "purposes": [ "설명", "강조" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "기본", "useWhen": [ "리뷰·인터뷰 한 문장을 카드로 보여줄 때", "슬라이드에서 강연자 발언을 인용할 때" ], "goodExample": "큰 따옴표가 300ms에 나타나고 문장 3줄이 줄당 500ms, 120ms 간격으로 올라오며 250ms 뒤 저자명이 붙고 4초 유지된다", "badExample": "모든 요소가 동시에 튀어나와 읽는 순서가 없고 저자가 문장보다 먼저 보인다", "avoid": [ "저자는 항상 마지막에 등장", "줄 수 4 초과 금지", "유지 시간은 글자 수 x 0.12초 이상 확보" ], "params": [ { "name": "인용 부호", "default": "300ms", "range": "200~400ms", "note": "scale 0.8→1, 페이드" }, { "name": "줄당 지속", "default": "500ms", "range": "400~700ms", "note": "아래에서 24px 상승" }, { "name": "줄 간 지연", "default": "120ms", "range": "80~160ms", "note": "읽는 순서" }, { "name": "저자 지연", "default": "250ms", "range": "150~400ms", "note": "마지막 줄 뒤" }, { "name": "유지", "default": "4s", "range": "3~6s", "note": "읽기 시간" } ], "ease": "power2.out", "snippet": "tl.from('.mark', { scale: 0.8, opacity: 0, duration: 0.3, ease: 'power2.out' }, 0)\n .from('.line', { y: 24, opacity: 0, duration: 0.5, stagger: 0.12, ease: 'power2.out' }, 0.3)\n .from('.author', { x: -16, opacity: 0, duration: 0.4, ease: 'power2.out' }, '>+0.25');", "pairsWith": [ "word-rise-fade", "typewriter", "fade-slide" ], "engines": { "hyperframes": "한 paused 타임라인에 부호, 줄, 저자를 순서대로 놓고 마지막에 hold용 빈 tween을 둬 클립 길이를 맞춘다", "reelforge": "인용 카드 씬에 인용문, 저자, 유지 4s를 파라미터로 싣고 줄바꿈은 한국어 의미 단위로 미리 나눈다", "scrolline": "등장은 진행률 0~0.4에 몰고 나머지는 홀드로 둔다. 스크롤이 멈춰도 저자까지 읽히도록 저자는 0.4 이전에 끝낸다" }, "prompts": { "claude": "<대상>에 인용 카드를 만들어줘. 큰 따옴표가 0.3초에 scale 0.8→1로 나타나고, 문장 <줄 수>줄이 0.3초부터 줄당 0.5초, 0.12초 간격으로 24px 위로 올라오며 나타나고, 마지막 줄 0.25초 뒤 저자 '<이름>'이 왼쪽에서 들어와. 이징 power2.out, 전체 4초 유지. 줄바꿈은 의미 단위로 나눠.", "codex": "<파일>의 인용 카드를 tl 순서(부호 0s, 줄 0.3s stagger 0.12, 저자 마지막+0.25s)로 구성해. 0.2초에 부호만, 1.0초에 문장이 다 나타났고 저자는 아직 없는지, 2.2초에 저자가 보이는지 캡처로 확인해." }, "sources": [ { "title": "jschr/textillate", "url": "https://github.com/jschr/textillate", "license": "MIT" }, { "title": "motion-canvas/motion-canvas", "url": "https://github.com/motion-canvas/motion-canvas", "license": "MIT" } ], "runtime": "gsap", "variants": [ "단일 문장 인용", "여러 줄 인용", "핵심어 강조" ], "oneLinerEn": "The quotation mark and lines appear in order and the attribution attaches last.", "conveysEn": "Makes the relation between statement and speaker clear and gives time to read; the weight of a quote comes from the order.", "useWhenEn": [ "Show a single review or interview line as a card.", "Quote a speaker on a slide." ], "promptsEn": { "claude": "Build a quote card in . The large quotation mark appears at 0.3 seconds with scale 0.8 to 1; lines rise 24px and fade in from 0.3 seconds, 0.5 seconds per line and 0.12 seconds apart; 0.25 seconds after the last line the attribution '' slides in from the left. Ease power2.out and hold for 4 seconds in total. Break lines at meaning units.", "codex": "Build the quote card in as a timeline in order (mark at 0s, lines at 0.3s with stagger 0.12, attribution at last + 0.25s). Capture at 0.2s to see only the mark, at 1.0s to see all lines but no attribution, and at 2.2s to confirm the attribution is visible." }, "render": "planned", "files": {}, "no": "105" }, { "slug": "repeat-text-wall", "family": "type", "ko": "반복 텍스트 벽", "en": "Repeated Text Wall", "aka": [ "Repeated Text Wall Build", "반복 텍스트 벽 생성", "Repeated Text Fragment Fan", "반복 텍스트 조각 펼침" ], "oneLiner": "한 문구가 여러 행으로 복제되어 화면을 채우는 효과", "conveys": "문구의 존재감과 반복이 주는 압도감을 만든다. 리듬이 있는 화면 질감이 된다", "purposes": [ "주목 끌기", "분위기" ], "media": [ "숏폼", "발표", "웹 UI" ], "level": "기본", "useWhen": [ "브랜드명이나 캠페인 문구를 배경 패턴으로 확장할 때", "타이틀 끝에서 화면을 문구로 가득 채워 강한 인상을 남길 때" ], "goodExample": "'MOVE' 한 줄이 7행으로 늘어나며 행 지연 80ms, 글자 지연 20ms로 위아래에서 퍼져 가운데 행만 선명하다", "badExample": "모든 행이 같은 밝기로 채워져 어느 행이 주 문구인지 알 수 없다", "avoid": [ "가운데 행 외에는 opacity 0.15~0.5로 낮춘다", "행 수 9 초과 금지", "반복 문구가 긴 문장이면 쓰지 않는다" ], "params": [ { "name": "행 수", "default": "7", "range": "5~9", "note": "화면을 채우는 수" }, { "name": "행 지연", "default": "80ms", "range": "50~120ms", "note": "가운데에서 바깥으로" }, { "name": "글자 지연", "default": "20ms", "range": "10~40ms", "note": "행 내부" }, { "name": "지속", "default": "700ms", "range": "500~900ms", "note": "행당" } ], "ease": "power3.out", "snippet": "const mid = Math.floor(rows.length / 2);\nrows.forEach((row, i) => {\n const d = Math.abs(i - mid);\n tl.from(row, { yPercent: (i - mid) * -40, opacity: 0, duration: 0.7, ease: 'power3.out' }, d * 0.08);\n gsap.set(row, { opacity: d === 0 ? 1 : Math.max(0.15, 0.6 - d * 0.15) });\n});", "pairsWith": [ "stagger", "marquee", "kinetic-beats" ], "engines": { "hyperframes": "행 복제는 빌드 시 DOM으로 만들고 tween은 yPercent/opacity만 쓴다. 행 수가 많아도 transform이라 렌더 비용이 낮다", "reelforge": "타이포 씬 브리프에 문구, 행 수 7, 가운데 행 강조 규칙을 명시한다", "scrolline": "행 확장을 진행률에 매핑하고 스크롤이 진행되면 바깥 행이 더 늦게 열리게 지연을 진행률 비율로 둔다" }, "prompts": { "claude": "<대상>의 문구 '<문구>'를 7행으로 복제해 화면을 채워줘. 가운데 행이 먼저 나타나고 위아래로 80ms씩 늦게 퍼지며, 행당 0.7초 power3.out, 행 내부 글자는 20ms 간격. 가운데 행만 opacity 1이고 바깥은 0.15~0.5로 낮춰. 한글은 음절 단위 분리.", "codex": "<파일>에 문구 7행 복제 레이어를 만들고 tl.from(yPercent, opacity)을 |i-mid|*0.08 위치에 건다. 0.3초, 0.8초, 2초 캡처로 가운데 행이 가장 선명하고 바깥 행 opacity가 0.5 이하인지 확인해." }, "sources": [ { "title": "codrops/RepetitiveTypography", "url": "https://github.com/codrops/RepetitiveTypography", "license": "MIT" }, { "title": "codrops/TextRepetitionEffect", "url": "https://github.com/codrops/TextRepetitionEffect", "license": "MIT" } ], "runtime": "gsap", "variants": [ "조각 복사본 펼침", "상하 확장", "번갈아 글자 복제", "격자 확장", "세로 부채", "대각선 부채", "투명도 시차" ], "oneLinerEn": "One phrase is duplicated into many rows that fill the screen.", "conveysEn": "Builds presence and the overwhelming feel of repetition into a rhythmic screen texture.", "useWhenEn": [ "Extend a brand name or campaign line into a background pattern.", "End a title by flooding the frame with the phrase." ], "promptsEn": { "claude": "Duplicate '' into 7 rows filling . The center row appears first and the rows spread up and down 80ms apart, 0.7 seconds per row with power3.out and 20ms between letters within a row. Only the center row is at opacity 1; the outer rows sit between 0.15 and 0.5. Split Korean by syllable.", "codex": "In build a 7-row duplicate layer and add tl.from (yPercent, opacity) at |i-mid|*0.08. Capture at 0.3s, 0.8s and 2s and verify the center row is the sharpest and outer rows are at opacity 0.5 or lower." }, "render": "planned", "files": {}, "no": "106" }, { "slug": "scene-integrated-title", "family": "type", "ko": "장면 통합 타이틀", "en": "Scene Integrated Title", "aka": [ "Integrated Diegetic Title", "장면에 통합된 타이틀" ], "oneLiner": "제목 글자가 장면의 선이나 인물 동선과 맞물려 드러나고 가려지는 효과", "conveys": "제목이 이야기 세계 안에 놓여 있다는 느낌을 준다. 자막이 아니라 장면의 일부로 읽힌다", "purposes": [ "브랜딩", "분위기" ], "media": [ "설명 영상", "숏폼" ], "level": "고급", "useWhen": [ "영상 오프닝에서 제목이 사물 뒤로 지나가게 할 때", "건물·기둥 같은 선과 글자를 정렬해 등장시킬 때" ], "goodExample": "제목이 건물 실루엣 뒤에 있다가 인물이 지나가는 2초 동안 같은 속도로 마스크가 열려 글자가 드러난다", "badExample": "마스크 이동이 장면 오브젝트와 다른 속도여서 글자가 배경과 따로 노는 것처럼 뜬다", "avoid": [ "마스크 시간표를 장면 오브젝트와 반드시 같은 값으로 맞춘다", "전경 마스크 없이 글자만 얹지 않는다", "장면이 복잡하면 글자 대비를 확보한다" ], "params": [ { "name": "장면 연동 시간", "default": "2s", "range": "1.5~3s", "note": "오브젝트 이동과 동일" }, { "name": "마스크 이동", "default": "오브젝트와 동일 px", "range": "-", "note": "같은 tween에서 구동" }, { "name": "글자 불투명도", "default": "1", "range": "0.9~1", "note": "깊이감을 위해 살짝 낮출 수 있음" }, { "name": "이징", "default": "오브젝트와 동일", "range": "none~power2.inOut", "note": "동기 필수" } ], "ease": "power1.inOut", "snippet": "/* 전경 마스크 이미지(.fg, 알파 있음)가 제목(.title) 위에 겹침 */\ntl.to('.actor', { x: 900, duration: 2, ease: 'power1.inOut' }, 0)\n .to('.fg-mask', { x: 900, duration: 2, ease: 'power1.inOut' }, 0)\n .fromTo('.title', { opacity: 0 }, { opacity: 1, duration: 0.6 }, 0.9);", "pairsWith": [ "counter-moving-matte", "mask-reveal", "parallax" ], "engines": { "hyperframes": "오브젝트와 마스크를 같은 tween 객체나 같은 position 값으로 묶는다. 마스크 알파 PNG를 미리 준비해 레이어 순서 title, fg-mask 순으로 쌓는다", "reelforge": "장면 생성 후 전경 알파 컷아웃을 별도 자산으로 요청하도록 브리프에 명시한다. 없으면 이 효과는 쓰지 않는다", "scrolline": "오브젝트 이동과 마스크 이동을 같은 진행률 값으로 함께 매핑한다. 정지해도 제목이 가려진 상태가 어색하지 않게 홀드 지점을 마스크가 열린 위치에 둔다" }, "prompts": { "claude": "<대상> 장면에서 제목 '<문구>'가 전경 오브젝트(<오브젝트>) 뒤에 놓이게 레이어를 나눠줘. 오브젝트와 전경 마스크가 2초 동안 x 900px을 power1.inOut으로 함께 움직이고, 그 사이 제목이 0.9초부터 0.6초 동안 나타나게 해. 오브젝트와 마스크의 tween 값은 항상 동일해야 해.", "codex": "<파일>에서 .actor와 .fg-mask를 동일한 x, duration, ease의 tween으로 구동하고 제목은 그 뒤 레이어에 둬. 0.5초에 제목이 가려져 있고 1.5초에 일부만 드러나 있으며 2.5초에 완전히 읽히는지 캡처로 확인해. 마스크와 오브젝트 x 값이 각 시점에 같은지 스크립트로 검사해." }, "sources": [ { "title": "Art of the Title", "url": "https://www.artofthetitle.com/title/catch-me-if-you-can/", "license": "unknown" } ], "runtime": "svg", "variants": [ "실루엣 가림", "건축선 연결", "선이 글자로 이어짐" ], "oneLinerEn": "The title is revealed and hidden in step with lines or character paths inside the scene.", "conveysEn": "Places the title inside the story world so it reads as part of the scene rather than a caption.", "useWhenEn": [ "Let a title pass behind an object in an opening.", "Align letters with lines such as columns or buildings as they appear." ], "promptsEn": { "claude": "In the scene put the title '' behind the foreground object () by splitting layers. The object and the foreground mask move x 900px together over 2 seconds with power1.inOut, while the title fades in from 0.9 seconds over 0.6 seconds. The object and mask tween values must always be identical.", "codex": "In drive .actor and .fg-mask with the same x, duration and ease, and put the title on a layer behind. Capture at 0.5s (title hidden), 1.5s (partly revealed) and 2.5s (fully readable), and check by script that the mask and object x values match at each time." }, "render": "planned", "files": {}, "no": "107" }, { "slug": "spatial-word-composition", "family": "type", "ko": "공간 키네틱 문장", "en": "Spatial Word Composition", "aka": [ "Spatial Kinetic Word Composition", "공간 키네틱 문장 구성" ], "oneLiner": "발화 순서대로 단어가 크기와 각도를 바꾸며 화면 공간을 채워 하나의 타이포 포스터가 되는 구성", "conveys": "문장의 강세와 의미 관계가 배치로 전달된다. 말하는 리듬이 그대로 그림이 된다", "purposes": [ "강조", "설명", "분위기" ], "media": [ "숏폼", "설명 영상", "발표" ], "level": "고급", "useWhen": [ "내레이션 한 단락을 키워드 중심 타이포 포스터로 구성할 때", "감성적인 문장을 크고 작은 글자 배치로 표현할 때" ], "goodExample": "단어가 250~600ms 간격으로 하나씩 놓이고 핵심어는 1.8배 크기로 최대 15도 기울어 화면 중앙에 자리 잡는다", "badExample": "단어를 무작위로 흩뿌려 서로 겹치고 읽는 순서를 알 수 없다", "avoid": [ "모든 단어의 목표 좌표를 사전에 손으로 정하거나 격자로 계산한다. 런타임 난수 배치는 겹침을 낳는다", "핵심어는 문장당 1~2개. 전부 크면 강조가 사라진다" ], "params": [ { "name": "단어 간격", "default": "250~600ms", "range": "200~700ms", "note": "발화 속도에 맞춤" }, { "name": "핵심어 크기", "default": "1.8배", "range": "1.5~2.2배", "note": "본문 대비" }, { "name": "최대 회전", "default": "15도", "range": "5~15도", "note": "핵심어 위주" }, { "name": "등장", "default": "0.3s", "range": "0.2~0.4s", "note": "scale 0.9에서 1" } ], "ease": "power3.out", "snippet": "// 단어 목표: {text, x, y, size, rot, t} 배열을 사전에 확정\nwords.forEach(w => {\n tl.fromTo(el(w), {opacity:0, scale:0.9, rotation:w.rot * 0.5}, {opacity:1, scale:1, rotation:w.rot, duration:0.3, ease:'power3.out'}, w.t);\n});", "pairsWith": [ "kinetic-beats", "word-emphasis", "phrase-push-build" ], "engines": { "hyperframes": "좌표와 시각을 담은 데이터 배열에서 timeline을 만든다. 폰트 측정 뒤 겹침 검사를 스크립트로 돌려 통과시킨다", "reelforge": "브리프에 단어별 시각, 크기, 좌표, 회전 배열과 핵심어 표시를 싣는다. 겹침 검사 스크립트를 함께 준다", "scrolline": "단어의 등장 시각을 진행률 위치로 환산한다. 되감으면 단어가 순서대로 사라진다" }, "prompts": { "claude": "<대상> 문장을 공간 타이포 구성으로 배치해줘. 단어별 {시각, x, y, 크기, 회전} 배열을 격자 위에서 미리 정하고, 핵심어(<핵심어>)는 1.8배 크기, 최대 15도로 중앙에 놓아. 각 단어는 scale 0.9에서 1, opacity 0에서 1로 0.3초 power3.out. 발화 간격은 250~600ms.", "codex": "<파일>에 spatial word composition을 적용해. 단어 목표 배열을 확정하고 각 단어 scale 0.9에서 1, rotation 절반에서 목표 각, opacity 0에서 1을 0.3초 power3.out으로 w.t에 배치. 완성 프레임 캡처 후 단어 bbox 겹침이 0건인지 검사하고 0.5초 시점에는 첫 단어만 보이는지 확인해." }, "sources": [ { "title": "dcmcand/dynamic-typography-videos", "url": "https://github.com/dcmcand/dynamic-typography-videos", "license": "Apache-2.0" }, { "title": "motion-canvas/motion-canvas", "url": "https://github.com/motion-canvas/motion-canvas", "license": "MIT" } ], "runtime": "gsap", "variants": [ "질문과 답 배치", "핵심어 확대", "대각선 문장" ], "oneLinerEn": "Words appear in spoken order, changing size and angle to fill the frame as one typographic composition.", "conveysEn": "Emphasis and meaning relations are carried by placement; speech rhythm becomes a picture.", "useWhenEn": [ "Turning a narration paragraph into a keyword-led type poster", "Expressing an emotional sentence through big and small type" ], "promptsEn": { "claude": "Lay out the sentence in as a spatial typographic composition. Predefine an array of {time, x, y, size, rotation} per word on a grid; the key words () are 1.8x size and up to 15 degrees, near center. Each word scales 0.9 to 1 and fades in over 0.3s power3.out, with 250 to 600ms between words.", "codex": "Apply spatial word composition in . Fix a target array and animate each word scale 0.9 to 1, rotation half to target, opacity 0 to 1 over 0.3s power3.out at w.t. Capture the finished frame and check word bounding boxes overlap in 0 cases; at 0.5s only the first word should be visible." }, "render": "planned", "files": {}, "no": "108" }, { "slug": "split-flap", "family": "type", "ko": "분할 플랩 문자판", "en": "Split Flap Display", "aka": [ "Split flap", "Flipboard Text", "플랩 보드 글자", "flipboard-3d-rotateX", "hacker-flip-3d", "Split Flap Number Flip", "분할 플랩 숫자 뒤집기" ], "oneLiner": "글자판의 위아래 반쪽이 경첩을 따라 넘어가며 새 글자가 만들어지는 기계식 문자판", "conveys": "공항 안내판 같은 기계식 정보 갱신. 순차로 바뀌는 상태를 손에 잡히게 전달한다", "purposes": [ "피드백", "분위기" ], "media": [ "웹 UI", "숏폼", "제품 시연" ], "level": "고급", "useWhen": [ "도착·출발 시각, 순위, 재고처럼 표로 갱신되는 짧은 정보", "복고 기계 분위기의 카운터나 타이틀" ], "goodExample": "\"서울\"이 \"부산\"으로 바뀔 때 글자마다 0.2초씩 위 반쪽이 아래로 접히고 글자 사이 시작 차 0.04초로 왼쪽부터 정착한다", "badExample": "플랩 수를 늘려 글자당 1초 넘게 넘기거나 perspective를 100px로 줘 형태가 찌그러진다", "avoid": [ "perspective 400px 안팎을 지키고 rotateX만 쓴다. Y 회전을 섞으면 경첩이 어색하다", "긴 문장에는 쓰지 않는다. 8글자 이하 라벨에 적합하다" ], "params": [ { "name": "글자당 시간", "default": "0.2s", "range": "0.15~0.3s", "note": "한 번 넘김" }, { "name": "시작 차", "default": "0.04s", "range": "0.02~0.06s", "note": "왼쪽부터" }, { "name": "perspective", "default": "400px", "range": "300~600px", "note": "깊이감" }, { "name": "중간 글자 수", "default": "2", "range": "0~4", "note": "넘기는 동안 거치는 임의 글자" } ], "ease": "power1.in (상반) / power1.out (하반)", "snippet": "chars.forEach((c, i) => {\n const t = 0.3 + i * 0.04;\n tl.to(top(i), {rotationX:-90, duration:0.1, ease:'power1.in', transformOrigin:'50% 100%'}, t)\n .set(topBack(i), {opacity:1}, t + 0.1)\n .fromTo(bottomNew(i), {rotationX:90}, {rotationX:0, duration:0.1, ease:'power1.out', transformOrigin:'50% 0%'}, t + 0.1);\n});\n// 부모: perspective:400px; 반쪽은 clip-path로 상/하 절반만 표시", "pairsWith": [ "glyph-roll", "flip-decode-text", "letter-flip-3d" ], "engines": { "hyperframes": "반쪽 4장(위 이전, 위 새, 아래 이전, 아래 새)을 clip-path로 구성하고 rotationX만 보간한다. backface-visibility hidden으로 뒷면 깜빡임을 막는다", "reelforge": "브리프에 글자열, 글자당 0.2초, 시작 차 0.04초, perspective 400px를 싣는다. 반쪽 마크업은 워커가 생성하고 값만 파라미터로 노출한다", "scrolline": "진행률에 글자별 위상을 곱해 rotationX를 결정한다. 넘김이 반쯤 걸친 채 멈추지 않도록 구간 끝을 스냅한다" }, "prompts": { "claude": "<대상> 라벨을 분할 플랩 문자판으로 바꿔줘. 글자마다 위 반쪽이 0.1초 동안 -90도로 접히고 아래 반쪽이 새 글자로 0.1초에 펼쳐지게, 글자 시작 차 0.04초, perspective 400px, 경첩은 글자 중앙선. clip-path로 반쪽을 나누고 rotateX만 써.", "codex": "<파일>에 split flap을 적용해. 글자별 상/하 반쪽을 clip-path로 분리, 상반 rotationX 0에서 -90(0.1초), 하반 90에서 0(0.1초), 시작 차 0.04, perspective 400px. 0.35초·0.42초·1.0초를 캡처해 접히는 중, 펼쳐지는 중, 완료 상태에서 뒷면이 비치지 않는지 확인해." }, "sources": [ { "title": "ui.aceternity.com", "url": "https://ui.aceternity.com/components/text-flipping-board", "license": "unknown" }, { "title": "DavidHDev/react-bits", "url": "https://github.com/DavidHDev/react-bits", "license": "MIT + Commons Clause v1.0" }, { "title": "magicuidesign/magicui", "url": "https://magicui.design/docs/components/text-3d-flip", "license": "MIT" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/split-flap-board/registry-item.json", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/05-text-animator.md#flipboard-3d-rotateX", "license": "Apache-2.0" } ], "runtime": "css", "variants": [ "숫자 플립", "글자 플랩 보드", "Text Flipping Board", "SplitFlapText", "Text 3D Flip", "text-3d-flip", "text-flipping-board", "아래 경첩" ], "oneLinerEn": "The top and bottom halves of each character card flip along a hinge to form a new glyph.", "conveysEn": "Mechanical, sequential information updates, like an airport board, made tangible.", "useWhenEn": [ "Short tabular info that updates, such as times, rankings or stock counts", "A retro mechanical counter or title" ], "promptsEn": { "claude": "Turn the label in into a split-flap display. Per character, the top half folds to -90deg in 0.1s and the bottom half unfolds to the new glyph in 0.1s, starting 0.04s apart, perspective 400px, hinge at the glyph midline. Split halves with clip-path and use rotateX only.", "codex": "Apply split flap in . Split each glyph into top and bottom halves via clip-path; top rotationX 0 to -90 (0.1s), bottom 90 to 0 (0.1s), stagger 0.04, perspective 400px. Capture at 0.35s, 0.42s and 1.0s and verify no back face bleeds through during fold, unfold or at rest." }, "render": "planned", "files": {}, "no": "109" }, { "slug": "strikethrough-replace", "family": "type", "ko": "취소선 교체", "en": "Strikethrough Replace", "aka": [ "Strikethrough replace" ], "oneLiner": "기존 문구에 취소선이 그어진 뒤 새 문구가 옆이나 아래에 나타나는 교체 효과", "conveys": "이전 주장이 폐기되고 새 값으로 바뀌었다는 사실이 순서로 전달된다", "purposes": [ "강조", "비교", "피드백" ], "media": [ "설명 영상", "발표", "제품 시연" ], "level": "기본", "useWhen": [ "가격·수치·정책처럼 바뀐 값을 이전 값과 함께 보여 줄 때", "틀린 통념을 지우고 바른 주장을 세우는 장면" ], "goodExample": "\"월 29,000원\"에 0.35초간 선이 좌에서 우로 그어지고 0.1초 뒤 \"월 19,000원\"이 아래에서 올라온다", "badExample": "취소선과 새 문구가 동시에 나오거나 취소선이 글자보다 옅어 지워졌는지 알 수 없다", "avoid": [ "취소선이 끝나기 전에 새 문구를 띄우지 않는다. 순서가 뒤섞이면 어느 쪽이 정답인지 모호하다", "옛 문구는 지운 뒤에도 opacity 0.45 이상 남겨 무엇이 바뀌었는지 보이게 한다" ], "params": [ { "name": "취소선 시간", "default": "0.35s", "range": "0.25~0.5s", "note": "좌에서 우로 scaleX 0에서 1" }, { "name": "취소선과 교체 사이", "default": "0.1s", "range": "0.05~0.2s", "note": "정지가 있어야 지웠다는 것이 읽힘" }, { "name": "새 문구 등장", "default": "0.25s", "range": "0.2~0.4s", "note": "y 12px 아래에서 위로" }, { "name": "옛 문구 남는 불투명도", "default": "0.45", "range": "0.35~0.6", "note": "지운 뒤 흐리게 유지" } ], "ease": "power2.out", "snippet": "tl.fromTo('.strike', {scaleX:0}, {scaleX:1, duration:0.35, ease:'power2.inOut', transformOrigin:'left center'}, 0.4)\n .to('.old', {opacity:0.45, duration:0.2}, 0.75)\n .fromTo('.new', {y:12, opacity:0}, {y:0, opacity:1, duration:0.25, ease:'power2.out'}, 0.85);", "pairsWith": [ "mask-reveal", "word-emphasis", "diff-reveal" ], "engines": { "hyperframes": "취소선은 span의 background나 별도 div의 scaleX로 만든다. 세 tween을 한 paused 타임라인에 position으로 고정해 seek 가능하게 한다", "reelforge": "씬 브리프에 옛 값, 새 값, 취소선 0.35초, 정지 0.1초를 넣는다. 새 값 위치(옆/아래)는 파라미터로 노출한다", "scrolline": "진행률 0~0.4에 선, 0.4~0.7에 새 문구를 매핑한다. scrub 중에는 선이 뒤로 감기므로 scaleX 값만 진행률에 묶는다" }, "prompts": { "claude": "<대상>의 \"이전 값\"에 취소선이 좌에서 우로 0.35초 동안 그어지게 하고, 0.1초 쉰 뒤 \"새 값\"이 12px 아래에서 0.25초에 올라오게 만들어줘. 옛 값은 취소선 뒤 불투명도 0.45로 남겨. 세 동작은 한 GSAP 타임라인 하나로 묶어.", "codex": "<파일>에 취소선 교체를 적용해. .strike scaleX 0에서 1을 0.4초 시작, 0.35초, power2.inOut. .old opacity 0.45는 0.75초, .new는 0.85초에 y 12에서 0으로 0.25초. 0.3초·0.7초·1.3초 시점을 캡처해 선 진행 중, 선 완료, 새 문구 완료가 순서대로 보이는지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/strikethrough-replace/registry-item.json", "license": "Apache-2.0" } ], "runtime": "svg", "variants": [], "oneLinerEn": "A strikethrough is drawn over the old text, then the new text appears beside or below it.", "conveysEn": "It shows that a claim was withdrawn and replaced, through order alone.", "useWhenEn": [ "When showing a changed price, figure or policy next to its previous value", "When striking down a myth and putting the correct claim in its place" ], "promptsEn": { "claude": "On , draw a strikethrough over the old value left to right in 0.35s, wait 0.1s, then bring in the new value from 12px below in 0.25s. Leave the old value at 0.45 opacity after the strike. Bind all three moves in a single GSAP timeline.", "codex": "Apply strikethrough replace in . .strike scaleX 0 to 1 at 0.4s, 0.35s, power2.inOut; .old opacity 0.45 at 0.75s; .new y 12 to 0 at 0.85s over 0.25s. Capture at 0.3s, 0.7s and 1.3s and verify the strike mid-draw, the strike complete, and the new text settled, in that order." }, "render": "planned", "files": {}, "no": "110" }, { "slug": "text-scatter-assemble", "family": "type", "ko": "흩어진 글자 조립", "en": "Text Scatter Assemble", "aka": [ "Letter Scatter Assemble", "흩어진 글자 모으기", "Scatter Title Assemble", "netflix-title-converge", "depth-scatter-assemble", "Text scatter assembly", "문자 흩어짐과 조립" ], "oneLiner": "사방에 흩어진 글자가 제자리로 모여 읽을 수 있는 문장이 되는 효과", "conveys": "흩어진 정보가 정리되어 의미를 갖는 순간을 보여준다. 도착 직전에 주목이 한곳으로 모인다", "purposes": [ "주목 끌기", "설명" ], "media": [ "숏폼", "설명 영상", "발표" ], "level": "중급", "useWhen": [ "제목 등장에서 '정리된다, 하나로 모인다'는 메시지를 함께 전할 때", "오프닝 타이틀을 시네마틱하게 열 때" ], "goodExample": "12글자 제목이 x ±200%, y ±150% 위치에서 0.9초에 걸쳐 50ms 간격으로 모여 정확히 제자리에 멈춘다", "badExample": "글자마다 회전과 크기를 무작위로 크게 줘 어수선하고, 도착 후에도 잔여 회전이 남는다", "avoid": [ "시작 위치를 Math.random으로 정하지 않는다(시드 난수 사용)", "도착 시점에 회전·투명도 잔여 금지", "글자 수 20자 초과 문장에는 쓰지 않는다" ], "params": [ { "name": "시작 범위", "default": "x ±200%, y ±150%", "range": "±100~±250%", "note": "글자 자기 크기 기준" }, { "name": "글자 간 지연", "default": "50ms", "range": "30~80ms", "note": "도착 순서가 읽히는 간격" }, { "name": "지속", "default": "900ms", "range": "700~1200ms", "note": "글자당 이동 시간" }, { "name": "이징", "default": "power3.out", "range": "power2.out~power4.out", "note": "도착 직전 감속" }, { "name": "난수 시드", "default": "9", "range": "임의 정수", "note": "재현 가능" } ], "ease": "power3.out", "snippet": "let s = 9; const rnd = () => (s = (s * 16807) % 2147483647) / 2147483647 - 0.5;\nchars.forEach((el, i) => {\n tl.from(el, { x: rnd() * 4 * el.offsetWidth, y: rnd() * 3 * el.offsetHeight,\n rotation: rnd() * 60, opacity: 0, duration: 0.9, ease: 'power3.out' }, i * 0.05);\n});", "pairsWith": [ "stagger", "letter-anagram-shift", "text-particle-dissolve", "depth-assemble" ], "engines": { "hyperframes": "글자를 span으로 미리 나눠 tl.from으로 걸고 난수는 시드 함수만 쓴다. paused 상태로 0.9초 뒤 상태가 최종 배치와 같은지 seek로 확인한다", "reelforge": "텍스트 애니메이터 씬에 seed=9, 범위, 글자 지연을 파라미터로 전달한다. 글자 분리는 한글이면 음절 단위 span으로 한다", "scrolline": "모임 정도를 진행률 0..1에 매핑한다. scrub에서는 되감아도 같은 궤적이 나오도록 시드 고정과 ease-out을 쓴다" }, "prompts": { "claude": "<대상> 제목 '<문구>'를 글자(한글은 음절) 단위 span으로 나눠, 시드 9 난수로 정한 x ±200%, y ±150%, 회전 ±30도 위치에서 제자리로 모이게 해줘. 글자당 0.9초, 50ms 간격, power3.out이고 마지막 글자 도착 뒤 1초 정지. 자모 단위로 쪼개지 말고 paused 타임라인에서 seek 되게 만들어.", "codex": "<파일>의 제목을 글자 span으로 분리하고 시드 난수(seed 9)로 시작 오프셋을 계산해 tl.from을 i*0.05 간격으로 건다. Math.random 금지. 0.3초에 글자가 분산돼 있고 1.8초에 원래 위치·회전 0으로 완전히 정렬됐는지 캡처로 확인하고, 한글은 음절이 깨지지 않았는지도 확인해." }, "sources": [ { "title": "codrops/OnScrollTypographyAnimations", "url": "https://github.com/codrops/OnScrollTypographyAnimations", "license": "MIT" }, { "title": "codrops/TextBlockTransitions", "url": "https://github.com/codrops/TextBlockTransitions", "license": "MIT" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/05-text-animator.md#netflix-title-converge", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/gallery-index.json", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/fragments/typo/netflix-converge.html", "license": "Apache-2.0" } ], "runtime": "gsap", "variants": [ "조각 조립", "낱자로 흩어지기", "평면 산개", "깊이 산개", "문장 해체 후 다음 문장 조립", "평면 산포", "회전 산포", "깊이 산포" ], "oneLinerEn": "Letters scattered around the frame fly in and settle into a readable sentence.", "conveysEn": "Shows scattered information being organized into meaning, with attention converging just before arrival.", "useWhenEn": [ "Open a title while also conveying that pieces come together.", "Start a cinematic opening title." ], "promptsEn": { "claude": "Split the title '' of into per-character spans (per syllable for Korean, never per jamo). Using a seed-9 pseudo random generator, start each from x +/-200%, y +/-150%, rotation +/-30 degrees and converge to place: 0.9 seconds each, 50ms apart, power3.out, holding 1 second after the last one lands. Keep it on one paused timeline so it can be seeked.", "codex": "In split the title into character spans, compute start offsets from a seeded generator (seed 9), and add tl.from calls at i*0.05. No Math.random. Capture at 0.3s to confirm the letters are scattered and at 1.8s to confirm rotation and offset are exactly 0; for Korean also confirm no syllable was broken." }, "render": "planned", "files": {}, "no": "111" }, { "slug": "text-wave", "family": "type", "ko": "글자 웨이브", "en": "Text Wave", "aka": [ "offset-cascade-wave", "Text wave", "텍스트 파동", "Text Wave Selector", "텍스트 파동 셀렉터", "Wiggly selector", "Expression selector", "Text Baseline Wave" ], "oneLiner": "좁은 파동이 문장을 훑으며 걸린 글자만 잠깐 올라갔다 돌아오는 효과", "conveys": "읽는 방향과 리듬을 따라 시선이 이동한다. 문장이 살아 숨 쉬는 느낌을 준다", "purposes": [ "분위기", "순서·흐름" ], "media": [ "숏폼", "웹 UI", "설명 영상" ], "level": "기본", "useWhen": [ "대기 화면이나 로딩 문구를 가볍게 움직이게 할 때", "음악·리듬이 있는 영상에서 제목을 박자에 맞춰 흔들 때" ], "goodExample": "\"Loading your story\" 위를 폭 3글자, 진폭 16px의 파동이 1.2초마다 한 번 지나간다", "badExample": "진폭을 60px까지 키우고 모든 글자가 동시에 출렁여 문장이 읽히지 않는다", "avoid": [ "진폭은 글자 크기의 30% 이하로 둔다", "파동은 한 번에 3~4글자만 걸리게 한다. 전체가 함께 움직이면 파동이 아니다" ], "params": [ { "name": "진폭", "default": "16px", "range": "8~24px", "note": "y 이동" }, { "name": "파동 폭", "default": "3글자", "range": "2~5글자", "note": "한 번에 걸리는 글자" }, { "name": "주기", "default": "1.2s", "range": "0.8~2.0s", "note": "문장 끝까지 한 번" }, { "name": "이징", "default": "sine.inOut", "range": "sine", "note": "올라갔다 내려오는 곡선" } ], "ease": "sine.inOut", "snippet": "const o = {p:0}, W = 3;\ntl.to(o, {p:1, duration:1.2, ease:'none', onUpdate(){\n chars.forEach((_, i) => {\n const d = (i - o.p * (N + W) + W / 2) / (W / 2);\n const y = Math.abs(d) < 1 ? -16 * Math.cos(d * Math.PI / 2) : 0;\n node(i).style.transform = `translateY(${y}px)`;\n });\n}}, 0);", "pairsWith": [ "variable-font-weight-wave", "letter-spin-in", "stagger" ], "engines": { "hyperframes": "파동 위치 p를 t의 함수로 두고 글자별 y를 onUpdate에서 계산한다. repeat를 쓰지 않고 필요한 만큼 타임라인에 배치해 seek를 유지한다", "reelforge": "브리프에 문장, 진폭 16px, 파동 폭 3글자, 주기 1.2초, 반복 횟수를 싣는다. 반복 횟수는 총 길이에 맞게 정해 무한 반복은 피한다", "scrolline": "진행률 p를 파동 위치로 그대로 쓴다. 스크롤에 따라 파동이 전진하고 멈추면 문장이 평평하게 돌아온다" }, "prompts": { "claude": "<대상> 문장에 텍스트 웨이브를 넣어줘. 폭 3글자, 진폭 16px의 파동이 1.2초에 문장을 왼쪽에서 오른쪽으로 한 번 훑고, 걸린 글자만 cos 곡선으로 올라갔다 내려와. 무한 repeat 대신 총 길이만큼 타임라인에 배치해.", "codex": "<파일>에 text wave를 적용해. 음절 span에 대해 onUpdate에서 d=(i-p*(N+3)+1.5)/1.5, |d|<1이면 y=-16*cos(d*PI/2), 주기 1.2초. 0.3초·0.6초·0.9초를 캡처해 상승한 글자의 위치가 왼쪽에서 오른쪽으로 이동하고 동시에 3글자 이하만 올라가 있는지 확인해." }, "sources": [ { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/05-text-animator.md#offset-cascade-wave", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/gallery-index.json", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/fragments/typo/cascade-wave.html", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/fragments/typo/cascade-wave.meta.json", "license": "Apache-2.0" }, { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-split-text-wavy", "license": "unknown" } ], "runtime": "gsap", "variants": [ "두 열 반대 위상", "셀렉터 파동", "물결 휨", "좌우 웨이브", "색과 위치 파동", "typo/cascade-wave", "수직 파동", "회전 파동" ], "oneLinerEn": "A narrow wave sweeps a sentence and lifts only the glyphs it passes.", "conveysEn": "The eye follows reading direction and rhythm, and the sentence seems to breathe.", "useWhenEn": [ "Lightly animating a loading or idle line", "Bouncing a title on the beat in music-driven video" ], "promptsEn": { "claude": "Add a text wave to the sentence in . A wave 3 glyphs wide with 16px amplitude sweeps the sentence left to right once every 1.2s; only glyphs in the wave rise and fall on a cosine curve. Lay out repetitions on the timeline instead of infinite repeat.", "codex": "Apply text wave in . Per syllable span, in onUpdate compute d=(i-p*(N+3)+1.5)/1.5 and y=-16*cos(d*PI/2) when |d|<1; period 1.2s. Capture at 0.3s, 0.6s and 0.9s and verify the raised glyphs move left to right with at most 3 raised at once." }, "render": "planned", "files": {}, "no": "112" }, { "slug": "text-window-fill", "family": "type", "ko": "글자 속 영상", "en": "Text Window Fill", "aka": [ "Motion Inside Text", "Text Image Fill Motion", "텍스트 내부 이미지 이동" ], "oneLiner": "글자 외곽은 고정하고 그 안쪽에서만 영상이나 색 곡선이 움직이는 효과", "conveys": "문구의 뜻과 화면의 소재가 한 덩어리로 묶여 읽힌다. 큰 제목 하나로 장면 분위기를 정한다", "purposes": [ "브랜딩", "분위기" ], "media": [ "숏폼", "발표", "웹 UI" ], "level": "중급", "useWhen": [ "인트로 제목이 주제 영상(바다, 도시 등)을 품게 하고 싶을 때", "로고나 한 단어 슬로건에 움직이는 질감을 줄 때" ], "goodExample": "굵은 고딕 'OCEAN' 안에서 파도 영상이 6초 동안 천천히 흐르고 글자 윤곽은 움직이지 않는다", "badExample": "가는 글꼴에 복잡한 영상을 채워 글자 형태가 읽히지 않거나, 글자 자체까지 함께 흔들린다", "avoid": [ "획 굵기가 얇은 글꼴 금지(내부 영상이 보이지 않음)", "글자 수 6자 초과 금지(창이 좁아짐)", "채움 영상의 대비가 낮으면 배경과 섞이므로 금지" ], "params": [ { "name": "채움 이동 거리", "default": "240px", "range": "120~400px", "note": "영상이 글자 안에서 천천히 팬" }, { "name": "주기", "default": "6s", "range": "4~10s", "note": "한 번 흐르는 시간" }, { "name": "글자 크기", "default": "320px", "range": "240~420px", "note": "굵은 고딕 기준 1920x1080" }, { "name": "이징", "default": "sine.inOut", "range": "linear~sine.inOut", "note": "반복 시 끊김이 없어야 함" } ], "ease": "sine.inOut", "snippet": "/* SVG: */\ntl.fromTo('#fill', { x: -120 }, { x: 120, duration: 6, ease: 'sine.inOut' }, 0);\ntl.fromTo('#fill', { scale: 1.15 }, { scale: 1.3, duration: 6, ease: 'none' }, 0);", "pairsWith": [ "texture-fill-motion", "text-cutout-zoom", "mask-reveal" ], "engines": { "hyperframes": "clipPath 안의 image 또는 video를 paused 타임라인에서 x/scale만 움직인다. 비디오는 currentTime을 seek 값에 맞춘다", "reelforge": "타이포 씬 브리프에 문구, 채움 소스 경로, 주기 6s를 파라미터로 싣는다. 채움은 미리 렌더한 루프 클립을 권한다", "scrolline": "채움 이동을 진행률 0..1에 선형 매핑한다. 스크롤 멈춤 때 정지해도 글자는 읽혀야 하므로 밝기 대비를 높게 둔다" }, "prompts": { "claude": "<대상> 제목 '<문구>'를 320px 굵은 고딕으로 크게 놓고, SVG clipPath에 글자를 넣어 그 안에서만 <영상 또는 이미지>가 보이게 해줘. 채움은 6초 동안 x를 -120px에서 120px로, scale을 1.15에서 1.3으로 sine.inOut으로 움직이고, 글자 윤곽은 고정해. paused 타임라인 하나로 seek 되게 만들어.", "codex": "<파일>의 제목을 SVG clipPath text로 바꾸고 내부 image를 6초 tween(x -120→120, scale 1.15→1.3, sine.inOut)으로 구동해. 글자 위치는 절대 변하지 않아야 한다. 0초, 3초, 6초 시점을 캡처해 글자 윤곽 bbox가 동일하고 채움만 이동했는지 확인해." }, "sources": [ { "title": "magicuidesign/magicui", "url": "https://magicui.design/docs/components/video-text", "license": "MIT" }, { "title": "ui.aceternity.com", "url": "https://ui.aceternity.com/components/canvas-text", "license": "unknown" }, { "title": "codrops/TextStylesHoverEffects", "url": "https://github.com/codrops/TextStylesHoverEffects", "license": "unknown" }, { "title": "codrops/TextClipScroll", "url": "https://github.com/codrops/TextClipScroll", "license": "MIT" } ], "runtime": "svg", "variants": [ "Video Text", "Canvas Text", "video-text", "canvas-text", "영상 채움", "사진 팬", "패턴 흐름" ], "oneLinerEn": "The letter outlines stay fixed while video or color flows only inside them.", "conveysEn": "Ties the words to the visual subject so a single large title sets the mood of the scene.", "useWhenEn": [ "Let an intro title carry its own subject footage, such as sea or city.", "Add a moving texture to a logo or a one-word slogan." ], "promptsEn": { "claude": "Set the title '' of in 320px heavy sans and clip inside the letters with an SVG clipPath. Over 6 seconds move the fill x from -120px to 120px and scale from 1.15 to 1.3 with sine.inOut, keeping the letter outline fixed. Use one paused timeline so it can be seeked.", "codex": "In convert the title to an SVG clipPath text and drive the inner image with a 6 second tween (x -120 to 120, scale 1.15 to 1.3, sine.inOut). The glyph positions must never change. Capture at 0s, 3s and 6s and verify the glyph bounding box is identical while only the fill has moved." }, "render": "planned", "files": {}, "no": "113" }, { "slug": "tracking-reveal", "family": "type", "ko": "자간 공개", "en": "Tracking Reveal", "aka": [ "Tracking reveal", "자간 변화 리빌", "Tracking Converge", "자간 수렴", "tracking-kerning-tween", "Tracking Animation", "자간 애니메이션", "Text Tracking Expansion" ], "oneLiner": "넓게 벌어져 있던 글자 사이 간격이 줄어들며 제목이 한 덩어리로 정돈되는 효과", "conveys": "고급스러운 응집. 흩어진 것이 정렬되며 신뢰와 차분함을 준다", "purposes": [ "분위기", "브랜딩" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "럭셔리·브랜드 필름의 제목 등장", "섹션 이름이나 부제를 조용히 정착시킬 때" ], "goodExample": "\"COLLECTION\"이 letter-spacing 0.2em에서 0으로 0.8초 동안 모이며 opacity 0에서 1로 올라온다", "badExample": "간격을 0.6em 이상 벌려 글자가 서로 무관한 조각처럼 보이고 줄이 화면 밖으로 넘친다", "avoid": [ "letter-spacing 자체를 보간하면 리플로가 생겨 줄이 흔들린다. 글자 위치를 고정하고 transform x로 보간한다", "한국어는 자간이 벌어지면 읽기가 끊긴다. 벌림은 0.2em 이하로 한다" ], "params": [ { "name": "지속", "default": "0.8s", "range": "0.6~1.2s", "note": "천천히 모임" }, { "name": "시작 자간", "default": "0.2em", "range": "0.1~0.4em", "note": "한국어는 0.2em 이하" }, { "name": "opacity", "default": "0에서 1", "range": "0~1", "note": "앞 60%에서 올라옴" }, { "name": "이징", "default": "power2.out", "range": "power2~power4", "note": "끝이 부드럽게 정지" } ], "ease": "power2.out", "snippet": "// 최종 위치를 측정한 뒤 x 오프셋으로 벌림을 만든다\nchars.forEach((c, i) => {\n const off = (i - (N - 1) / 2) * 0.2 * fontSize;\n tl.fromTo(node(i), {x: off, opacity:0}, {x:0, opacity:1, duration:0.8, ease:'power2.out'}, 0.2);\n});", "pairsWith": [ "blur-reveal", "word-rise-fade", "clip-reveal" ], "engines": { "hyperframes": "글자 span의 x를 중앙 기준 오프셋으로 시작해 0으로 모은다. letter-spacing을 건드리지 않아 리플로가 없다", "reelforge": "브리프에 문구, 시작 자간 0.2em, 0.8초, 글자 크기를 싣는다. 크기가 바뀌면 오프셋을 다시 계산하게 한다", "scrolline": "진행률 0~0.5에 매핑한다. 홀드 구간에서 자간이 다시 벌어지지 않도록 종료 상태를 고정한다" }, "prompts": { "claude": "<대상> 제목에 tracking reveal을 넣어줘. 글자를 중앙 기준 0.2em 간격 오프셋에서 0으로 0.8초 동안 power2.out으로 모으고 opacity 0에서 1로 올려. letter-spacing 대신 글자별 transform x를 써서 리플로가 없게, 완료 뒤 1초 정지.", "codex": "<파일>의 제목에 tracking reveal을 적용해. 글자 i의 x 시작값=(i-(N-1)/2)*0.2*fontSize, x 0으로 0.8초 power2.out, opacity 0에서 1. 0.3초·0.7초·1.5초를 캡처해 글자 간격이 줄고 마지막 프레임에서 정상 자간인지, 줄 좌우 폭이 최종에서 화면 안인지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/tracking-in/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-creative/references/motion-principles.md", "license": "unknown" }, { "title": "animista.net", "url": "https://animista.net/play", "license": "BSD-2-Clause (FreeBSD)" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/grammar/05-text-animator.md#tracking-kerning-tween", "license": "Apache-2.0" }, { "title": "gongnyang/reelforge", "url": "gongnyang/reelforge:skills/reelforge/references/gallery/gallery-index.json", "license": "Apache-2.0" } ], "runtime": "gsap", "variants": [ "자간 수렴", "자간 확장", "tracking-in", "tracking-out", "expand", "contract", "앞", "뒤" ], "oneLinerEn": "Widely spaced letters tighten together until the title settles into one compact unit.", "conveysEn": "Refined cohesion: scattered elements aligning to signal trust and calm.", "useWhenEn": [ "Title reveals in luxury or brand films", "Settling a section name or subtitle quietly" ], "promptsEn": { "claude": "Add a tracking reveal to the title in . Pull the letters from 0.2em offsets around the center to 0 over 0.8s power2.out while opacity goes 0 to 1. Use per-glyph transform x instead of letter-spacing to avoid reflow; hold 1s at the end.", "codex": "Apply tracking reveal to the title in . Glyph i starts at x=(i-(N-1)/2)*0.2*fontSize, tweens to 0 over 0.8s power2.out, opacity 0 to 1. Capture at 0.3s, 0.7s and 1.5s and verify spacing tightens, ends at normal tracking, and the line stays within the frame." }, "render": "planned", "files": {}, "no": "114" }, { "slug": "variable-font-axis-morph", "family": "type", "ko": "가변 글꼴 축 변형", "en": "Variable Font Axis Morph", "aka": [ "Variable font flex", "가변 글꼴 변형", "Variable Font Width Breathing", "가변 폰트 폭 호흡", "Variable Font Slant Motion", "가변 폰트 기울기 변화", "Variable Font Custom Axis Morph", "가변 폰트 사용자 축 변형" ], "oneLiner": "한 단어의 굵기와 너비가 연속으로 변하며 강도와 성격을 바꾸는 가변 폰트 변형", "conveys": "같은 단어가 가벼운 톤에서 단단한 톤으로 바뀌는 인상. 무게가 실린다", "purposes": [ "강조", "분위기" ], "media": [ "숏폼", "발표", "웹 UI" ], "level": "중급", "useWhen": [ "핵심 단어를 가늘게 시작해 굵게 키워 강조할 때", "호버·선택 상태에서 텍스트 성격을 바꿀 때" ], "goodExample": "\"집중\"이 0.7초 동안 wght 300에서 800, wdth 80에서 100으로 변하며 글자마다 0.06초 늦게 시작한다", "badExample": "축 변화가 프레임마다 다른 값을 내 글자가 떨리거나 줄 바꿈이 바뀌어 레이아웃이 점프한다", "avoid": [ "줄 바꿈이 바뀌지 않도록 white-space nowrap과 고정 박스 폭을 준다", "wght와 wdth를 동시에 바꿀 때는 wdth 변화폭을 20% 이하로 둔다" ], "params": [ { "name": "지속", "default": "0.7s", "range": "0.5~1.0s", "note": "power2.inOut" }, { "name": "wght", "default": "300에서 800", "range": "200~900", "note": "폰트 지원 범위" }, { "name": "wdth", "default": "80에서 100", "range": "75~100", "note": "너비 축" }, { "name": "글자 시작 차", "default": "0.06s", "range": "0~0.1s", "note": "0이면 단어 전체 동시" } ], "ease": "power2.inOut", "snippet": "chars.forEach((_, i) => {\n const o = {w:300, d:80};\n tl.to(o, {w:800, d:100, duration:0.7, ease:'power2.inOut', onUpdate(){\n node(i).style.fontVariationSettings = `'wght' ${o.w}, 'wdth' ${o.d}`;\n }}, 0.2 + i * 0.06);\n});", "pairsWith": [ "variable-font-weight-wave", "font-shuffle", "word-emphasis" ], "engines": { "hyperframes": "GSAP가 객체 속성을 보간하고 onUpdate로 CSS 변수에 쓴다. 단어 컨테이너 폭은 최종(가장 넓은) 상태 기준으로 고정한다", "reelforge": "브리프에 폰트와 축 범위, 시작·끝 값, 0.7초, 시작 차 0.06초를 싣는다", "scrolline": "진행률 0~1을 축 값에 직접 선형 매핑하고 이징은 진행률 쪽에서 준다. 단어가 화면 중앙에 오는 구간에서만 변화하게 구간을 잘라 쓴다" }, "prompts": { "claude": "<대상> 단어에 가변 폰트 축 변형을 넣어줘. 0.7초 동안 wght 300에서 800, wdth 80에서 100으로 power2.inOut 변형하고 글자 시작 차 0.06초. 컨테이너 폭은 최종 상태 기준으로 고정하고 줄 바꿈을 막아 레이아웃이 움직이지 않게 해.", "codex": "<파일>에 variable font axis morph를 적용해. 객체 {w:300,d:80}를 800/100으로 0.7초 power2.inOut 보간하고 onUpdate에서 font-variation-settings에 기록, 글자 stagger 0.06. 0.3초·0.6초·1.3초를 캡처해 wght 증가와 컨테이너 좌표 불변을 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/variable-axis-type/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/variable-font-flex/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/caption-weight-shift/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/techniques.md", "license": "unknown" }, { "title": "yanone/fontanimation", "url": "https://github.com/yanone/fontanimation", "license": "Apache-2.0" } ], "runtime": "css", "variants": [ "폭 호흡", "기울기 변화", "포인터 거리 반응", "압축 제목", "넓은 제목", "폭과 굵기 동시 변화", "전체 기울기", "글자별 기울기 파동" ], "oneLinerEn": "A word's weight and width change continuously, shifting its intensity and character.", "conveysEn": "The same word moves from a light voice to a solid one, gaining weight.", "useWhenEn": [ "When a key word should grow from thin to bold for emphasis", "When text personality should change on hover or selection" ], "promptsEn": { "claude": "Morph the word in along variable-font axes: over 0.7s go wght 300 to 800 and wdth 80 to 100 with power2.inOut, glyph starts 0.06s apart. Fix the container width to the final state and forbid wrapping so layout never moves.", "codex": "Apply variable font axis morph in . Tween {w:300,d:80} to 800/100 over 0.7s power2.inOut, write to font-variation-settings in onUpdate, glyph stagger 0.06. Capture at 0.3s, 0.6s and 1.3s and verify wght rises while container coordinates stay fixed." }, "render": "planned", "files": {}, "no": "115" }, { "slug": "variable-font-weight-wave", "family": "type", "ko": "가변 글꼴 두께 파동", "en": "Variable Font Weight Wave", "aka": [ "Weight wave", "글꼴 두께 파동", "가변 폰트 굵기 물결" ], "oneLiner": "굵기와 기울기의 봉우리가 글자를 차례로 지나가는 가변 폰트 파동", "conveys": "글자 자체에 살아 있는 물성과 리듬이 있다. 로고 없이도 브랜드 톤을 준다", "purposes": [ "분위기", "주목 끌기", "브랜딩" ], "media": [ "숏폼", "웹 UI", "설명 영상" ], "level": "고급", "useWhen": [ "가변 폰트를 쓰는 브랜드 헤드라인에 생동감을 줄 때", "로딩·대기 화면에서 텍스트만으로 움직임을 줄 때" ], "goodExample": "\"Flow Motion\" 위를 폭 4글자의 파동이 1.5초에 한 번 지나가며 걸린 글자만 wght 300에서 900으로 부풀었다 돌아온다", "badExample": "글자 폭이 변해 전체 줄이 흔들리도록 wdth까지 함께 건다", "avoid": [ "축 값을 바꿔도 줄 전체 폭이 변하지 않게 글자 슬롯 폭을 고정하거나 wdth 축은 잠근다", "한국어 웹폰트는 가변 축 지원 여부를 먼저 확인한다. 미지원이면 라틴 문구에만 쓴다" ], "params": [ { "name": "주기", "default": "1.5s", "range": "1.0~2.5s", "note": "파동이 한 번 지나가는 시간" }, { "name": "wght 범위", "default": "300~900", "range": "200~900", "note": "폰트 지원 범위 안" }, { "name": "파장", "default": "4글자", "range": "3~6글자", "note": "봉우리 폭" }, { "name": "slnt", "default": "0~-10", "range": "0~-12", "note": "기울기" } ], "ease": "sine.inOut", "snippet": "const N = chars.length, L = 4;\nconst o = {p:0};\ntl.to(o, {p:1, duration:1.5, ease:'none', repeat:0, onUpdate(){\n chars.forEach((_, i) => {\n const d = Math.abs(i - o.p * (N + L) + L / 2);\n const k = Math.max(0, 1 - d / (L / 2));\n node(i).style.fontVariationSettings = `'wght' ${300 + 600 * k}, 'slnt' ${-10 * k}`;\n });\n}}, 0);", "pairsWith": [ "variable-font-axis-morph", "text-wave", "font-shuffle" ], "engines": { "hyperframes": "onUpdate에서 t의 함수로 축 값을 계산한다. 렌더 전에 document.fonts.load로 가변 폰트를 확실히 적재한다", "reelforge": "브리프에 폰트명과 지원 축 범위, 주기 1.5초, 파장 4글자를 싣는다. 폰트 파일 경로는 프로젝트 내부로 고정한다", "scrolline": "진행률 p를 파동 위치로 직접 쓴다. 스크롤에 따라 파동이 문장 위를 오간다. 축 값은 부드러운 함수라 스프링 없이도 자연스럽다" }, "prompts": { "claude": "<대상> 헤드라인에 가변 폰트 굵기 파동을 넣어줘. 파동은 폭 4글자, 1.5초에 문장을 한 번 지나가며 걸린 글자의 wght를 300에서 900, slnt를 0에서 -10으로 올렸다 되돌려. 줄 전체 폭이 변하지 않게 글자 슬롯 폭을 고정하고, 축 값은 t의 함수로만 계산해.", "codex": "<파일>에 variable font weight wave를 적용해. fonts.load 이후 onUpdate에서 글자 i의 k=max(0,1-|i-p*(N+4)+2|/2)를 계산해 wght 300+600k, slnt -10k를 font-variation-settings에 쓴다. 주기 1.5초. 0.3초·0.75초·1.2초를 캡처해 굵은 봉우리가 왼쪽에서 오른쪽으로 이동하고 줄 폭 변화가 2px 이하인지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/weight-wave/registry-item.json", "license": "OFL-1.1" }, { "title": "amazingcreationsltd/variable-font-animator", "url": "https://github.com/amazingcreationsltd/variable-font-animator", "license": "unknown" }, { "title": "yanone/fontanimation", "url": "https://github.com/yanone/fontanimation", "license": "Apache-2.0" }, { "title": "MDN", "url": "https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/font-variation-settings", "license": "unknown" } ], "runtime": "css", "variants": [ "글자별 파동", "단어별 굵기 강조", "전체 호흡" ], "oneLinerEn": "A wave of weight and slant passes through the letters of a variable font.", "conveysEn": "The type itself feels alive and rhythmic, carrying brand tone without a logo.", "useWhenEn": [ "When adding life to a variable-font brand headline", "When a loading or idle screen needs motion from text alone" ], "promptsEn": { "claude": "Add a variable-font weight wave to the headline in . A wave 4 glyphs wide sweeps the line once every 1.5s, raising wght from 300 to 900 and slnt from 0 to -10 on the glyphs it touches, then relaxing. Fix glyph slot widths so the line width never changes, and compute axes purely from t.", "codex": "Apply variable font weight wave in . After fonts.load, in onUpdate compute k=max(0,1-|i-p*(N+4)+2|/2) for glyph i and write wght 300+600k, slnt -10k into font-variation-settings; period 1.5s. Capture at 0.3s, 0.75s and 1.2s, verify the heavy crest moves left to right and line width varies by 2px or less." }, "render": "planned", "files": {}, "no": "116" }, { "slug": "word-relay", "family": "type", "ko": "단어 릴레이", "en": "Word Relay", "aka": [ "Word relay" ], "oneLiner": "큰 단어 하나가 오른쪽에서 들어와 중앙에서 멈췄다가 왼쪽으로 나가고 다음 단어가 이어받는 릴레이", "conveys": "핵심 메시지가 한 단어씩 바통처럼 전달되는 리듬. 마지막 단어는 남아 결론이 된다", "purposes": [ "순서·흐름", "주목 끌기", "강조" ], "media": [ "스크롤덱", "숏폼", "발표" ], "level": "중급", "useWhen": [ "2~5단어로 된 슬로건을 한 단어씩 크게 보여 줄 때", "스크롤 한 구간에서 핵심 키워드를 차례로 전달할 때" ], "goodExample": "\"기획 / 제작 / 배포\" 세 단어가 각각 0.4초 진입, 0.6초 정지, 0.3초 퇴장으로 이어지고 \"배포\"는 남는다", "badExample": "단어가 퇴장하는 동안 다음 단어가 이미 중앙에 있어 두 단어가 겹쳐 읽힌다", "avoid": [ "단어당 정지 0.5초 이하로 줄이지 않는다. 읽히기 전에 나가 버린다", "단어는 2~5개. 6개 이상이면 릴레이가 지루해진다" ], "params": [ { "name": "진입", "default": "0.4s", "range": "0.3~0.5s", "note": "x +900px에서 0" }, { "name": "정지", "default": "0.6s", "range": "0.5~1.0s", "note": "중앙 홀드" }, { "name": "퇴장", "default": "0.3s", "range": "0.25~0.4s", "note": "x -900px" }, { "name": "마지막 단어", "default": "유지", "range": "유지 또는 퇴장", "note": "결론 단어는 남긴다" } ], "ease": "power3.out (진입) / power2.in (퇴장)", "snippet": "const ws = ['기획','제작','배포'];\nws.forEach((w, i) => {\n const t = i * 1.3;\n tl.fromTo(el(i), {x:900, opacity:0}, {x:0, opacity:1, duration:0.4, ease:'power3.out'}, t);\n if (i < ws.length - 1) tl.to(el(i), {x:-900, opacity:0, duration:0.3, ease:'power2.in'}, t + 1.0);\n});", "pairsWith": [ "phrase-push-build", "kinetic-beats", "pinned-scrollytelling" ], "engines": { "hyperframes": "단어마다 absolute로 겹쳐 두고 x만 보간한다. 진입/정지/퇴장 세 구간을 고정 위치에 배치해 seek 정확도를 확보한다", "reelforge": "브리프에 단어 배열, 진입 0.4초, 정지 0.6초, 퇴장 0.3초, 마지막 단어 유지 여부를 싣는다", "scrolline": "정본 형태다. 진행률 0~0.7을 단어 수로 나누고 구간마다 진입·정지·퇴장을 배분한다. 핀 길이는 약 220vh, scrub에서는 ease-out만 쓴다" }, "prompts": { "claude": "<대상>에 word relay를 만들어줘. 큰 단어 <단어들>이 오른쪽 900px에서 0.4초 power3.out으로 들어와 중앙에서 0.6초 멈추고 0.3초 power2.in으로 왼쪽 900px로 나가며 다음 단어가 이어받아. 마지막 단어는 남겨. 한 타임라인 하나로 seek 가능하게.", "codex": "<파일>에 word relay를 적용해. 단어 span을 absolute로 겹치고 단어 i의 시작 t=i*1.3, 진입 0.4, 퇴장은 t+1.0에 0.3초. 마지막은 퇴장 없음. 각 단어의 중앙 정지 구간(0.6초, 1.9초, 3.2초)을 캡처해 한 시점에 한 단어만 보이는지 확인해." }, "sources": [ { "title": "gongnyang/awesome-html-scrolline-deck", "url": "gongnyang/awesome-html-scrolline-deck:references/techniques.md#word-relay", "license": "MIT" }, { "title": "gongnyang/awesome-html-scrolline-deck", "url": "gongnyang/awesome-html-scrolline-deck:templates/scenes/word-relay/scene.css", "license": "MIT" }, { "title": "gongnyang/awesome-html-scrolline-deck", "url": "gongnyang/awesome-html-scrolline-deck:templates/scenes/word-relay/scene.html", "license": "MIT" }, { "title": "gongnyang/awesome-html-scrolline-deck", "url": "gongnyang/awesome-html-scrolline-deck:templates/scenes/word-relay/scene.js", "license": "MIT" }, { "title": "gongnyang/awesome-html-scrolline-deck", "url": "gongnyang/awesome-html-scrolline-deck:templates/scenes/word-relay/template.json", "license": "MIT" } ], "runtime": "gsap", "variants": [ "슬로건 릴레이", "고정 마지막 단어", "word-relay", "split-anchor-word-slot" ], "oneLinerEn": "One big word enters from the right, pauses at center, exits left, and the next word takes over.", "conveysEn": "The message is passed along word by word like a baton, and the last word stays as the conclusion.", "useWhenEn": [ "When showing a 2 to 5 word slogan one huge word at a time", "When delivering keywords in sequence within one scroll section" ], "promptsEn": { "claude": "Build a word relay in . Big words enter from 900px right in 0.4s power3.out, hold at center for 0.6s, and exit 900px left in 0.3s power2.in as the next takes over. Keep the last word. Use a single seekable timeline.", "codex": "Apply word relay in . Stack word spans absolutely; word i starts at t=i*1.3, enters in 0.4s, exits at t+1.0 over 0.3s; the last word does not exit. Capture inside each center hold (0.6s, 1.9s, 3.2s) and verify only one word is visible per frame." }, "render": "planned", "files": {}, "no": "117" }, { "slug": "word-rise-fade", "family": "type", "ko": "단어 떠오르기", "en": "Word Rise Fade", "aka": [ "Word rise", "Word Fade", "단어 페이드 상승", "word-fade-up", "Word Staggered Fade", "단어 순차 페이드", "Wordwise text reveal", "단어 단위 공개" ], "oneLiner": "단어가 흐림을 벗으며 조금씩 위로 올라와 제자리에 자리 잡는 순차 공개", "conveys": "차분하고 정돈된 문장 구성. 읽는 순서가 단어 순서로 그대로 드러난다", "purposes": [ "설명", "분위기" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "한두 문장짜리 설명 자막이나 부제를 부드럽게 공개할 때", "차분한 브랜드 톤의 영상 도입 문구" ], "goodExample": "\"작게 시작해서 크게 배운다\" 다섯 어절이 0.08초 간격으로 12px 올라오며 선명해진다", "badExample": "글자마다 y 40px과 blur 20px을 걸어 등장에 1.5초가 걸리고 마지막 단어를 읽기 전에 화면이 바뀐다", "avoid": [ "전체 공개가 1.2초를 넘지 않게 한다. 어절이 많으면 간격을 줄인다", "한국어는 어절 단위로 나눈다. 조사만 떼어 올리면 읽기 흐름이 끊긴다" ], "params": [ { "name": "단어 간격", "default": "0.08s", "range": "0.05~0.12s", "note": "어절 수가 많으면 0.05s" }, { "name": "단어 지속", "default": "0.35s", "range": "0.3~0.9s", "note": "soft-blur 계열은 0.9s" }, { "name": "올라오는 거리", "default": "12px", "range": "8~14px", "note": "1920x1080 기준" }, { "name": "시작 블러", "default": "6px", "range": "0~10px", "note": "0이면 순수 상승 페이드" } ], "ease": "power2.out", "snippet": "const words = text.split(' ').map(w => `${w}`).join(' ');\nel.innerHTML = words;\ntl.from('.w', {y:12, opacity:0, filter:'blur(6px)', duration:0.35, ease:'power2.out', stagger:0.08}, 0.2);", "pairsWith": [ "char-stagger", "blur-reveal", "stagger" ], "engines": { "hyperframes": "단어 span을 만들어 stagger 하나로 처리한다. filter blur는 프레임 캡처마다 비용이 크므로 blur 6px 이하로 둔다", "reelforge": "씬 워커 브리프에 문장, 어절 수, 간격 0.08초, 이동 12px를 싣는다. 자막 길이에 맞춰 간격만 자동 조정하게 한다", "scrolline": "진행률 0.1~0.5에 stagger 전체를 매핑한다. scrub 중에는 스프링 대신 power2.out으로 두어 뒤로 돌려도 자연스럽다" }, "prompts": { "claude": "<대상> 문장을 어절 단위 span으로 나눠 word rise fade로 공개해줘. 각 어절은 y 12px 아래에서 올라오며 opacity 0에서 1, blur 6px에서 0, 0.35초 power2.out이고 어절 간격은 0.08초야. 전체가 1.2초 안에 끝나게 하고 등장 뒤 1초 정지해.", "codex": "<파일>의 문장 컨테이너에 word rise fade를 적용해. 공백 기준으로 span 분리, stagger 0.08, duration 0.35, y 12, blur 6px, power2.out. 0.3초·0.8초·1.6초 시점을 캡처해 단어가 왼쪽부터 순서대로 선명해지고 마지막에 전부 blur 0인지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/per-word-rise/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/per-word-crossfade/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/soft-blur-in/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/blur-in/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:embedded-captions/references/motion-vocabulary.md", "license": "unknown" } ], "runtime": "gsap", "variants": [ "단어 단위 순차 공개", "animate-text/soft-blur-in", "animate-text/per-word-crossfade", "단어", "글자", "페이드", "블러", "에치" ], "oneLinerEn": "Words rise slightly while shedding blur, settling into place one after another.", "conveysEn": "A calm, orderly sentence build where reading order is visible as word order.", "useWhenEn": [ "When revealing a one or two line explainer caption or subtitle softly", "For the calm brand-tone opening line of a video" ], "promptsEn": { "claude": "Reveal the sentence in word by word. Each word rises 12px, fades 0 to 1 and de-blurs from 6px to 0 over 0.35s with power2.out, staggered 0.08s. Finish within 1.2s and hold for 1s.", "codex": "Apply word rise fade to the sentence container in . Split on spaces into spans; stagger 0.08, duration 0.35, y 12, blur 6px, power2.out. Capture at 0.3s, 0.8s and 1.6s and confirm words sharpen left to right and all end at blur 0." }, "render": "planned", "files": {}, "no": "118" }, { "slug": "word-slot-cycle", "family": "type", "ko": "고정 슬롯 단어 순환", "en": "Word Slot Cycle", "aka": [ "Fixed slot word cycle", "Line swap", "줄 교체", "Rotating Word Loop", "단어 순환 교체", "Headline Slot Ticker", "헤드라인 슬롯 티커", "Shared Axis Text Swap" ], "oneLiner": "문장의 고정 부분은 그대로 두고 한 단어 자리만 다른 단어로 순환해 바뀌는 효과", "conveys": "하나의 문장 틀에 여러 선택지가 들어간다. 다양성과 범용성이 전달된다", "purposes": [ "설명", "브랜딩", "주목 끌기" ], "media": [ "숏폼", "설명 영상", "웹 UI" ], "level": "중급", "useWhen": [ "\"AI로 ___를 만든다\"처럼 한 틀에 여러 용도를 보여 줄 때", "제품 헤드라인에서 대상 고객이나 기능을 돌아가며 보여 줄 때" ], "goodExample": "\"당신의 ___ 팀을 위한\" 문장에서 슬롯이 0.5초 멈추고 0.25초에 위로 밀려 다음 단어로 바뀐다. 슬롯 폭은 가장 긴 단어에 맞춰 고정이다", "badExample": "단어가 바뀔 때마다 문장 전체가 좌우로 밀려 눈이 따라다녀야 한다", "avoid": [ "슬롯 폭을 가장 넓은 단어 기준으로 고정하고 나머지 문장은 움직이지 않게 한다", "한 단어 노출이 0.5초 미만이면 읽지 못한다. 단어는 4~6개 이내로 한다" ], "params": [ { "name": "단어 정지", "default": "0.6s", "range": "0.5~1.0s", "note": "읽을 시간" }, { "name": "교체 시간", "default": "0.25s", "range": "0.2~0.35s", "note": "나가는 단어와 들어오는 단어가 겹침" }, { "name": "이동 거리", "default": "0.6em", "range": "0.4~0.8em", "note": "위로 나가고 아래에서 들어옴" }, { "name": "블러 나감", "default": "4px", "range": "0~6px", "note": "나가는 단어만" } ], "ease": "power2.inOut", "snippet": "const words = ['디자인','영업','교육','연구'];\nwords.forEach((w, i) => {\n const t = 0.4 + i * 0.85;\n if (i) tl.to(cur(i-1), {yPercent:-60, opacity:0, duration:0.25, ease:'power2.in'}, t - 0.25)\n .fromTo(cur(i), {yPercent:60, opacity:0}, {yPercent:0, opacity:1, duration:0.25, ease:'power2.out'}, t - 0.25);\n});\n// 슬롯 span은 position:absolute, 부모 width는 가장 긴 단어 폭으로 고정", "pairsWith": [ "text-scramble", "kinetic-type-sweep", "word-emphasis" ], "engines": { "hyperframes": "슬롯 안 단어들을 겹쳐 놓고 opacity와 yPercent만 보간한다. 폭은 document.fonts.ready 이후 offsetWidth로 측정해 고정한다", "reelforge": "브리프에 단어 배열, 정지 0.6초, 교체 0.25초, 고정 문구를 싣는다. 한국어는 조사가 붙으면 폭이 변하므로 조사를 슬롯 밖에 둔다", "scrolline": "단어 수 N으로 진행률을 N등분해 구간마다 교체한다. 구간 사이에는 홀드를 둬서 스크롤을 멈춘 상태에서도 단어가 읽힌다" }, "prompts": { "claude": "<대상>에 고정 슬롯 단어 순환을 만들어줘. 문장 틀은 고정하고 빈 자리에 단어 4개(<단어들>)를 0.6초 정지, 0.25초 교체(위로 나가며 opacity 0, 아래에서 들어옴)로 돌려. 슬롯 폭은 가장 긴 단어에 맞춰 고정해 문장이 밀리지 않게 하고, 조사는 슬롯 밖에 둬.", "codex": "<파일>의 헤드라인에 word slot cycle을 적용해. 슬롯 span은 absolute로 겹치고 부모 폭은 가장 긴 단어 offsetWidth로 고정. 단어당 주기 0.85초(정지 0.6, 교체 0.25), yPercent ±60. 0.5초·1.2초 시점을 캡처해 문장 나머지 부분의 x 좌표가 두 프레임에서 동일한지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/kinetic-type-swap/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/blueprints/fixed-anchor-cycle.md", "license": "unknown" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/line-swap/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/shared-axis-y/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/blur-out-up/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "variants": [ "줄 교체", "헤드라인 슬롯", "공유 축 교체", "animate-text/blur-out-up", "Word Rotate", "Flip Words", "Text Loop", "RotatingText" ], "oneLinerEn": "The fixed part of a sentence stays put while one word slot cycles through alternatives.", "conveysEn": "One sentence frame carries many options, signalling range and variety.", "useWhenEn": [ "When showing many uses in one frame, such as \"Build ___ with AI\"", "When a product headline rotates through audiences or features" ], "promptsEn": { "claude": "Build a fixed-slot word cycle in . Keep the sentence frame static and cycle 4 words () in the blank: hold 0.6s, swap over 0.25s (outgoing rises and fades, incoming enters from below). Fix the slot width to the widest word so nothing reflows; keep Korean postpositions outside the slot.", "codex": "Apply word slot cycle to the headline in . Absolutely stack slot spans; fix the parent width to the widest word offsetWidth. Period 0.85s per word (0.6 hold, 0.25 swap), yPercent +/-60. Capture at 0.5s and 1.2s and verify the x position of the rest of the sentence is identical in both frames." }, "render": "planned", "files": {}, "no": "119" }, { "slug": "crossfade", "family": "transitions", "ko": "크로스페이드", "en": "Crossfade", "aka": [ "Dissolve", "디졸브", "교차 페이드", "Cross dissolve", "Cross Dissolve", "fade()", "fadeTransition", "Non-Additive Dissolve" ], "oneLiner": "장면 A의 불투명도가 낮아지는 동안 장면 B의 불투명도가 함께 높아지는 전환", "conveys": "두 장면이 잠시 공존하며 문장과 확률 정보가 이어진다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "문장에서 관련 데이터 장면으로 부드럽게 넘어갈 때", "시간이나 분위기가 이어지는 두 장면을 연결할 때" ], "goodExample": "문장이 0.8초 동안 사라지며 후보 확률 막대가 같은 시간에 나타난다", "badExample": "A를 먼저 완전히 지운 뒤 B를 켜서 중간에 빈 화면을 만든다", "avoid": [ "겹치는 두 장면의 본문을 오래 읽게 하지 않는다", "각 장면에 주홍 초점을 따로 두지 않는다" ], "params": [ { "name": "전환 시작", "default": "0.35s", "range": "0.3~0.8s", "note": "시작 장면을 먼저 보여준다" }, { "name": "겹침 지속", "default": "0.8s", "range": "0.5~1.2s", "note": "중간 시점에 두 장면을 확인한다" }, { "name": "불투명도", "default": "A 1→0, B 0→1", "range": "0~1", "note": "같은 시각에 반대 방향으로 변화" }, { "name": "완성 홀드", "default": "1.65s", "range": "0.5~2s", "note": "주석 등장 완료 후 정지" } ], "ease": "none", "snippet": "tl.to('#A', {opacity:0, duration:0.8, ease:'none'}, 0.35);\ntl.to('#B', {opacity:1, duration:0.8, ease:'none'}, 0.35);\ntl.to('.lead', {opacity:1, duration:0.2}, 1.3);", "pairsWith": [ "wipe", "match-cut", "timing-spacing" ], "engines": { "hyperframes": "장면 둘을 같은 좌표에 배치하고 하나의 paused GSAP 타임라인으로 전환한다. 전환 뒤 완성 장면을 홀드한다", "reelforge": "전환 비트의 시작 시각과 지속 시간을 고정하고 장면 레이어 두 개를 함께 렌더한다", "scrolline": "전환 구간의 진행률을 하나의 타임라인에 연결하고 양 끝에 읽기 위한 정지 구간을 둔다" }, "prompts": { "claude": "<대상>의 장면 A와 B를 같은 좌표에 배치해 크로스페이드를 만들어줘. 0.35초부터 0.8초 동안 A opacity를 1에서 0, B를 0에서 1로 동시에 바꾸고 ease는 none으로 해. 1.5초부터 3초까지 완성 장면을 정지하고 주홍 초점은 한 곳만 둬.", "codex": "<파일>의 장면 전환에 GSAP opacity 크로스페이드를 적용해. 시작 0.35초, 지속 0.8초, ease none으로 A 1→0과 B 0→1을 같은 타임라인에 넣어. 0.23초, 0.75초, 2.9초를 캡처해 시작 A, 두 장면의 반투명 겹침, 완성 B 홀드를 확인해." }, "sources": [ { "title": "Dissolve (filmmaking)", "url": "https://en.wikipedia.org/wiki/Dissolve_(filmmaking)", "license": "개념 인용" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/transitions-dissolve/registry-item.json", "license": "Apache-2.0" }, { "title": "motiondivision/motion", "url": "https://motion.dev/docs/react-animate-presence", "license": "MIT" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Plugins/Flip/", "license": "GSAP Standard License" }, { "title": "pmndrs/react-spring", "url": "https://www.react-spring.dev/docs/components/use-transition", "license": "MIT" }, { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-curtains-fade", "license": "unknown" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/sticky-mock-swap/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/skeleton-reveal/registry-item.json", "license": "Apache-2.0" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/crossfade/index.html", "mp4": "effects/crossfade/clip.mp4", "gif": "effects/crossfade/preview.gif", "poster": "effects/crossfade/poster.jpg" }, "no": "120", "oneLinerEn": "Scene A fades out as scene B fades in at the same time.", "conveysEn": "Briefly lets both scenes coexist, connecting the sentence to probability information.", "useWhenEn": [ "Move smoothly from a sentence to a related data scene.", "Connect two scenes that share a continuous sense of time or atmosphere." ], "promptsEn": { "claude": "Create a crossfade by placing scenes A and B of at the same coordinates. Starting at 0.35 seconds over 0.8 seconds, simultaneously animate A opacity from 1 to 0 and B opacity from 0 to 1 with ease none. Hold the completed scene from 1.5 to 3 seconds and use only one vermilion focal point.", "codex": "Apply a GSAP opacity crossfade to the scene transition in . In the same timeline, animate A from 1 to 0 and B from 0 to 1 with start 0.35 seconds, duration 0.8 seconds, and ease none. Capture at 0.23, 0.75, and 2.9 seconds to check initial scene A, the translucent overlap of both scenes, and the completed scene B hold." }, "variants": [ "이미지 교체", "전체 화면", "공유 요소 페이드", "fade", "fadefast", "fadeslow", "Non additive dissolve", "Linear light film dissolve", "IDE 스킨", "브랜드 스킨" ] }, { "slug": "push-transition", "family": "transitions", "ko": "푸시 전환", "en": "Push", "aka": [ "밀어내기 전환", "Push transition", "밀기 전환", "Push Transition", "slideTransition", "slide()", "Push scene transition", "밀어내는 장면 전환" ], "oneLiner": "새 장면이 들어오는 만큼 기존 장면을 같은 방향으로 밀어내는 전환", "conveys": "두 장면이 연결된 공간처럼 이어진다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "순서가 있는 단계 사이를 이동할 때", "옆에 놓인 장면으로 넘어가는 공간 관계를 보여줄 때" ], "goodExample": "B가 오른쪽에서 들어오는 동안 A가 같은 속도로 왼쪽으로 나간다", "badExample": "A는 멈춰 있는데 B만 덮어서 푸시가 슬라이드 오버레이처럼 보인다", "avoid": [ "장면마다 다른 이징을 쓰지 않는다", "이동 거리가 달라 틈이 생기지 않도록 한다" ], "params": [ { "name": "전환 시작", "default": "0.35s", "range": "0.3~0.8s", "note": "준비 상태 유지" }, { "name": "전환 지속", "default": "1.2s", "range": "0.7~1.5s", "note": "두 장면 이동 확인" }, { "name": "이동 거리", "default": "−1168px", "range": "장면 폭 1배", "note": "두 장면을 붙인 레일의 이동" }, { "name": "이징", "default": "power2.inOut", "range": "power1~power3.inOut", "note": "같은 부모에서 가속과 감속 공유" } ], "ease": "power2.inOut", "snippet": "const tl = Motion.timeline();\ntl.to('#rail', {x:-1168, duration:1.2, ease:'power2.inOut'}, 0.35);\ntl.to('.lead', {opacity:1, duration:0.2}, 1.7);", "pairsWith": [ "wipe", "pan", "easing-curves" ], "engines": { "hyperframes": "장면 둘을 같은 좌표에 배치하고 하나의 paused GSAP 타임라인으로 전환한다. 전환 뒤 완성 장면을 홀드한다", "reelforge": "전환 비트의 시작 시각과 지속 시간을 고정하고 장면 레이어 두 개를 함께 렌더한다", "scrolline": "전환 구간의 진행률을 하나의 타임라인에 연결하고 양 끝에 읽기 위한 정지 구간을 둔다" }, "prompts": { "claude": "<대상>의 장면 A와 B를 폭 1168px씩 하나의 레일에 나란히 붙여 푸시 전환을 만들어줘. B를 x 1168px에 놓고 부모 레일만 0.35초부터 1.2초 동안 x −1168px로 power2.inOut 이동해. 1.9초부터 3초까지 완성 장면을 정지해.", "codex": "<파일>의 장면 전환을 공통 레일 기반 푸시로 바꿔. 폭 1168px 장면 두 개를 좌우로 붙이고 레일 x를 0.35초부터 1.2초 동안 −1168px로 power2.inOut 이동해. 0.23초, 0.73초, 1.23초, 2.9초를 캡처해 A와 B가 틈 없이 같이 움직이고 마지막에 B만 남는지 확인해." }, "sources": [ { "title": "GSAP CSS transform documentation", "url": "https://gsap.com/docs/v3/GSAP/CorePlugins/CSS/", "license": "개념 인용" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/directional-wipe/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/page-slide/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/transitions-push/registry-item.json", "license": "Apache-2.0" }, { "title": "pixel-point/animate-text", "url": "https://raw.githubusercontent.com/pixel-point/animate-text/master/skills/animate-text/assets/specs/shared-axis-x.json", "license": "unknown" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/Directional.glsl", "license": "MIT" }, { "title": "ibelick/motion-primitives", "url": "https://motion-primitives.com/docs/transition-panel", "license": "MIT" }, { "title": "shadcn-ui/ui", "url": "https://github.com/shadcn-ui/ui", "license": "MIT" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/push-transition/index.html", "mp4": "effects/push-transition/clip.mp4", "gif": "effects/push-transition/preview.gif", "poster": "effects/push-transition/poster.jpg" }, "no": "121", "oneLinerEn": "An incoming scene pushes the existing scene out by the same amount in the same direction.", "conveysEn": "Makes two scenes feel like adjoining spaces.", "useWhenEn": [ "Move between ordered steps.", "Show the spatial relationship when moving to an adjacent scene." ], "promptsEn": { "claude": "Create a push transition for by placing scenes A and B side by side on one rail, each 1168px wide. Place B at x 1168px and animate only the parent rail to x -1168px starting at 0.35 seconds over 1.2 seconds with power2.inOut. Hold the completed scene from 1.9 to 3 seconds.", "codex": "Change the scene transition in to a push using a shared rail. Place two 1168px-wide scenes side by side and animate the rail x to -1168px starting at 0.35 seconds over 1.2 seconds with power2.inOut. Capture at 0.23, 0.73, 1.23, and 2.9 seconds to verify that A and B move together without gaps and only B remains at the end." }, "variants": [ "animate-text/shared-axis-x", "좌우", "상하", "탄성 밀기", "공유 축 X", "좌우 밀기", "상하 밀기", "Directional", "directional-easing", "slideleft" ] }, { "slug": "wipe", "family": "transitions", "ko": "와이프", "en": "Wipe", "aka": [ "방향 전환", "Diagonal split", "대각 분할 전환", "Cover wipe", "가림막 와이프", "Shutter", "셔터 전환", "Diagonal Wipe" ], "oneLiner": "이동하는 경계 뒤로 새 장면을 순서대로 공개하는 전환", "conveys": "같은 방향의 경계 이동이 새 장면의 진입을 보여준다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "왼쪽부터 오른쪽으로 정보의 순서를 보여줄 때", "장면 경계를 뚜렷하게 보이면서 내용 전체를 교체할 때" ], "goodExample": "주홍 세로선이 1.2초 동안 오른쪽으로 지나가며 후보 확률 장면이 드러난다", "badExample": "세로선은 이동하지만 새 장면은 전체가 한꺼번에 나타난다", "avoid": [ "경계선과 clip-path의 진행률을 따로 움직이지 않는다", "전환 중 주홍 대상 여러 개를 추가하지 않는다" ], "params": [ { "name": "전환 시작", "default": "0.35s", "range": "0.3~0.8s", "note": "시작 상태 확인" }, { "name": "전환 지속", "default": "1.2s", "range": "0.7~1.5s", "note": "이동하는 경계가 보이는 시간" }, { "name": "마스크", "default": "inset(0% 100% 0% 0%) → inset(0% 0% 0% 0%)", "range": "오른쪽 inset 100~0%", "note": "왼쪽에서 공개" }, { "name": "경계 이동", "default": "1164px", "range": "무대 폭에서 선 폭 제외", "note": "4px 선을 마지막에도 유지" } ], "ease": "none", "snippet": "tl.to('#B', {clipPath:'inset(0% 0% 0% 0%)', duration:1.2, ease:'none'}, 0.35);\ntl.to('#edge', {x:1164, duration:1.2, ease:'none'}, 0.35);\ntl.to('.lead', {opacity:1, duration:0.2}, 1.7);", "pairsWith": [ "push-transition", "mask-reveal", "crossfade" ], "engines": { "hyperframes": "장면 둘을 같은 좌표에 배치하고 하나의 paused GSAP 타임라인으로 전환한다. 전환 뒤 완성 장면을 홀드한다", "reelforge": "전환 비트의 시작 시각과 지속 시간을 고정하고 장면 레이어 두 개를 함께 렌더한다", "scrolline": "전환 구간의 진행률을 하나의 타임라인에 연결하고 양 끝에 읽기 위한 정지 구간을 둔다" }, "prompts": { "claude": "<대상>의 두 장면을 겹쳐 두고 B의 clip-path를 inset(0% 100% 0% 0%)로 시작해줘. 0.35초부터 1.2초 동안 오른쪽 inset을 0%로 바꾸고 4px 주홍 세로선을 x 0에서 1164px로 같이 움직여줘. 이징은 none, 전체 길이는 3초로 하고 1.9초 이후 완성 상태를 정지해.", "codex": "<파일>에 왼쪽에서 오른쪽으로 진행하는 와이프를 적용해. B clip-path inset 오른쪽 100%→0%와 4px 세로선 x 0→1164를 0.35초에 시작해 1.2초 동안 none으로 움직여. 0.23초, 0.73초, 1.23초, 2.9초 캡처에서 경계 뒤에는 B, 앞에는 A가 보이며 끝에 B 전체가 유지되는지 확인해." }, "sources": [ { "title": "Wipe (transition)", "url": "https://en.wikipedia.org/wiki/Wipe_(transition)", "license": "개념 인용" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/transitions-cover/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/lt-clean-bar/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/lt-bold-block/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/lt-stack-bars/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/lower-third-bild/registry-item.json", "license": "Apache-2.0" }, { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-footer-reveal", "license": "unknown" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Plugins/ScrollTrigger/", "license": "GSAP Standard License" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/wipe/index.html", "mp4": "effects/wipe/clip.mp4", "gif": "effects/wipe/preview.gif", "poster": "effects/wipe/poster.jpg" }, "no": "122", "oneLinerEn": "A moving boundary progressively reveals the new scene behind it.", "conveysEn": "Communicates the entrance of a new scene through a boundary moving in a consistent direction.", "useWhenEn": [ "Present information in order from left to right.", "Replace the entire scene while keeping the transition boundary visible." ], "promptsEn": { "claude": "Overlay the two scenes in and start B with clip-path inset(0% 100% 0% 0%). Starting at 0.35 seconds over 1.2 seconds, animate the right inset to 0% while moving a 4px vermilion vertical line from x 0 to 1164px. Use ease none and a total duration of 3 seconds. Hold the completed state after 1.9 seconds.", "codex": "Apply a left-to-right wipe in . Starting at 0.35 seconds over 1.2 seconds with ease none, animate B's right clip-path inset from 100% to 0% and a 4px vertical line from x 0 to 1164. Capture at 0.23, 0.73, 1.23, and 2.9 seconds to verify B behind the boundary, A ahead of it, and the full scene B holding at the end." }, "variants": [ "2색면", "5색면", "띠 가림", "하단 이름표 가림", "15도", "45도", "단색 블록", "이미지 간 교체", "wipe-transform", "가로" ] }, { "slug": "iris-mask", "family": "transitions", "ko": "마스크 전환", "en": "Shape Mask Transition", "aka": [ "원형 전환", "Iris reveal", "Iris mask", "아이리스 마스크", "Circle iris", "Diamond iris", "Iris Mask", "iris-circle-directional" ], "oneLiner": "한 점에서 원형 마스크가 커지며 다음 장면을 드러내는 전환", "conveys": "같은 위치를 출발점으로 새 정보가 화면 전체로 퍼진다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "질문에서 답으로 장면을 바꿀 때", "선택한 지점을 기준으로 상세 장면을 열 때" ], "goodExample": "주홍 점의 중심에서 원형 마스크가 커지며 질문 장면을 답 장면으로 바꾼다", "badExample": "원이 충분히 커지지 않아 끝까지 모서리에 이전 장면이 남는다", "avoid": [ "마스크 중심과 시작 점의 위치를 맞춘다", "완료 반경을 가장 먼 모서리 거리보다 크게 둔다" ], "params": [ { "name": "시작 반경", "default": "0px", "range": "0~20px", "note": "작은 한 점에서 시작" }, { "name": "완료 반경", "default": "680px", "range": "655~800px", "note": "1168×580 중심 기준으로 모서리까지 덮는다" }, { "name": "중심", "default": "584px 290px", "range": "장면 내부 좌표", "note": "주홍 점 중심과 동일" }, { "name": "전환 시간", "default": "1.35s", "range": "0.8~1.5s", "note": "마스크 확장이 읽히는 시간" } ], "ease": "power2.inOut", "snippet": "tl.to('.b', {clipPath:'circle(680px at 584px 290px)', duration:1.35, ease:'power2.inOut'}, 0.4);\ntl.to('.seed', {scale:0, duration:0.15, ease:'power1.in'}, 0.4);\ntl.to('.lead', {opacity:1, duration:0.25}, 1.95);", "pairsWith": [ "wipe", "spotlight", "progressive-disclosure" ], "engines": { "hyperframes": "장면 안쪽 래퍼를 paused GSAP 타임라인 하나로 움직인다. 3초 길이에서 마지막 0.5초 이상을 홀드한다.", "reelforge": "전환 전후 장면을 동시에 배치하고 이동·마스크·색 변화의 시작과 끝을 같은 시간축에 둔다.", "scrolline": "전환 시간을 스크롤 진행률로 환산한다. 역방향 seek에도 시작 상태가 복원되게 초기값을 타임라인에 둔다." }, "prompts": { "claude": "<대상>을 한 점에서 열리는 원형 마스크 전환으로 만들어줘. 1168×580 장면 중심 584px, 290px에 주홍 점을 두고 장면 B의 clip-path를 circle(0px at 584px 290px)에서 circle(680px at 584px 290px)로 바꿔. 0.4초부터 1.35초간 power2.inOut으로 확장하고 2.2초 이후 정지해.", "codex": "<파일>의 장면 B 래퍼에 clip-path circle을 적용해. 중심은 584px, 290px이고 반경은 0.4초부터 1.35초간 0에서 680px까지 power2.inOut으로 커지게 해. 0.23초, 0.73초, 1.23초, 2.9초 캡처로 시작 점과 마스크 중심이 일치하고 마지막 모서리에 장면 A가 남지 않는지 확인해." }, "sources": [ { "title": "GSAP Timeline 공식 문서", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 개념 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/iris-reveal/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/sdf-iris/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/transitions-radial/registry-item.json", "license": "Apache-2.0" }, { "title": "motion.dev Motion+", "url": "https://motion.dev/docs/react-use-curtains", "license": "unknown" }, { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-curtains-iris", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/iris-mask/index.html", "mp4": "effects/iris-mask/clip.mp4", "gif": "effects/iris-mask/preview.gif", "poster": "effects/iris-mask/poster.jpg" }, "no": "123", "oneLinerEn": "A circular mask expands from a point to reveal the next scene.", "conveysEn": "Shows new information spreading across the screen from a shared origin.", "useWhenEn": [ "Transition from a question to its answer.", "Open a detail scene from a selected point." ], "promptsEn": { "claude": "Create a circular mask transition for that opens from a point. Place a vermilion dot at the center of a 1168×580 scene, at 584px, 290px. Animate scene B's clip-path from circle(0px at 584px 290px) to circle(680px at 584px 290px). Expand starting at 0.4 seconds over 1.35 seconds with power2.inOut, and hold after 2.2 seconds.", "codex": "Apply a circle clip-path to the scene B wrapper in . Use center 584px, 290px and animate the radius from 0 to 680px starting at 0.4 seconds over 1.35 seconds with power2.inOut. Capture at 0.23, 0.73, 1.23, and 2.9 seconds to verify that the starting dot and mask center align and that no scene A remains in the corners at the end." }, "variants": [ "원", "마름모", "SDF 경계", "중앙", "대상 좌표 원점", "코너", "문틀", "다이아몬드", "열기와 닫기", "화면 중앙" ] }, { "slug": "match-cut", "family": "transitions", "ko": "매치컷", "en": "Match Cut", "aka": [ "Graphic Match", "형태 매치", "Match cut", "매치 컷", "match-cut-position-carry", "match-cut-graphic-analogy", "object-scale-continuity", "Matched dissolve" ], "oneLiner": "컷 앞뒤에서 같은 위치와 크기의 형태를 이어 두 장면을 연결하는 전환", "conveys": "문장 끝 점이 차트의 점으로 이어져 문장과 데이터를 연결한다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "모양이 같은 대상을 통해 서로 다른 장면을 연결할 때", "설명에서 데이터로 넘어가며 시선 위치를 유지할 때" ], "goodExample": "문장 끝 주홍 마침표를 지름 48px로 키운 뒤 같은 중심과 크기의 차트 점으로 하드 컷한다", "badExample": "컷 뒤 점의 위치나 크기가 달라져 연결 형태가 튄다", "avoid": [ "컷 시점에 디졸브를 섞지 않는다", "대응하는 형태의 중심과 크기를 바꾸지 않는다" ], "params": [ { "name": "마침표 확대", "default": "scale 0.25→1", "range": "0.2~1", "note": "최종 지름 48px" }, { "name": "확대 지속", "default": "0.5s", "range": "0.3~0.7s", "note": "컷 전 형태를 보여준다" }, { "name": "컷 시각", "default": "1.25s", "range": "1.0~1.6s", "note": "순간적으로 장면 교체" }, { "name": "형태 일치", "default": "중심 (568,322), 지름 48px", "range": "컷 전후 동일", "note": "장면 좌표 기준" } ], "ease": "확대 power2.out, 컷은 tl.set", "snippet": "tl.to('#period', {scale:1, duration:0.5, ease:'power2.out'}, 0.35);\ntl.set('#A', {visibility:'hidden'}, 1.25);\ntl.set('#B', {visibility:'visible'}, 1.25);\ntl.to('.lead', {opacity:1, duration:0.2}, 1.6);", "pairsWith": [ "shape-morph", "crossfade", "zoom-to-detail" ], "engines": { "hyperframes": "장면 둘을 같은 좌표에 배치하고 하나의 paused GSAP 타임라인으로 전환한다. 전환 뒤 완성 장면을 홀드한다", "reelforge": "전환 비트의 시작 시각과 지속 시간을 고정하고 장면 레이어 두 개를 함께 렌더한다", "scrolline": "전환 구간의 진행률을 하나의 타임라인에 연결하고 양 끝에 읽기 위한 정지 구간을 둔다" }, "prompts": { "claude": "<대상>의 문장 끝 주홍 마침표와 차트 점을 연결하는 매치컷을 만들어줘. 마침표를 0.35초부터 0.5초 동안 scale 0.25에서 1로 power2.out 확대하고 최종 중심 (568,322), 지름 48px를 유지해. 1.25초에 A를 숨기고 B를 즉시 보이게 하며 B 점도 같은 중심과 크기로 두고 1.8초부터 3초까지 정지해.", "codex": "<파일>에 문장 마침표에서 차트 점으로 이어지는 하드 매치컷을 구현해. 두 점은 left 544px, top 298px, width와 height 48px로 두고 A 점만 0.35초부터 0.5초 동안 scale 0.25→1로 확대해. 1.25초에 visibility를 동시에 교체하고 1.233초와 1.267초를 캡처해 주홍 원 중심 (568,322)과 지름 48px가 같고 배경 장면만 바뀌는지 확인해. 2.9초 캡처로 완성 홀드도 확인해." }, "sources": [ { "title": "Match cut", "url": "https://en.wikipedia.org/wiki/Match_cut", "license": "개념 인용" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/match-cut/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/text-match-cut/registry-item.json", "license": "Apache-2.0" }, { "title": "Adobe", "url": "https://www.adobe.com/creativecloud/video/post-production/cuts-in-film/match-cut.html", "license": "unknown" }, { "title": "CapCut", "url": "https://www.capcut.com/resource/dissolve-transition-in-video", "license": "unknown" }, { "title": "motion dictionary 1-principles.md#7. 연속성·공간 모델·시선 유도", "url": "", "license": "own" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/match-cut/index.html", "mp4": "effects/match-cut/clip.mp4", "gif": "effects/match-cut/preview.gif", "poster": "effects/match-cut/poster.jpg" }, "no": "124", "oneLinerEn": "Matching shapes at the same position and size connect two scenes across a cut.", "conveysEn": "Connects a sentence to data by matching its final period to a chart dot.", "useWhenEn": [ "Connect different scenes through objects with matching shapes.", "Keep the viewer's gaze in place when moving from explanation to data." ], "promptsEn": { "claude": "Create a match cut for connecting the sentence's vermilion period to a chart dot. Starting at 0.35 seconds, enlarge the period from scale 0.25 to 1 over 0.5 seconds with power2.out, maintaining final center (568,322) and diameter 48px. At 1.25 seconds, hide A and immediately show B, with B's dot at the same center and size. Hold from 1.8 to 3 seconds.", "codex": "Implement a hard match cut from a sentence period to a chart dot in . Set both dots to left 544px, top 298px, width 48px, and height 48px. Enlarge only A's dot from scale 0.25 to 1 starting at 0.35 seconds over 0.5 seconds. Switch visibility simultaneously at 1.25 seconds. Capture at 1.233 and 1.267 seconds to verify identical vermilion circle center (568,322) and diameter 48px, with only the background scene changing. Capture at 2.9 seconds to verify the completed hold." }, "variants": [ "좌표 매치", "그래픽 은유", "미시에서 거시 스케일 승계", "pairs/match-cut-in", "pairs/match-cut-out", "Graphic match", "Motion match", "Color match", "Audio match", "Matched dissolve" ] }, { "slug": "shape-morph", "family": "transitions", "ko": "모프 전환", "en": "Morph", "aka": [ "형태 전환", "Shape morph", "Container Morph", "컨테이너 모프", "card-morph-anchor", "Optical flow morph cut", "광학 흐름 모프 컷", "Universal chart mark morph" ], "oneLiner": "같은 요소의 형태와 색을 바꿔 다음 장면의 구성 요소로 이어지는 전환", "conveys": "형태가 달라져도 정보의 연결과 대상의 연속성이 유지된다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "입력 도형을 출력 데이터의 막대로 연결할 때", "장면 사이에 같은 대상을 다른 역할로 이어갈 때" ], "goodExample": "먹 사각형이 주홍 원으로 변한 뒤 가로로 늘어나 고른다 62% 막대가 된다", "badExample": "사각형을 지우고 별도 원과 막대를 등장시켜 연속 변형이 사라진다", "avoid": [ "같은 DOM 요소를 유지한다", "변형 중 형태를 읽을 짧은 정지를 둔다" ], "params": [ { "name": "원 변형 시간", "default": "0.7s", "range": "0.4~0.9s", "note": "색과 모서리를 함께 변경" }, { "name": "원 홀드", "default": "0.2s", "range": "0.15~0.35s", "note": "다음 변형 전 원을 읽는다" }, { "name": "막대 배율", "default": "3.1 × 0.16", "range": "가로 2~4 × 세로 0.1~0.3", "note": "200px 원이 620×32px 막대로 변한다" }, { "name": "막대 변형 시간", "default": "0.75s", "range": "0.5~1s", "note": "위치와 크기를 동시에 연결" } ], "ease": "power2.inOut / power3.inOut", "snippet": "tl.set('.morph', {x:420, borderRadius:'0%', scaleX:1, scaleY:1});\ntl.to('.morph', {borderRadius:'50%', backgroundColor:'var(--verm)', duration:0.7, ease:'power2.inOut'}, 0.35);\ntl.to('.morph', {x:0, scaleX:3.1, scaleY:0.16, borderRadius:'0%', duration:0.75, ease:'power3.inOut'}, 1.25);\ntl.to('.b', {opacity:1, duration:0.3}, 1.75);", "pairsWith": [ "match-cut", "squash-stretch", "bar-grow" ], "engines": { "hyperframes": "장면 안쪽 래퍼를 paused GSAP 타임라인 하나로 움직인다. 3초 길이에서 마지막 0.5초 이상을 홀드한다.", "reelforge": "전환 전후 장면을 동시에 배치하고 이동·마스크·색 변화의 시작과 끝을 같은 시간축에 둔다.", "scrolline": "전환 시간을 스크롤 진행률로 환산한다. 역방향 seek에도 시작 상태가 복원되게 초기값을 타임라인에 둔다." }, "prompts": { "claude": "<대상>의 먹 사각형을 같은 DOM 요소로 주홍 원과 막대로 변형해줘. 200×200px 사각형은 0.35초부터 0.7초간 border-radius 0%에서 50%로 바꾸며 주홍으로 변하고 1.25초부터 0.75초간 x 420에서 0, scaleX 3.1, scaleY 0.16, border-radius 0%로 변해. 장면 B는 1.75초부터 0.3초간 드러내고 2.35초 이후 정지해.", "codex": "<파일>에서 left 64px, top 190px인 200×200px 요소를 transform-origin 0 50%로 두고 초기 x를 420px로 설정해. 0.35초부터 0.7초간 원과 주홍색으로 변형하고 1.25초부터 0.75초간 x 0, scaleX 3.1, scaleY 0.16으로 움직여 첫 막대로 연결해. 0.23초, 0.73초, 1.23초, 1.73초, 2.9초를 캡처해 같은 요소의 크기·색·형태가 이어지고 최종 막대가 620×32px인지 확인해." }, "sources": [ { "title": "GSAP Timeline 공식 문서", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 개념 참조" }, { "title": "Adobe", "url": "https://helpx.adobe.com/uk/premiere/desktop/add-video-effects/apply-video-transitions/apply-morph-cut-to-smoothen-jump-cuts.html", "license": "unknown" }, { "title": "Blackmagic Design", "url": "https://www.blackmagicdesign.com/products/davinciresolve/edit", "license": "unknown" }, { "title": "apache/echarts", "url": "https://echarts.apache.org/handbook/en/basics/release-note/5-2-0/", "license": "Apache-2.0" }, { "title": "Heer & Robertson 2007", "url": "https://sites.stat.columbia.edu/gelman/communication/HeerRobertson2007.pdf", "license": "unknown" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/modal-morph/registry-item.json", "license": "Apache-2.0" }, { "title": "greensock/GSAP", "url": "https://gsap.com/docs/v3/Plugins/Flip/", "license": "GSAP Standard License" }, { "title": "motiondivision/motion", "url": "https://motion.dev/docs/react-layout-animations", "license": "MIT" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/shape-morph/index.html", "mp4": "effects/shape-morph/clip.mp4", "gif": "effects/shape-morph/preview.gif", "poster": "effects/shape-morph/poster.jpg" }, "no": "125", "oneLinerEn": "The same element changes shape and color to become a component of the next scene.", "conveysEn": "Maintains the connection between information and the continuity of an object as its shape changes.", "useWhenEn": [ "Transform an input shape into a bar in output data.", "Carry the same object across scenes in a different role." ], "promptsEn": { "claude": "Transform the ink-black square in into a vermilion circle and then a bar using the same DOM element. Starting at 0.35 seconds over 0.7 seconds, change the 200×200px square from border-radius 0% to 50% while turning it vermilion. Starting at 1.25 seconds over 0.75 seconds, animate x from 420 to 0, scaleX to 3.1, scaleY to 0.16, and border-radius to 0%. Reveal scene B starting at 1.75 seconds over 0.3 seconds, and hold after 2.35 seconds.", "codex": "In , place a 200×200px element at left 64px and top 190px with transform-origin 0 50% and initial x 420px. Starting at 0.35 seconds over 0.7 seconds, morph it into a vermilion circle. Starting at 1.25 seconds over 0.75 seconds, animate to x 0, scaleX 3.1, and scaleY 0.16 to connect it to the first bar. Capture at 0.23, 0.73, 1.23, 1.73, and 2.9 seconds to verify continuity of the same element's size, color, and shape, and a final bar size of 620×32px." }, "variants": [ "카드에서 아이콘", "로고에서 CTA", "알약에서 메뉴", "cta-morph-press", "막대와 파이", "막대와 점", "지도와 막대", "선버스트와 트리맵", "Container Morph", "Optical flow morph cut" ] }, { "slug": "whip-pan", "family": "transitions", "ko": "휩팬", "en": "Whip Pan", "aka": [ "스위시 팬", "Swish pan", "Whip pan", "휘프 팬", "whip-pan-cut-mask", "whip-pan-directional", "hypercut-whip", "Whip pan cut" ], "oneLiner": "장면을 빠르게 수평으로 쓸며 가로 블러와 급감속으로 다음 장면에 안착하는 전환", "conveys": "카메라가 빠르게 방향을 돌려 다음 정보로 시선을 이동시킨다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "질문에서 결과로 빠르게 넘어갈 때", "연속 장면의 속도감과 방향을 유지할 때" ], "goodExample": "질문 장면이 왼쪽으로 쓸려 나가고 확률 장면이 같은 속도로 들어와 선명하게 멈춘다", "badExample": "장면 A와 B가 반대 방향으로 움직이거나 도착 뒤에도 블러가 남는다", "avoid": [ "블러는 이동 축에만 적용한다", "정착 시 블러를 0으로 복원한다", "빠른 전환을 연속 반복하지 않는다" ], "params": [ { "name": "이동 거리", "default": "-1168px", "range": "장면 너비의 -1배", "note": "A와 B를 한 스트립에 나란히 배치" }, { "name": "전환 시간", "default": "0.42s", "range": "0.3~0.6s", "note": "급가속과 급감속이 짧게 이어진다" }, { "name": "최대 가로 블러", "default": "28px", "range": "16~36px", "note": "세로 블러는 0px" }, { "name": "시작 시각", "default": "1.03s", "range": "0.6~1.2s", "note": "전환 전 문장을 읽을 시간" } ], "ease": "power4.inOut", "snippet": "const blur = {x:0};\nconst updateBlur = () => document.querySelector('#blur-x').setAttribute('stdDeviation', `${blur.x} 0`);\ntl.to('.strip', {x:-1168, duration:0.42, ease:'power4.inOut'}, 1.03);\ntl.to(blur, {x:28, duration:0.21, ease:'power3.in', onUpdate:updateBlur}, 1.03);\ntl.to(blur, {x:0, duration:0.21, ease:'power3.out', onUpdate:updateBlur}, 1.24);", "pairsWith": [ "push-transition", "pan", "anticipation" ], "engines": { "hyperframes": "장면 안쪽 래퍼를 paused GSAP 타임라인 하나로 움직인다. 3초 길이에서 마지막 0.5초 이상을 홀드한다.", "reelforge": "전환 전후 장면을 동시에 배치하고 이동·마스크·색 변화의 시작과 끝을 같은 시간축에 둔다.", "scrolline": "전환 시간을 스크롤 진행률로 환산한다. 역방향 seek에도 시작 상태가 복원되게 초기값을 타임라인에 둔다." }, "prompts": { "claude": "<대상>에 왼쪽 휩팬 전환을 만들어줘. 너비 1168px인 A와 B를 한 스트립에 나란히 두고 1.03초부터 0.42초간 x 0에서 -1168px로 power4.inOut 이동시켜. SVG feGaussianBlur의 stdDeviation은 프록시 tween으로 가로 0에서 28px로 0.21초간 증가한 뒤 0.21초간 0으로 복원하고 세로는 0으로 유지해. 1.95초 이후 완성 상태로 정지해.", "codex": "<파일>의 두 장면을 2336×580px 스트립 안에 left 0과 1168px로 배치해. 1.03초부터 0.42초간 power4.inOut으로 스트립 x를 -1168px까지 움직이고 SVG 가로 블러 프록시는 1.24초에 28px, 1.45초에 0px가 되게 해. 0.73초, 1.23초, 1.45초, 2.9초 캡처로 A와 B가 같은 방향으로 움직이고 가로만 흐려지며 최종 장면이 선명하게 유지되는지 확인해." }, "sources": [ { "title": "GSAP Timeline 공식 문서", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 개념 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/whip-pan/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/whip-pan-cut/registry-item.json", "license": "Apache-2.0" }, { "title": "Adobe", "url": "https://helpx.adobe.com/premiere/desktop/add-video-effects/effects-and-transitions-library/list-of-video-transitions.html", "license": "unknown" }, { "title": "remotion-dev/remotion", "url": "https://www.remotion.dev/docs/transitions/presentations/blur-slide", "license": "Remotion License" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/whip-pan/index.html", "mp4": "effects/whip-pan/clip.mp4", "gif": "effects/whip-pan/preview.gif", "poster": "effects/whip-pan/poster.jpg" }, "no": "126", "oneLinerEn": "A rapid horizontal sweep with horizontal blur and sharp deceleration settles into the next scene.", "conveysEn": "Moves attention to the next information as if the camera rapidly pans.", "useWhenEn": [ "Move quickly from a question to a result.", "Maintain speed and direction across consecutive scenes." ], "promptsEn": { "claude": "Create a leftward whip-pan transition for . Place A and B, each 1168px wide, side by side in one strip. Starting at 1.03 seconds over 0.42 seconds, animate x from 0 to -1168px with power4.inOut. Tween a proxy for SVG feGaussianBlur stdDeviation, increasing horizontal blur from 0 to 28px over 0.21 seconds, then restoring it to 0 over 0.21 seconds. Keep vertical blur at 0. Hold the completed state after 1.95 seconds.", "codex": "Place the two scenes in at left 0 and 1168px inside a 2336×580px strip. Starting at 1.03 seconds over 0.42 seconds, animate the strip x to -1168px with power4.inOut. Set the SVG horizontal blur proxy to reach 28px at 1.24 seconds and 0px at 1.45 seconds. Capture at 0.73, 1.23, 1.45, and 2.9 seconds to verify that A and B move in the same direction, only horizontal blur appears, and the final scene remains sharp." }, "variants": [ "좌우", "상하", "씬 내부 교체", "씬 경계 전환", "pairs/whip-pan-in", "pairs/whip-pan-out", "Whip pan cut", "Whip Pan / Swish Pan Transition" ] }, { "slug": "zoom-through", "family": "transitions", "ko": "줌 전환", "en": "Zoom Through", "aka": [ "줌 스루", "Zoom transition", "Zoom through", "줌 통과", "줌 관통", "zoom-through-portal", "Cross zoom", "교차 줌" ], "oneLiner": "글자 O의 구멍을 확대해 그 안의 다음 장면으로 통과하는 전환", "conveys": "같은 중심을 향해 깊이 들어가며 다음 정보로 연결된다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "중급", "useWhen": [ "제목의 글자 내부를 다음 장면의 입구로 사용할 때", "전체 개요에서 확률 같은 세부 정보로 들어갈 때" ], "goodExample": "O 중심을 고정한 채 8배 확대해 구멍 안에서 후보 확률 장면이 드러난다", "badExample": "구멍과 카메라의 중심이 달라 O의 먹 면이 화면을 가린다", "avoid": [ "구멍 밖에서 다음 장면이 먼저 노출되지 않게 한다", "확대 중 중심을 옮기지 않는다" ], "params": [ { "name": "확대 배율", "default": "8", "range": "6~12", "note": "구멍이 장면의 네 모서리를 덮는 배율" }, { "name": "확대 시간", "default": "1.35s", "range": "0.8~1.5s", "note": "출발은 천천히, 끝은 빠르게" }, { "name": "시작 시각", "default": "0.4s", "range": "0.3~0.5s", "note": "O를 먼저 읽는 준비 시간" }, { "name": "다음 장면 초기 배율", "default": "0.18", "range": "0.12~0.3", "note": "구멍 속 먼 장면으로 시작" } ], "ease": "power3.in / power2.out", "snippet": "tl.set('.content', {scale:0.18, opacity:0});\ntl.to('.a', {scale:8, duration:1.35, ease:'power3.in'}, 0.4);\ntl.to('.content', {opacity:1, duration:0.25}, 0.7);\ntl.to('.content', {scale:1, duration:1.15, ease:'power2.out'}, 0.7);\ntl.set('.a', {opacity:0}, 1.75);", "pairsWith": [ "push-in", "zoom-to-detail", "match-cut" ], "engines": { "hyperframes": "장면 안쪽 래퍼를 paused GSAP 타임라인 하나로 움직인다. 3초 길이에서 마지막 0.5초 이상을 홀드한다.", "reelforge": "전환 전후 장면을 동시에 배치하고 이동·마스크·색 변화의 시작과 끝을 같은 시간축에 둔다.", "scrolline": "전환 시간을 스크롤 진행률로 환산한다. 역방향 seek에도 시작 상태가 복원되게 초기값을 타임라인에 둔다." }, "prompts": { "claude": "<대상>에 글자 O를 통과하는 줌 전환을 만들어줘. 1168×580 장면의 중심 584px, 290px에 구멍 반경 95px, 140px인 O를 두고 0.4초부터 1.35초간 power3.in으로 8배 확대해. 구멍 아래 장면은 0.7초부터 1.15초간 0.18배에서 1배로 확대하고 2.25초 이후 정지해.", "codex": "<파일>의 장면 전환에 중심 584px, 290px의 O 마스크 래퍼를 적용해. 0.4초부터 scale 1에서 8로 1.35초간 power3.in으로 움직이고 다음 장면은 0.7초부터 1.15초간 scale 0.18에서 1로 움직여. 0.23초, 1.23초, 1.73초, 2.9초를 캡처해 O의 구멍 안에만 장면 B가 드러나고 마지막에 먹 테두리 없이 완성 상태가 남는지 확인해." }, "sources": [ { "title": "GSAP Timeline 공식 문서", "url": "https://gsap.com/docs/v3/GSAP/Timeline/", "license": "공식 문서 개념 참조" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/zoom-through-transition/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/cinematic-zoom/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/transitions-scale/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/shared-axis-z/registry-item.json", "license": "Apache-2.0" }, { "title": "pixel-point/animate-text", "url": "https://raw.githubusercontent.com/pixel-point/animate-text/master/skills/animate-text/assets/specs/shared-axis-z.json", "license": "unknown" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/work-with-layers/camera-layer/cameras-lights-points-interest.html", "license": "unknown" }, { "title": "Kurzgesagt", "url": "https://kurzgesagt.org/what-we-do?visit=videos", "license": "unknown" } ], "runtime": "gsap", "render": "done", "files": { "html": "effects/zoom-through/index.html", "mp4": "effects/zoom-through/clip.mp4", "gif": "effects/zoom-through/preview.gif", "poster": "effects/zoom-through/poster.jpg" }, "no": "127", "oneLinerEn": "The opening in the letter O enlarges to carry the viewer through to the next scene inside it.", "conveysEn": "Connects to the next information by moving deeper toward a shared center.", "useWhenEn": [ "Use the inside of a title letter as an entrance to the next scene.", "Move from an overview into details such as probabilities." ], "promptsEn": { "claude": "Create a zoom-through transition through the letter O for . Place an O with opening radii 95px and 140px at the center of a 1168×580 scene, at 584px, 290px. Starting at 0.4 seconds over 1.35 seconds, enlarge it to 8 times its size with power3.in. Scale the scene beneath the opening from 0.18 to 1 starting at 0.7 seconds over 1.15 seconds, and hold after 2.25 seconds.", "codex": "Apply an O mask wrapper centered at 584px, 290px to the scene transition in . Starting at 0.4 seconds over 1.35 seconds, animate scale from 1 to 8 with power3.in. Animate the next scene from scale 0.18 to 1 starting at 0.7 seconds over 1.15 seconds. Capture at 0.23, 1.23, 1.73, and 2.9 seconds to verify that scene B appears only inside the O opening and that the completed state remains without an ink-black border at the end." }, "variants": [ "animate-text/shared-axis-z", "8배", "20배", "로고 구멍", "문틀 포털", "pairs/zoom-portal-in", "pairs/zoom-portal-out", "CrossZoom", "SimpleZoom", "SimpleZoomOut" ] }, { "slug": "blur-dissolve", "family": "transitions", "ko": "블러 디졸브", "en": "Blur Dissolve", "aka": [ "Blur dissolve", "Blur through", "Defocus dissolve", "초점 흐림 디졸브", "Linear blur dissolve", "직선 블러 디졸브", "Horizontal blur dissolve", "가로 블러 디졸브", "VR spherical blur transition", "구면 블러 전환" ], "oneLiner": "두 장면이 흐려진 채 겹쳐 교체되고 새 장면이 다시 선명해지는 전환", "conveys": "시간이 흐르거나 기억 속으로 들어가는 부드러운 분위기 변화. 컷의 각진 느낌 없이 초점이 옮겨 간다", "purposes": [ "전환", "분위기" ], "media": [ "설명 영상", "발표", "숏폼" ], "level": "기본", "useWhen": [ "시간이 지났거나 장소가 바뀌는 장면 사이를 부드럽게 이을 때", "같은 톤의 두 화면을 컷 없이 이어 붙일 때" ], "goodExample": "앞 장면이 0.35초 동안 12px까지 흐려지며 사라지고, 뒤 장면이 12px 흐림에서 선명해지며 0.7초 안에 자리를 잡는다", "badExample": "블러를 40px 이상 걸어 화면이 뭉개진 채 오래 머물거나, 흐림 없이 단순 페이드만 걸어 효과가 안 보인다", "avoid": [ "최대 블러 20px 초과 금지(해상도 높은 영상에서 렌더가 느려지고 뭉개진다)", "글자가 핵심인 장면은 블러 정점에서 글자가 읽히지 않으므로 정점 시간을 0.15초 이내로 줄인다" ], "params": [ { "name": "전체 지속", "default": "0.7s", "range": "0.5~1.0s", "note": "교차점은 50%" }, { "name": "최대 블러", "default": "12px", "range": "6~20px", "note": "1920x1080 기준" }, { "name": "교차 opacity", "default": "0→1 / 1→0", "range": "선형~power1", "note": "정점에서 두 장면 합이 1" }, { "name": "이징", "default": "power1.inOut", "range": "sine~power2", "note": "강하게 걸면 블러가 튄다" } ], "ease": "power1.inOut", "snippet": "tl.to('.a', { filter: 'blur(12px)', opacity: 0, duration: 0.7, ease: 'power1.inOut' }, 0)\n .fromTo('.b', { filter: 'blur(12px)', opacity: 0 },\n { filter: 'blur(0px)', opacity: 1, duration: 0.7, ease: 'power1.inOut' }, 0);", "pairsWith": [ "crossfade", "blur-resolve", "dip-to-color" ], "engines": { "hyperframes": "두 레이어를 겹치고 filter와 opacity를 같은 paused 타임라인에서 보간한다. 블러 반경은 seek마다 같은 값이 나오도록 타임라인 값으로만 쓴다", "reelforge": "씬 워커 브리프에 blurPx=12, durationMs=700, crossPoint=0.5를 실어 두 씬의 경계에서만 호출하게 한다", "scrolline": "진행률 p를 0~1로 받아 blur = 12*sin(pi*p)로 매핑한다. scrub에서는 스프링 대신 ease-out을 쓰고 정점이 진행률 0.5에 오게 한다" }, "prompts": { "claude": "<대상A>와 <대상B> 장면 사이에 블러 디졸브를 GSAP로 만들어줘. A는 0.7초 동안 blur 0에서 12px로 흐려지며 opacity 0으로, B는 blur 12px에서 0으로 선명해지며 opacity 1로 가게 하고 이징은 power1.inOut, 시작 시각은 둘 다 같게 해. paused 타임라인 하나로 seek가 되게 해.", "codex": "<파일>의 장면 경계에 블러 디졸브를 넣어. 앞 레이어 filter blur(0)에서 blur(12px)로, 뒤 레이어는 blur(12px)에서 blur(0)로 0.7초 동안 보간하고 ease power1.inOut을 쓴다. 0.35초 시점을 캡처해 두 장면이 모두 흐리고 절반씩 보이는지, 0.7초에 뒤 장면이 완전히 선명한지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/transitions-blur/registry-item.json", "license": "Apache-2.0" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/DefocusBlur.glsl", "license": "MIT" }, { "title": "remotion-dev/remotion", "url": "https://github.com/remotion-dev/remotion/blob/main/packages/template-prompt-to-video/src/lib/utils.ts", "license": "Remotion License" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/LinearBlur.glsl", "license": "MIT" } ], "runtime": "css", "variants": [ "블러 통과", "DefocusBlur", "LinearBlur", "hblur", "Defocus dissolve", "Linear blur dissolve", "Horizontal blur dissolve", "VR spherical blur transition" ], "oneLinerEn": "Two scenes overlap while blurred, swap, and the new scene sharpens back into focus.", "conveysEn": "A soft shift in time or mood, as if focus is moving instead of the picture being cut.", "useWhenEn": [ "Bridge a time skip or location change smoothly.", "Join two shots of a similar tone without a hard cut." ], "promptsEn": { "claude": "Build a blur dissolve between and in GSAP. Over 0.7 seconds, A blurs from 0 to 12px while fading to opacity 0, and B goes from 12px blur to 0 while fading to opacity 1. Use power1.inOut, start both at the same time, and keep everything in a single paused timeline that supports seeking.", "codex": "Add a blur dissolve at the scene boundary in . Tween the outgoing layer from blur(0) to blur(12px) and the incoming layer from blur(12px) to blur(0) over 0.7 seconds with ease power1.inOut. Capture at 0.35 seconds to confirm both scenes are blurred and half visible, and at 0.7 seconds to confirm the new scene is fully sharp." }, "render": "done", "files": { "html": "effects/blur-dissolve/index.html", "mp4": "effects/blur-dissolve/clip.mp4", "gif": "effects/blur-dissolve/preview.gif", "poster": "effects/blur-dissolve/poster.jpg" }, "no": "128" }, { "slug": "bounce-transition", "family": "transitions", "ko": "바운스 전환", "en": "Bounce Transition", "aka": [ "Bounce reveal", "바운스 덮기" ], "oneLiner": "장면 경계가 아래로 떨어져 바닥에서 여러 번 튀고 그림자가 뒤따르며 새 장면을 보여준다", "conveys": "장면이 무게 있는 물체처럼 떨어져 바닥에서 튄다. 가볍고 유쾌한 톤을 만든다", "purposes": [ "전환", "분위기" ], "media": [ "숏폼", "설명 영상", "웹 UI" ], "level": "기본", "useWhen": [ "캐주얼하고 밝은 톤의 설명 영상에서 장면을 툭 떨어뜨려 덮을 때", "제품 소개 카드가 아래에서 튀어 들어와 다음 섹션을 열 때" ], "goodExample": "새 장면이 위에서 0.9초 동안 떨어져 바닥에서 세 번 튀고, 경계 아래로 옅은 그림자가 함께 움직인다", "badExample": "튐 횟수가 6번을 넘어 끝나기까지 오래 걸리거나, 그림자가 없어 종이가 흔들리는 것처럼 보인다", "avoid": [ "바운스 횟수는 3회 이하로 둔다", "진지하고 격식 있는 발표에는 쓰지 않는다" ], "params": [ { "name": "지속", "default": "900ms", "range": "700~1100ms", "note": "마지막 정지까지 포함" }, { "name": "튐 횟수", "default": "3", "range": "2~4", "note": "bounce.out이 기본 3회 근사" }, { "name": "그림자 높이", "default": "화면 높이 7.5%", "range": "5~10%", "note": "경계 아래 그라디언트" }, { "name": "그림자 opacity", "default": "0.6", "range": "0.4~0.7", "note": "검정" }, { "name": "낙하 거리", "default": "화면 높이 100%", "range": "100%", "note": "위에서 시작" } ], "ease": "bounce.out", "snippet": "tl.fromTo('.next', { y: -1080 }, { y: 0, duration: 0.9, ease: 'bounce.out' }, 0);\ntl.fromTo('.shadow', { y: -1080 }, { y: 0, duration: 0.9, ease: 'bounce.out' }, 0);\ntl.to('.prev', { opacity: 0, duration: 0.01 }, 0.9);", "pairsWith": [ "spring-overshoot", "squash-stretch", "push-transition" ], "engines": { "hyperframes": "bounce.out은 seek해도 결정론이라 그대로 쓴다. 그림자 레이어를 next와 같은 이징으로 묶어 paused 타임라인 하나에 둔다", "reelforge": "씬 워커 브리프에 지속 900ms, 튐 3회, 그림자 opacity 0.6을 파라미터로 싣고 그림자 레이어를 별도 요소로 요구한다", "scrolline": "scrub에서는 바운스가 진행률에 따라 앞뒤로 튀어 어색하다. ease-out 한 번 착지로 바꾸고 튐은 홀드 구간의 짧은 재생으로 뺀다" }, "prompts": { "claude": "<대상> 장면 전환에 바운스 전환을 넣어줘. 다음 장면이 화면 위(-1080px)에서 0.9초 동안 bounce.out으로 떨어지고, 같은 이징의 그림자 레이어(높이 화면의 7.5%, 검정 opacity 0.6)가 경계 아래에서 함께 움직이게 해. 착지 뒤 0.4초 정지. GSAP 타임라인 하나로 seek 가능하게 해줘.", "codex": "<파일>의 전환부에 바운스 전환을 구현해. .next를 y -1080에서 0으로 0.9초 bounce.out, .shadow는 같은 트윈, .prev는 0.9초 시점에 숨겨. 0.3초, 0.6초, 0.9초, 1.4초 시점을 캡처해 튐이 세 번 보이는지, 착지 후 그림자가 사라지는지, 마지막 프레임에 이전 장면이 안 보이는지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/Bounce.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "gsap", "variants": [ "Bounce", "Bounce reveal" ], "oneLinerEn": "The scene boundary drops, bounces off the floor several times with a trailing shadow, and reveals the new scene.", "conveysEn": "Makes a scene feel like a physical object with weight. Light and cheerful.", "useWhenEn": [ "When dropping a scene in to cover the old one in a casual, bright explainer", "When a product card bounces in from below to open the next section" ], "promptsEn": { "claude": "Add a Bounce Transition to . The next scene falls from y -1080px over 0.9s with bounce.out, while a shadow layer (7.5% of frame height, black at 0.6 opacity) moves with it using the same ease. Hold 0.4s after landing. Use a single seekable GSAP timeline.", "codex": "Implement a bounce transition in . Tween .next from y -1080 to 0 over 0.9s with bounce.out, tween .shadow identically, hide .prev at 0.9s. Capture at 0.3s, 0.6s, 0.9s, and 1.4s to confirm three visible bounces, that the shadow disappears after landing, and that the old scene is gone in the last frame." }, "render": "planned", "files": {}, "no": "129" }, { "slug": "burn-transition", "family": "transitions", "ko": "번 전환", "en": "Burn Transition", "aka": [ "Page burn", "페이지 소각", "Noise burn edge", "노이즈 연소 테두리", "dissolve()", "Undulating burn out", "물결 연소 와이프" ], "oneLiner": "불규칙한 경계가 타들어 가듯 퍼지며 앞 장면을 지우고 다음 장면을 드러내는 전환", "conveys": "파괴와 새 시작. 경계에 남는 뜨거운 빛이 극적인 무게를 준다", "purposes": [ "전환", "주목 끌기" ], "media": [ "숏폼", "설명 영상", "제품 시연" ], "level": "고급", "useWhen": [ "낡은 방식에서 새 방식으로 넘어가는 이야기의 전환점에서", "극적이고 손에 잡히는 질감이 필요한 오프닝이나 클라이맥스에서" ], "goodExample": "노이즈 임계값이 1초 동안 0에서 1로 올라가며 타는 경계가 번지고, 경계 폭 화면의 1%에 주황색 빛이 남는다", "badExample": "경계 노이즈가 프레임마다 달라져 깜빡이거나, 발광색이 채도 100%라 화면이 조잡해 보인다", "avoid": [ "노이즈 시드는 고정한다(Math.random 금지)", "발광 경계 폭 화면 2% 초과 금지" ], "params": [ { "name": "전체 지속", "default": "1.0s", "range": "0.7~1.5s", "note": "진행률 0에서 1" }, { "name": "시드", "default": "1", "range": "정수", "note": "고정 필수" }, { "name": "발광 폭", "default": "화면의 1%", "range": "0.5~2%", "note": "경계 띠 두께" }, { "name": "발광색", "default": "#ff8a3d", "range": "주황~황색", "note": "중심은 밝게 #ffd9a0" } ], "ease": "power1.inOut", "snippet": "const u = { p: 0 };\ntl.to(u, { p: 1, duration: 1.0, ease: 'power1.inOut', onUpdate: () => {\n const t = u.p * 1.2 - 0.1; // 임계값\n matte.setAttribute('tableValues', `0 ${t} ${t + 0.02} 1`);\n} });", "pairsWith": [ "noise-dissolve", "light-leak", "edge-trace-transition" ], "engines": { "hyperframes": "SVG feTurbulence의 seed를 고정하고 feComponentTransfer로 임계값만 움직인다. 발광 띠는 임계값 ±0.02 구간에 합성한다", "reelforge": "씬 워커 브리프에 seed, burnWidthPct, edgeColor, durationMs를 싣는다. 노이즈 텍스처는 미리 구워 두면 렌더가 안정적이다", "scrolline": "진행률 p를 임계값에 직접 매핑한다. 스크롤을 정지했을 때 반쯤 탄 화면이 남는 것이 의도이므로 경계 색을 절제한다" }, "prompts": { "claude": "<대상A>에서 <대상B>로 번 전환을 만들어줘. seed 1 고정 노이즈 마스크의 임계값을 1초 동안 power1.inOut으로 0에서 1로 올려 A를 태우며 B를 드러내고, 경계에 화면의 1% 폭으로 #ff8a3d 발광 띠를 넣어. Math.random은 쓰지 말고 진행값은 GSAP 상태 객체로 paused 타임라인에서 seek되게 해.", "codex": "<파일>에 burn transition을 적용해. 고정 seed=1 노이즈 임계값 progress를 1.0s, power1.inOut으로 0에서 1로 보간하고 임계값 근처 1% 폭에 #ff8a3d 발광을 합성한다. 0.3초, 0.5초, 0.8초 프레임에서 구멍이 확장하는지 확인하고 같은 시각을 두 번 캡처해 픽셀이 동일한지도 비교해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/ridged-burn/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/transitions-destruction/registry-item.json", "license": "Apache-2.0" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/dissolve.glsl", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/burn0.glsl", "license": "MIT" }, { "title": "remotion-dev/remotion", "url": "https://www.remotion.dev/docs/transitions/presentations/dissolve", "license": "Remotion License" } ], "runtime": "webgl", "variants": [ "dissolve", "burn0", "undulatingBurnOut", "모서리 시작", "중심 시작", "노이즈 구멍", "맥동 경계", "Page burn", "Noise burn edge", "Undulating burn out" ], "oneLinerEn": "An irregular edge burns outward, erasing the old scene and revealing the next, leaving a hot glow along the border.", "conveysEn": "Destruction and a fresh start, given dramatic weight by the glowing edge.", "useWhenEn": [ "Mark the turning point from an old way to a new one in a story.", "Open or climax a piece that needs a tactile, dramatic texture." ], "promptsEn": { "claude": "Build a burn transition from to . Raise a fixed-seed (seed 1) noise mask threshold from 0 to 1 over 1 second with power1.inOut to burn A away and reveal B, with a #ff8a3d glowing band about 1% of the frame wide at the edge. No Math.random; drive progress from a GSAP state object in one paused timeline that seeks.", "codex": "Apply a burn transition in . Tween a fixed seed=1 noise threshold from 0 to 1 over 1.0s with power1.inOut and composite a #ff8a3d glow across a 1% band near the threshold. Check that holes expand at 0.3, 0.5, and 0.8 seconds, and capture the same timestamp twice to confirm identical pixels." }, "render": "planned", "files": {}, "no": "130" }, { "slug": "chromatic-wipe", "family": "transitions", "ko": "크로매틱 와이프", "en": "Chromatic Wipe", "aka": [ "Chromatic wipe", "색수차 와이프" ], "oneLiner": "색 채널이 벌어지며 화면이 밀려 나가고 새 화면에서 다시 합쳐지는 전환", "conveys": "디지털 속도감과 렌즈 왜곡. 빠르고 차가운 기술적 인상", "purposes": [ "전환", "주목 끌기" ], "media": [ "숏폼", "제품 시연", "웹 UI" ], "level": "중급", "useWhen": [ "기술 제품이나 사이버 톤의 영상에서 빠르게 장면을 넘길 때", "와이프에 한 겹 더 질감을 얹고 싶을 때" ], "goodExample": "빨강 복사본이 왼쪽으로 최대 80px, 파랑 복사본이 오른쪽으로 80px 벌어지며 화면이 밀리고, 0.5초 안에 새 화면에서 채널이 합쳐진다", "badExample": "변위를 200px 넘게 벌려 세 화면이 따로 보이거나, 밝은 글자가 색 번짐으로 읽히지 않는다", "avoid": [ "채널 변위 최대 80px 초과 금지(1920px 기준)", "글자 장면은 변위 정점을 0.1초 이내로 끊는다" ], "params": [ { "name": "전체 지속", "default": "0.5s", "range": "0.35~0.8s", "note": "와이프와 변위가 함께" }, { "name": "최대 채널 변위", "default": "80px", "range": "30~100px", "note": "R는 음의 x, B는 양의 x" }, { "name": "와이프 방향", "default": "좌에서 우", "range": "4방향", "note": "변위 방향과 맞춘다" }, { "name": "합성", "default": "screen 또는 plus-lighter", "range": "", "note": "채널 복사본 합성" } ], "ease": "power3.inOut", "snippet": "tl.to('.r', { x: -80, duration: 0.25, ease: 'power2.out' }, 0)\n .to('.b', { x: 80, duration: 0.25, ease: 'power2.out' }, 0)\n .to('.clip', { clipPath: 'inset(0 0 0 100%)', duration: 0.5, ease: 'power3.inOut' }, 0)\n .to(['.r', '.b'], { x: 0, duration: 0.25, ease: 'power2.in' }, 0.25);", "pairsWith": [ "glitch-rgb-split", "wipe", "glitch-transition" ], "engines": { "hyperframes": "R, G, B 복사본은 mix-blend-mode: screen으로 겹치고 x만 타임라인에서 움직인다. 복사본이 3배 무거워지므로 해상도가 큰 영상은 미리 확인한다", "reelforge": "씬 워커 브리프에 maxShiftPx, wipeMs, direction을 실어 채널 복사 레이어가 씬 안에서만 생성되게 한다", "scrolline": "진행률 0~0.5에 변위를 0에서 80px로, 0.5~1에 80px에서 0으로 삼각형 매핑한다. 스프링은 쓰지 않는다" }, "prompts": { "claude": "<대상A>에서 <대상B>로 크로매틱 와이프를 만들어줘. A의 빨강 복사본을 -80px, 파랑 복사본을 +80px로 0.25초 동안 벌리면서 왼쪽에서 오른쪽으로 0.5초 동안 와이프하고, 뒤 절반 0.25초에 채널이 0으로 합쳐지게 해. 이징 power3.inOut, mix-blend-mode screen, paused 타임라인 하나.", "codex": "<파일>에 chromatic wipe를 넣어. R 복사본 x -80px, B 복사본 x +80px (각 0.25s power2.out), 동시에 inset clipPath 와이프 0.5s power3.inOut, 이후 채널 x를 0으로 복귀 (0.25s). 0.25초 시점 캡처에서 색 분리가 보이는지, 0.5초에 분리가 사라졌는지, 텍스트 가독성이 유지되는지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/chromatic-aberration-wipe/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/chromatic-radial-split/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/transitions/css-distortion.md", "license": "unknown" } ], "runtime": "css", "variants": [], "oneLinerEn": "Color channels split apart as the frame slides away, then recombine on the new frame.", "conveysEn": "Digital speed and lens distortion: a cold, technical impression.", "useWhenEn": [ "Move quickly between scenes in a tech or cyber-toned video.", "Add an extra layer of texture on top of a wipe." ], "promptsEn": { "claude": "Build a chromatic wipe from to . Split A's red copy to -80px and blue copy to +80px over 0.25 seconds while wiping left to right over 0.5 seconds, then bring the channels back to 0 in the last 0.25 seconds. Use power3.inOut, mix-blend-mode screen, one paused timeline.", "codex": "Add a chromatic wipe in . R copy x to -80px, B copy x to +80px (0.25s each, power2.out), with an inset clipPath wipe over 0.5s power3.inOut, then return channels to x 0 (0.25s). Capture at 0.25 seconds to confirm visible color separation, at 0.5 seconds to confirm it has closed, and check text legibility." }, "render": "planned", "files": {}, "no": "131" }, { "slug": "clock-wipe", "family": "transitions", "ko": "클록 와이프", "en": "Clock Wipe", "aka": [ "Clock wipe", "시계 와이프", "Pinwheel wipe", "바람개비 와이프", "Radial Wipe", "방사 와이프" ], "oneLiner": "중심에서 회전하는 부채꼴 경계가 다음 장면을 드러내는 와이프", "conveys": "시간이 흐른다는 연상. 시계 바늘이 지나가며 다음 장면이 열린다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "발표", "데이터 스토리" ], "level": "중급", "useWhen": [ "타임라인, 진행률, 시간 경과를 다룬 장면으로 넘어갈 때", "한 방향으로 도는 경계로 리듬감 있게 장면을 바꿀 때" ], "goodExample": "12시 방향에서 시작한 경계가 0.8초 동안 시계 방향으로 한 바퀴 돌며 다음 장면을 드러낸다", "badExample": "경계 속도가 들쭉날쭉하거나, 한 바퀴에 2초 이상 걸려 지루하다", "avoid": [ "이징은 linear를 기본으로 한다(가감속이 있으면 시계처럼 보이지 않는다)", "중심이 피사체와 겹치면 중심을 피사체 밖으로 옮긴다" ], "params": [ { "name": "전체 지속", "default": "0.8s", "range": "0.5~1.2s", "note": "한 바퀴 기준" }, { "name": "시작 각도", "default": "0deg (12시)", "range": "0~270deg", "note": "시계 방향" }, { "name": "중심", "default": "50% 50%", "range": "임의 좌표", "note": "피사체 위 피함" }, { "name": "날개 수", "default": "1", "range": "1~4", "note": "4는 핀휠 변형" } ], "ease": "none", "snippet": "const s = { a: 0 };\ntl.to(s, { a: 360, duration: 0.8, ease: 'none', onUpdate: () => {\n document.querySelector('.b').style.clipPath =\n `conic-gradient(from 0deg at 50% 50%, #000 ${s.a}deg, transparent 0)`; } });", "pairsWith": [ "wipe", "iris-mask", "mask-reveal" ], "engines": { "hyperframes": "마스크 각도를 paused 타임라인의 onUpdate에서 계산해 쓴다. 각도 값은 상태 객체 하나에 둬서 seek 때 같은 결과가 나오게 한다", "reelforge": "씬 워커 브리프에 centerX, centerY, sweepMs, wings를 파라미터로 싣는다. 뒤 장면은 mask로만 노출한다", "scrolline": "진행률 p를 각도 360*p로 직결한다. 이징을 걸지 않아야 스크롤 속도와 회전 속도가 일치한다" }, "prompts": { "claude": "<대상A> 위에 <대상B>가 클록 와이프로 드러나게 해줘. 12시 방향에서 시작해 시계 방향으로 0.8초 동안 360도 돌고, 중심은 50% 50%, 이징은 none(linear). mask는 conic-gradient로 만들고 각도는 GSAP 상태 객체에서 읽어 paused 타임라인 하나로 seek 가능하게 해.", "codex": "<파일>에 clock wipe를 적용해. 뒤 레이어에 conic-gradient mask를 걸고 각도를 0에서 360도로 0.8초, ease none으로 보간한다. 0.2초, 0.4초, 0.6초 시점을 캡처해 경계가 90도, 180도, 270도 근처인지 확인하고 0.8초에 뒤 장면이 전체 노출되는지 본다." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/angular.glsl", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/Radial.glsl", "license": "MIT" }, { "title": "FFmpeg/FFmpeg", "url": "https://ffmpeg.org/ffmpeg-filters.html#xfade", "license": "LGPL-2.1-or-later" }, { "title": "remotion-dev/remotion", "url": "https://www.remotion.dev/docs/transitions/presentations/clock-wipe", "license": "Remotion License" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "svg", "variants": [ "angular", "Radial", "radial", "pinwheel", "시계방향", "반시계방향", "양방향", "시계 방향", "반시계 방향", "핀휠", "Pinwheel wipe", "Radial Wipe" ], "oneLinerEn": "A rotating pie-shaped edge sweeps from the center and reveals the next scene.", "conveysEn": "The feeling of time passing, as if a clock hand is opening the next scene.", "useWhenEn": [ "Move into a scene about timelines, progress, or elapsed time.", "Change scenes with a single rhythmic sweep." ], "promptsEn": { "claude": "Reveal over with a clock wipe. Start at 12 o'clock and sweep clockwise 360 degrees over 0.8 seconds, center at 50% 50%, ease none (linear). Build the mask with conic-gradient, read the angle from a GSAP state object, and keep it in one paused timeline that supports seeking.", "codex": "Apply a clock wipe in . Put a conic-gradient mask on the incoming layer and tween the angle from 0 to 360 degrees over 0.8 seconds with ease none. Capture at 0.2, 0.4, and 0.6 seconds to check the edge sits near 90, 180, and 270 degrees, and at 0.8 seconds to confirm the new scene is fully shown." }, "render": "planned", "files": {}, "no": "132" }, { "slug": "column-melt", "family": "transitions", "ko": "컬럼 멜트", "en": "Column Melt", "aka": [ "Screen melt columns", "세로 기둥 녹아내리기" ], "oneLiner": "세로로 잘린 화면 띠들이 서로 다른 속도로 아래로 내려가 다음 장면을 드러낸다", "conveys": "화면이 무게를 받아 위에서부터 녹아내리는 느낌. 게임 종료나 장면 폭파처럼 거칠고 장난스러운 교체에 어울린다", "purposes": [ "전환", "분위기" ], "media": [ "숏폼", "설명 영상" ], "level": "중급", "useWhen": [ "게임, 레트로, 코미디 톤의 장면에서 이전 화면을 무너뜨리듯 지울 때", "짧은 숏폼에서 컷 하나를 눈에 띄게 만들고 싶을 때" ], "goodExample": "화면을 세로 30개 띠로 나눠 띠마다 0~0.25초씩 다르게 출발해 1.0초 안에 모두 아래로 빠지고 새 장면이 자리를 채운다", "badExample": "띠 수가 6개 이하라 블라인드처럼 보이거나, 지연 편차가 없어 화면 전체가 한 장으로 내려간다", "avoid": [ "띠 지연 편차는 0.3초 이하로 둔다(길면 전환이 끝나지 않은 느낌)", "진지한 업무 보고나 데이터 장표에는 쓰지 않는다" ], "params": [ { "name": "띠 수", "default": "30", "range": "16~48", "note": "많을수록 고운 녹음" }, { "name": "띠별 지연 편차", "default": "0~0.25s", "range": "0.1~0.3s", "note": "시드 난수로 고정" }, { "name": "전체 지속", "default": "1000ms", "range": "700~1200ms", "note": "가장 늦은 띠가 끝나는 시각" }, { "name": "낙하 거리", "default": "화면 높이 100%", "range": "100~110%", "note": "띠가 화면 밖으로 완전히 나가야 함" }, { "name": "이징", "default": "none(linear)", "range": "none~power1.in", "note": "중력이 붙는 가속은 약하게" } ], "ease": "none / power1.in", "snippet": "const N=30, H=1080; // 시드 고정 지연표\nconst d = i => ((i*37)%N)/N*0.25;\nfor (let i=0;i 장면을 다음 장면으로 바꿀 때 컬럼 멜트 전환을 넣어줘. 화면을 세로 30개 띠로 나누고 띠마다 0에서 0.25초 사이 고정 지연을 준 뒤, 각 띠가 0.75초 동안 power1.in으로 아래로 빠지게 해. 전체 길이는 1.0초, 아래 레이어에는 다음 장면이 이미 깔려 있어야 해. GSAP 타임라인 하나로 seek 가능하게 만들고 Math.random은 쓰지 마.", "codex": "<파일>의 장면 전환부에 컬럼 멜트를 구현해. 띠 30개, 지연 d(i)=((i*37)%30)/30*0.25, duration 0.75, ease power1.in, y는 1080px. 0.2초, 0.5초, 1.0초 시점을 캡처해 띠 높이가 서로 다르게 내려오는지, 1.0초에 이전 장면 픽셀이 남지 않았는지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/DoomScreenTransition.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "DoomScreenTransition", "Screen melt columns" ], "oneLinerEn": "A scene is cut into vertical strips that drop at different speeds, revealing the next scene like a melting screen.", "conveysEn": "The old screen collapses under its own weight. Rough, playful, and clearly game-like.", "useWhenEn": [ "When erasing a scene with a retro, game, or comedic tone", "When one cut in a short-form clip needs to stand out" ], "promptsEn": { "claude": "Apply a Column Melt transition to when switching to the next scene. Split the frame into 30 vertical strips, give each a fixed delay between 0 and 0.25s, and drop each by 1080px over 0.75s with power1.in. Total length 1.0s, with the next scene already sitting underneath. Use one seekable GSAP timeline and no Math.random.", "codex": "Implement Column Melt in the scene transition in . 30 strips, delay d(i)=((i*37)%30)/30*0.25, duration 0.75, ease power1.in, y 1080px. Capture at 0.2s, 0.5s, and 1.0s to confirm strips fall at visibly different heights and that no pixels of the old scene remain at 1.0s." }, "render": "planned", "files": {}, "no": "133" }, { "slug": "cube-transition", "family": "transitions", "ko": "큐브 전환", "en": "Cube Transition", "aka": [ "Cube rotation", "큐브 회전" ], "oneLiner": "두 장면이 큐브의 이웃 면처럼 원근을 가지며 회전해 교체되고 바닥 반사가 보인다", "conveys": "두 장면이 같은 3D 물체의 이웃 면처럼 이어진다. 공간이 회전한다는 감각을 준다", "purposes": [ "전환", "설명" ], "media": [ "설명 영상", "발표", "제품 시연" ], "level": "중급", "useWhen": [ "탭이나 섹션이 나란히 놓인 면이라는 구조를 보여 주며 다음 면으로 넘어갈 때", "제품 소개에서 기능 A 화면에서 기능 B 화면으로 회전해 넘어갈 때" ], "goodExample": "현재 장면이 왼쪽으로 90도 돌아 나가고 다음 장면이 오른쪽 면에서 들어오며, 0.8초 동안 원근 0.7로 바닥 반사가 살짝 보인다", "badExample": "원근이 너무 강해 화면이 찌그러지거나, 회전 축이 화면 중앙이 아니라 모서리로 어긋난다", "avoid": [ "회전은 90도 한 번으로 끝낸다(두 바퀴 이상은 어지러움)", "반사는 opacity 0.4 이하로 둔다" ], "params": [ { "name": "지속", "default": "800ms", "range": "600~1000ms", "note": "easeInOutCubic 근사" }, { "name": "원근", "default": "0.7", "range": "0.5~1.0", "note": "perspective 1400~2000px" }, { "name": "회전각", "default": "90deg", "range": "90deg 고정", "note": "이웃 면" }, { "name": "축소", "default": "0.3", "range": "0.2~0.4", "note": "회전 중 살짝 물러남" }, { "name": "반사 opacity", "default": "0.4", "range": "0.2~0.4", "note": "바닥 반사" } ], "ease": "power3.inOut", "snippet": "gsap.set('.stage', { perspective: 1600 });\ntl.fromTo('.next', { rotationY: 90, transformOrigin: '0% 50%' }, { rotationY: 0, duration: 0.8, ease: 'power3.inOut' }, 0);\ntl.to('.prev', { rotationY: -90, transformOrigin: '100% 50%', duration: 0.8, ease: 'power3.inOut' }, 0);", "pairsWith": [ "perspective-swap", "page-turn", "push-transition" ], "engines": { "hyperframes": "CSS 3D는 rotationY와 perspective만 쓰므로 seek 안전하다. 두 장면을 같은 stage 안에 겹쳐 두고 paused 타임라인에서 동시에 돌린다", "reelforge": "씬 워커 브리프에 회전각 90도, 원근 1600px, 지속 800ms를 넣고 두 장면 모두 backface-visibility를 hidden으로 요구한다", "scrolline": "scrub에서 회전각을 진행률 0~1에 선형 매핑하고 easeInOut 대신 ease-out을 적용해 끝에서 멈추는 느낌을 확실히 한다" }, "prompts": { "claude": "<대상> 두 장면 사이에 큐브 전환을 넣어줘. 부모에 perspective 1600px, 이전 장면은 오른쪽 축(100% 50%)으로 0에서 -90도, 다음 장면은 왼쪽 축(0% 50%)으로 90도에서 0으로 0.8초 동안 power3.inOut으로 돌려. backface-visibility는 hidden. 회전 중 살짝 축소해 원근이 보이게 하고, GSAP 타임라인 하나로 seek 가능하게 만들어.", "codex": "<파일>에 큐브 전환을 구현해. .stage perspective 1600, .prev rotationY 0에서 -90, .next 90에서 0, duration 0.8, ease power3.inOut, transformOrigin은 각각 100% 50%와 0% 50%. 0.2초, 0.4초, 0.6초, 0.8초 시점을 캡처해 두 면이 모서리에서 맞닿아 회전하는지, 틈이 벌어지지 않는지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/cube.glsl", "license": "MIT" }, { "title": "remotion-dev/remotion", "url": "https://www.remotion.dev/docs/transitions/presentations/cube", "license": "Remotion License" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "cube", "Cube rotation" ], "oneLinerEn": "Two scenes rotate in perspective like neighboring faces of a cube, with a hint of floor reflection.", "conveysEn": "Makes both scenes feel attached to one 3D object. Suggests space rotating.", "useWhenEn": [ "When showing tabs or sections as adjacent faces and moving to the next one", "When going from a feature A screen to a feature B screen in a product tour" ], "promptsEn": { "claude": "Add a Cube Transition between the two scenes in . Set perspective 1600px on the parent. Rotate the previous scene from 0 to -90deg around its right edge (100% 50%) and the next scene from 90deg to 0 around its left edge (0% 50%), over 0.8s with power3.inOut. Use backface-visibility hidden, and keep it on a single seekable GSAP timeline.", "codex": "Implement a cube transition in . .stage perspective 1600; .prev rotationY 0 to -90, .next 90 to 0, duration 0.8, ease power3.inOut, transformOrigin 100% 50% and 0% 50%. Capture at 0.2s, 0.4s, 0.6s, and 0.8s to confirm the two faces meet at the edge while rotating and no gap opens between them." }, "render": "planned", "files": {}, "no": "134" }, { "slug": "dip-to-color", "family": "transitions", "ko": "딥 투 컬러", "en": "Dip to Color", "aka": [ "Color dip", "색면 경유 전환", "Dip to color", "색면 경유 페이드", "Blur to solid color", "블러 색면 경유" ], "oneLiner": "앞 장면이 단색으로 사라진 뒤 같은 색에서 다음 장면이 나타나는 전환", "conveys": "문단이나 장의 경계. 한 호흡 쉬고 다음 이야기로 넘어간다는 신호", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "기본", "useWhen": [ "장이나 섹션이 바뀌는 지점을 분명히 나눌 때", "서로 어울리지 않는 두 장면을 색면 하나로 끊어 이을 때" ], "goodExample": "장면이 0.25초에 걸쳐 검은색으로 잠기고 0.1초 머문 뒤 다음 장면이 0.35초 동안 올라온다", "badExample": "색면 홀드를 0.5초 이상 두어 화면이 멈춘 것처럼 보이거나, 장면마다 다른 색을 써서 일관성이 없다", "avoid": [ "전환 전체 0.9초 초과 금지(리듬이 끊긴다)", "색은 영상 전체에서 1~2가지로 고정한다" ], "params": [ { "name": "퇴장", "default": "0.25s", "range": "0.15~0.4s", "note": "앞 장면이 색으로 잠기는 시간" }, { "name": "색면 홀드", "default": "0.1s", "range": "0~0.2s", "note": "색만 보이는 구간" }, { "name": "등장", "default": "0.35s", "range": "0.25~0.5s", "note": "퇴장보다 약간 길게" }, { "name": "색", "default": "#000", "range": "브랜드 색 가능", "note": "저채도 어두운 색이 안전" } ], "ease": "power2.out", "snippet": "tl.to('.a', { opacity: 0, duration: 0.25, ease: 'power1.in' })\n .to('.dip', { opacity: 1, duration: 0.25, ease: 'power1.in' }, 0)\n .set('.a', { display: 'none' }, 0.35)\n .to('.dip', { opacity: 0, duration: 0.35, ease: 'power2.out' }, 0.35)\n .fromTo('.b', { opacity: 0 }, { opacity: 1, duration: 0.35, ease: 'power2.out' }, 0.35);", "pairsWith": [ "crossfade", "flash-transition", "exit-before-enter" ], "engines": { "hyperframes": "색면 div를 두 씬 위에 z-index로 두고 opacity만 보간한다. 앞 장면의 display 전환도 타임라인에 넣어 seek에서 남지 않게 한다", "reelforge": "씬 경계 파라미터로 color, outMs, holdMs, inMs를 노출한다. 브랜드 색은 씬 토큰에서 받는다", "scrolline": "섹션 사이 구간을 진행률 0.4~0.6으로 잡고 가운데 0.1을 색 홀드로 쓴다. 스크롤을 멈추면 색 화면이 남으므로 홀드는 짧게 둔다" }, "prompts": { "claude": "<대상A>에서 <대상B>로 넘어갈 때 딥 투 컬러 전환을 GSAP로 넣어줘. A는 0.25초 동안 #000 색면 뒤로 사라지고, 색면이 0.1초 머문 뒤, B가 0.35초 동안 나타나게 해. 퇴장 이징 power1.in, 등장 이징 power2.out, paused 타임라인 하나로 만들어.", "codex": "<파일>의 섹션 경계에 dip-to-color를 넣어. 전면 #000 오버레이 opacity를 0.25초 동안 0에서 1, 0.1초 홀드, 0.35초 동안 1에서 0으로 보간하고 오버레이가 가장 진할 때 장면을 교체한다. 0.3초 시점에 화면이 순수 검정인지, 0.45초에 뒤 장면이 올라오는 중인지 캡처로 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/fade-through/registry-item.json", "license": "Apache-2.0" }, { "title": "pixel-point/animate-text", "url": "https://raw.githubusercontent.com/pixel-point/animate-text/master/skills/animate-text/assets/specs/fade-through.json", "license": "unknown" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/fadecolor.glsl", "license": "MIT" }, { "title": "FFmpeg/FFmpeg", "url": "https://ffmpeg.org/ffmpeg-filters.html#xfade", "license": "LGPL-2.1-or-later" }, { "title": "Adobe", "url": "https://helpx.adobe.com/premiere/desktop/add-video-effects/effects-and-transitions-library/list-of-video-dissolve-transitions.html", "license": "unknown" } ], "runtime": "css", "variants": [ "animate-text/fade-through", "fadecolor", "fadeblack", "fadewhite", "Dip To Black", "Dip To White", "Dip To Color Dissolve", "검정 경유", "흰색 경유", "브랜드색 경유", "블러 경유", "Color dip", "Blur to solid color" ], "oneLinerEn": "The outgoing scene fades into a flat color, then the next scene rises out of that same color.", "conveysEn": "A section or chapter break: a pause before the next idea.", "useWhenEn": [ "Mark clearly where a chapter or section changes.", "Cut between two mismatched scenes through a single color field." ], "promptsEn": { "claude": "Add a dip-to-color transition from to in GSAP. A fades out behind a #000 color layer over 0.25 seconds, the color holds for 0.1 seconds, then B fades in over 0.35 seconds. Use power1.in for the exit and power2.out for the entrance, all in one paused timeline.", "codex": "Add a dip-to-color at the section boundary in . Tween a full-frame #000 overlay from opacity 0 to 1 over 0.25 seconds, hold 0.1 seconds, then back to 0 over 0.35 seconds, swapping the scenes at the darkest point. Capture at 0.3 seconds to confirm the frame is pure black and at 0.45 seconds to confirm the next scene is rising in." }, "render": "planned", "files": {}, "no": "135" }, { "slug": "flash-transition", "family": "transitions", "ko": "플래시 전환", "en": "Flash Transition", "aka": [ "Overexposure flash", "과노출 플래시", "Flash Cut", "플래시 컷", "flash-frame-subliminal", "light-flash-join", "Exposure flash" ], "oneLiner": "흰색 섬광이 화면을 덮는 순간 장면이 바뀌고 밝기가 돌아오는 전환", "conveys": "충격과 강한 박자. 비트가 떨어지는 순간의 컷을 눈에 박아 준다", "purposes": [ "전환", "주목 끌기" ], "media": [ "숏폼", "제품 시연", "설명 영상" ], "level": "기본", "useWhen": [ "음악 비트나 강조 문장에 맞춰 장면을 바꿀 때", "제품 공개나 타이틀 직전에 시선을 모을 때" ], "goodExample": "비트 시점에 0.1초로 흰색이 100%까지 올라 1~2프레임 유지되고, 그 사이 장면이 바뀐 뒤 0.25초 동안 걷힌다", "badExample": "섬광을 0.5초 이상 끌거나, 한 영상에서 5번 넘게 써서 눈이 피로하고 효과가 무뎌진다", "avoid": [ "섬광 유지 3프레임 초과 금지", "영상 하나에 3회 이하로 쓴다(광과민 안전)" ], "params": [ { "name": "상승", "default": "0.1s", "range": "0.05~0.15s", "note": "in은 빠르게" }, { "name": "정점 유지", "default": "2 프레임", "range": "1~3프레임", "note": "이 사이 장면 교체" }, { "name": "복구", "default": "0.25s", "range": "0.15~0.4s", "note": "out은 조금 길게" }, { "name": "색", "default": "#fff", "range": "#fff~브랜드 밝은 색", "note": "순백은 가장 세다" } ], "ease": "power2.out", "snippet": "tl.to('.flash', { opacity: 1, duration: 0.1, ease: 'power2.in' })\n .set('.a', { display: 'none' })\n .set('.b', { display: 'block' })\n .to('.flash', { opacity: 0, duration: 0.25, ease: 'power2.out' }, '+=0.067');", "pairsWith": [ "strobe-flash", "zoom-flash", "crossfade" ], "engines": { "hyperframes": "전면 오버레이 opacity만 움직이고 장면 교체를 set으로 정점에 둔다. set이 seek 시 되돌려지도록 타임라인에 반드시 넣는다", "reelforge": "씬 워커 브리프에 beatSec, riseMs, holdFrames, decayMs를 실어 비트 시각에 정렬한다", "scrolline": "진행률 0.48~0.52 구간에서만 오버레이를 켠다. 스크롤을 정지했을 때 흰 화면이 남지 않도록 정점 구간을 아주 좁게 둔다" }, "prompts": { "claude": "<대상A>에서 <대상B>로 넘어가는 지점에 플래시 전환을 넣어줘. 전면 #fff 오버레이가 0.1초 동안 opacity 1까지 올라가고 2프레임(30fps 기준 0.067초) 유지되는 사이 A를 숨기고 B를 보이게 한 뒤, 0.25초 동안 0으로 내려가게 해. 이징은 올릴 때 power2.in, 내릴 때 power2.out이고 paused 타임라인 하나로 만들어.", "codex": "<파일>의 컷 지점에 flash transition을 적용해. #fff 오버레이 opacity 0에서 1 (0.1s), 0.067초 유지, 1에서 0 (0.25s)으로 두고 정점에서 장면을 교체한다. 0.1초 시점 캡처가 순백인지, 0.2초에 뒤 장면이 이미 보이는지, 0.45초에 오버레이가 사라졌는지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/flash-through-white/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/editorial-flash-overlay/registry-item.json", "license": "Apache-2.0" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/Overexposure.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/transitions/css-light.md", "license": "unknown" } ], "runtime": "gsap", "variants": [ "1프레임 플래시", "컬러 플래시", "화이트 조인", "pairs/light-flash-in", "pairs/light-flash-out", "Overexposure", "Overexposure flash", "Flash Cut", "Exposure flash" ], "oneLinerEn": "A white flash covers the frame, the scene changes at the peak, and brightness recovers.", "conveysEn": "Impact and a hard beat: the cut on the downbeat is burned into the eye.", "useWhenEn": [ "Change scenes on a music beat or emphasized line.", "Gather attention just before a product reveal or title." ], "promptsEn": { "claude": "Add a flash transition between and . A full-frame #fff overlay rises to opacity 1 over 0.1 seconds and holds for 2 frames (0.067s at 30fps), during which A is hidden and B shown, then falls to 0 over 0.25 seconds. Use power2.in going up, power2.out coming down, in one paused timeline.", "codex": "Apply a flash transition at the cut in . Tween a #fff overlay from opacity 0 to 1 (0.1s), hold 0.067s, then 1 to 0 (0.25s), swapping scenes at the peak. Capture at 0.1 seconds to confirm pure white, at 0.2 seconds to confirm the new scene is already visible, and at 0.45 seconds to confirm the overlay is gone." }, "render": "done", "files": { "html": "effects/flash-transition/index.html", "mp4": "effects/flash-transition/clip.mp4", "gif": "effects/flash-transition/preview.gif", "poster": "effects/flash-transition/poster.jpg" }, "no": "136" }, { "slug": "halftone-dissolve", "family": "transitions", "ko": "하프톤 디졸브", "en": "Halftone Dissolve", "aka": [ "Halftone dissolve", "하프톤 전환", "Polka dot curtain", "물방울 점 커튼" ], "oneLiner": "격자의 원형 구멍이 커지고 이어져 다음 장면을 드러내는 전환", "conveys": "인쇄물과 점묘의 질감. 레트로하고 그래픽적인 교체", "purposes": [ "전환", "분위기" ], "media": [ "숏폼", "설명 영상", "발표" ], "level": "중급", "useWhen": [ "레트로, 인쇄, 팝아트 톤의 영상에서 장면을 바꿀 때", "시작점에서 방사형으로 퍼지는 공개가 필요할 때" ], "goodExample": "16px 격자의 점이 시작점에서 가까운 것부터 셀당 15ms씩 늦게 커져 반경이 0에서 12px가 되고 0.9초 안에 화면을 채운다", "badExample": "격자가 32px 이상으로 커서 점이 도트 패턴이 아니라 큰 원처럼 보이거나, 점이 모두 동시에 커져 물결 없이 균일하다", "avoid": [ "격자는 8~24px로 유지한다", "최대 반경은 격자 대각선의 절반(약 0.71배)까지 키워야 빈틈이 메워진다" ], "params": [ { "name": "전체 지속", "default": "0.9s", "range": "0.6~1.4s", "note": "마지막 셀 완료 기준" }, { "name": "격자", "default": "16px", "range": "8~24px", "note": "셀 한 변" }, { "name": "최대 반경", "default": "12px", "range": "격자의 0.6~0.75배", "note": "0.71배 이상이면 완전 채움" }, { "name": "셀 지연", "default": "15ms", "range": "5~30ms", "note": "시작점 거리 순" } ], "ease": "power2.inOut", "snippet": "const cells = gsap.utils.toArray('.dot');\ncells.forEach((c, i) => {\n const d = Math.hypot(c.dataset.x - 960, c.dataset.y - 540) / 1102;\n tl.fromTo(c, { attr: { r: 0 } }, { attr: { r: 12 }, duration: 0.5, ease: 'power2.inOut' }, d * 0.4);\n});", "pairsWith": [ "mosaic-reveal", "noise-dissolve", "depixelate-reveal" ], "engines": { "hyperframes": "SVG 마스크 안에 circle을 격자로 만들고 r만 타임라인에서 보간한다. 셀 수가 많으면 canvas가 가볍다", "reelforge": "씬 워커 브리프에 cellPx, maxRadius, originXY, delayMs를 싣는다", "scrolline": "진행률 p를 셀 거리 순서 임계값에 매핑해 각 셀 반경 = clamp((p - d)/0.5)*12로 계산한다. 이징은 ease-out" }, "prompts": { "claude": "<대상A>에서 <대상B>로 하프톤 디졸브를 만들어줘. 16px 격자로 원 마스크를 깔고 시작점(50% 50%)에서 가까운 셀부터 반경이 0에서 12px로 커지게 해. 셀 사이 지연은 15ms, 셀당 지속 0.5초, 전체 0.9초 안에 끝나게 하고 이징은 power2.inOut. 원 위치는 코드로 결정론적으로 계산하고 paused 타임라인 하나로 만들어.", "codex": "<파일>에 halftone dissolve를 넣어. 16px 격자 circle 마스크의 r을 0에서 12px로 0.5s power2.inOut으로 키우고 시작 시각은 중심 거리 순으로 0~0.4s 사이에 분산한다. 0.3초, 0.6초, 0.9초를 캡처해 점이 커지며 이어지는지, 0.9초에 뒤 장면 전체가 채워졌는지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/halftone-dissolve/registry-item.json", "license": "Apache-2.0" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/PolkaDotsCurtain.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "canvas", "variants": [ "PolkaDotsCurtain", "동시 확장", "거리 순 확장", "Polka dot curtain" ], "oneLinerEn": "Circular holes on a grid grow and merge to reveal the next scene.", "conveysEn": "A print and stipple texture: a retro, graphic swap.", "useWhenEn": [ "Change scenes in a retro, print, or pop-art styled video.", "Reveal radially from a starting point." ], "promptsEn": { "claude": "Build a halftone dissolve from to . Lay a 16px grid of circle masks and grow each radius from 0 to 12px, starting with the cells nearest the origin (50% 50%). Use a 15ms delay per cell, 0.5 seconds per cell, 0.9 seconds total, and power2.inOut. Compute circle positions deterministically in code and use one paused timeline.", "codex": "Add a halftone dissolve to . Grow the r of a 16px grid of circle masks from 0 to 12px over 0.5s with power2.inOut, spreading start times across 0 to 0.4s by distance from the center. Capture at 0.3, 0.6, and 0.9 seconds to confirm dots grow and merge, and that the new scene is fully filled at 0.9 seconds." }, "render": "planned", "files": {}, "no": "137" }, { "slug": "page-turn", "family": "transitions", "ko": "페이지 턴", "en": "Page Turn", "aka": [ "Book page turn", "책 페이지 넘기기", "bookFlip()", "Page curl", "페이지 말림", "CC Page Turn" ], "oneLiner": "페이지 한쪽이나 모서리가 접히고 말려 올라가 뒷면과 그림자를 드러내며 다음 화면으로 넘어간다", "conveys": "책장을 넘기듯 다음 이야기로 이어진다는 감각. 순서와 진행을 부드럽게 알린다", "purposes": [ "전환", "순서·흐름" ], "media": [ "설명 영상", "발표", "스크롤덱" ], "level": "고급", "useWhen": [ "튜토리얼 단계나 이야기 장을 책 넘기듯 나눌 때", "전자책, 매뉴얼, 앨범처럼 종이 매체를 소재로 한 영상에서 화면을 바꿀 때" ], "goodExample": "오른쪽 모서리가 말려 올라가며 0.9초 동안 뒷면이 보이고, 말림 반경은 화면 폭의 16%, 그림자 opacity 0.25가 말림을 따라간다", "badExample": "말림이 너무 작아 잘 안 보이거나, 뒷면과 그림자 없이 앞면만 접혀 평면 회전으로 보인다", "avoid": [ "말림 반경은 화면 폭의 10~20% 안에 둔다", "한 영상에서 두 번 넘게 반복하지 않는다(장식으로 읽힘)" ], "params": [ { "name": "지속", "default": "900ms", "range": "700~1200ms", "note": "power2.inOut" }, { "name": "말림 반경", "default": "화면 폭 16%", "range": "10~20%", "note": "클수록 종이가 두꺼워 보임" }, { "name": "그림자 opacity", "default": "0.25", "range": "0.15~0.35", "note": "말림 뒤쪽에 드리움" }, { "name": "방향", "default": "오른쪽에서 왼쪽", "range": "좌우 또는 상하", "note": "읽는 방향과 맞춘다" }, { "name": "뒷면 밝기", "default": "0.9", "range": "0.8~1.0", "note": "뒷면은 약간 어둡게" } ], "ease": "power2.inOut", "snippet": "// 2D 근사: 앞면 클립을 접는 각도로 진행\nconst t = { p: 0 };\ntl.to(t, { p: 1, duration: 0.9, ease: 'power2.inOut',\n onUpdate: () => drawCurl(t.p) }, 0); // 반경 0.16*1920, 그림자 0.25\ntl.set('.prev', { autoAlpha: 0 }, 0.9);", "pairsWith": [ "cube-transition", "origami-fold", "wipe" ], "engines": { "hyperframes": "말림은 진행률 p 하나로 그리는 함수 drawCurl(p)로 짜고, onUpdate가 아니라 타임라인 시각으로 계산해 seek 시 같은 프레임이 나오게 한다. WebGL이면 uniform 하나만 움직인다", "reelforge": "씬 워커 브리프에 방향, 반경 16%, 그림자 0.25, 지속 900ms를 싣는다. 텍스처는 앞뒤 장면을 미리 렌더한 두 장을 넘긴다", "scrolline": "scrub에서 p를 스크롤 진행률에 직접 매핑하면 사용자가 페이지를 손으로 넘기는 느낌이 나서 잘 맞는다. 스프링 없이 선형에 가까운 ease-out으로 둔다" }, "prompts": { "claude": "<대상> 장면 끝에 페이지 턴 전환을 넣어줘. 오른쪽에서 왼쪽으로 900ms, power2.inOut으로 진행하고 말림 반경은 화면 폭의 16%, 말림 뒤에 opacity 0.25 그림자를 깔아줘. 뒷면은 앞면보다 10% 어둡게. 진행률 p 하나로 그리는 함수로 만들어 paused 타임라인에서 seek해도 같은 프레임이 나오게 해.", "codex": "<파일>에 페이지 턴 전환을 구현해. p를 0에서 1로 0.9초 power2.inOut, drawCurl(p)에서 반경 0.16*1920px, 그림자 0.25. 0.3초, 0.45초, 0.6초, 0.9초 시점을 캡처해 말림이 점점 커지는지, 뒷면이 보이는지, 0.9초에 이전 페이지가 완전히 사라졌는지 확인해. 난수와 setTimeout은 쓰지 마." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/BookFlip.glsl", "license": "MIT" }, { "title": "remotion-dev/remotion", "url": "https://www.remotion.dev/docs/transitions/presentations/book-flip", "license": "Remotion License" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/InvertedPageCurl.glsl", "license": "BSD-3-Clause" }, { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/apply-effects-and-animation-presets/list-of-effects/transition-effects.html", "license": "unknown" } ], "runtime": "webgl", "variants": [ "BookFlip", "InvertedPageCurl", "모서리 말기", "가장자리 넘기기", "책장 넘기기", "모서리 컬", "좌우", "상하", "Book page turn", "Page curl", "CC Page Turn" ], "oneLinerEn": "A page corner curls up, exposing the back and a soft shadow, and turns to the next screen.", "conveysEn": "Evokes turning to the next page of a story. Signals order and progress gently.", "useWhenEn": [ "When splitting tutorial steps or story chapters like turning pages", "When the subject is paper media such as ebooks, manuals, or albums" ], "promptsEn": { "claude": "Add a Page Turn transition at the end of . Run it right to left over 900ms with power2.inOut, curl radius 16% of frame width, and a 0.25 opacity shadow behind the curl. Make the back side 10% darker. Drive it with one progress value p and a drawCurl(p) function so seeking a paused timeline yields identical frames.", "codex": "Implement Page Turn in . Tween p from 0 to 1 over 0.9s with power2.inOut; drawCurl(p) uses radius 0.16*1920px and shadow 0.25. Capture at 0.3s, 0.45s, 0.6s, and 0.9s to confirm the curl grows, the back side shows, and the previous page is fully gone at 0.9s. No randomness or setTimeout." }, "render": "done", "files": { "html": "effects/page-turn/index.html", "mp4": "effects/page-turn/clip.mp4", "gif": "effects/page-turn/preview.gif", "poster": "effects/page-turn/poster.jpg" }, "no": "138" }, { "slug": "perspective-swap", "family": "transitions", "ko": "원근 카드 스왑", "en": "Perspective Swap", "aka": [ "Perspective swap", "원근 자리 교환" ], "oneLiner": "두 장면이 원근을 가진 카드처럼 좌우와 앞뒤 자리를 바꾸고 반사가 따라간다", "conveys": "두 장면이 앞뒤로 자리를 바꾼다. 주도권이 넘어가는 순간을 시각화한다", "purposes": [ "전환", "비교" ], "media": [ "설명 영상", "발표", "제품 시연" ], "level": "중급", "useWhen": [ "이전 버전과 새 버전처럼 두 화면의 주인공이 바뀌는 순간을 보여 줄 때", "두 카드가 앞뒤 자리를 교환하며 순위나 우선순위가 바뀐다고 알릴 때" ], "goodExample": "앞 카드가 오른쪽 뒤로 물러나며 작아지고 뒤 카드가 왼쪽에서 앞으로 나오며, 0.8초 동안 서로 스치지 않고 원을 그리듯 자리를 바꾼다", "badExample": "두 카드가 같은 깊이에서 겹쳐 지나가 z 순서가 튀거나, 크기 변화 없이 좌우로만 바뀐다", "avoid": [ "z 순서는 중간 지점(50%)에서 한 번만 바꾼다", "카드가 3개 이상이면 스택 셔플을 쓴다" ], "params": [ { "name": "지속", "default": "800ms", "range": "600~1000ms", "note": "easeInOutCubic 근사" }, { "name": "깊이 차이", "default": "z 300px", "range": "200~400px", "note": "뒤로 물러나는 양" }, { "name": "축소", "default": "0.82", "range": "0.75~0.9", "note": "뒤 카드 크기" }, { "name": "좌우 이동", "default": "±420px", "range": "300~500px", "note": "1920px 기준" }, { "name": "z 순서 교체", "default": "진행 50%", "range": "40~60%", "note": "한 번만" } ], "ease": "power3.inOut", "snippet": "gsap.set('.stage', { perspective: 1800 });\ntl.to('.front', { x: 420, z: -300, scale: 0.82, duration: 0.8, ease: 'power3.inOut' }, 0);\ntl.fromTo('.back', { x: -420, z: -300, scale: 0.82 }, { x: 0, z: 0, scale: 1, duration: 0.8, ease: 'power3.inOut' }, 0);\ntl.set('.back', { zIndex: 2 }, 0.4).set('.front', { zIndex: 1 }, 0.4);", "pairsWith": [ "cube-transition", "card-stack-shuffle", "split-compare" ], "engines": { "hyperframes": "x, z, scale만 쓰고 zIndex 교체는 set으로 0.4초에 한 번. paused 타임라인에서 seek해도 순서가 바르게 나온다", "reelforge": "씬 워커 브리프에 좌우 이동 420px, 깊이 300px, 교체 시점 50%를 싣고 두 카드를 같은 stage에 넣도록 한다", "scrolline": "scrub에서는 zIndex 교체 임계값을 진행률 0.5로 고정하고 스프링 대신 ease-out을 쓴다. 역방향 스크롤에서도 같은 값으로 돌아온다" }, "prompts": { "claude": "<대상> 두 카드(.front, .back)가 자리를 바꾸는 원근 스왑 전환을 넣어줘. 부모 perspective 1800px, 0.8초 power3.inOut. .front는 x +420, z -300, scale 0.82로 물러나고 .back은 x -420에서 0으로 앞으로 나와. zIndex는 0.4초에 한 번만 교체하고, 타임라인 하나로 seek 가능하게 해.", "codex": "<파일>에 원근 스왑을 구현해. perspective 1800, .front를 x 420, z -300, scale 0.82로, .back을 x -420에서 0, z -300에서 0으로, duration 0.8, ease power3.inOut. zIndex는 0.4초에 set. 0.2초, 0.4초, 0.6초, 0.8초 시점을 캡처해 두 카드가 서로 관통하지 않는지, 0.4초 전후로 앞뒤가 바뀌는지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/swap.glsl", "license": "MIT" }, { "title": "remotion-dev/remotion", "url": "https://www.remotion.dev/docs/transitions/presentations/swap", "license": "Remotion License" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "swap" ], "oneLinerEn": "Two scenes swap left-right and front-back like perspective cards, with reflections following.", "conveysEn": "Signals a change in who leads. Makes before-and-after or priority handoffs readable.", "useWhenEn": [ "When showing the moment two screens trade roles, such as old and new versions", "When two cards trade places to indicate a rank or priority change" ], "promptsEn": { "claude": "Add a Perspective Swap between the cards in . Set perspective 1800px on the stage, run 0.8s with power3.inOut. .front moves to x +420, z -300, scale 0.82; .back travels from x -420 to 0 and comes forward. Swap zIndex exactly once at 0.4s. Keep everything on one seekable GSAP timeline.", "codex": "Implement Perspective Swap in . perspective 1800; .front to x 420, z -300, scale 0.82; .back from x -420, z -300 to x 0, z 0; duration 0.8, ease power3.inOut; set zIndex at 0.4s. Capture at 0.2s, 0.4s, 0.6s, and 0.8s to confirm the cards never interpenetrate and that front and back swap around 0.4s." }, "render": "planned", "files": {}, "no": "139" }, { "slug": "ripple-dissolve", "family": "transitions", "ko": "리플 디졸브", "en": "Ripple Dissolve", "aka": [ "Ripple distortion", "물결 왜곡", "Ripple dissolve", "물결 디졸브", "Water drop reveal", "물방울 파문 공개" ], "oneLiner": "한 지점에서 퍼지는 동심 파문이 화면을 휘게 하며 다음 장면을 드러내는 전환", "conveys": "물 위에 돌이 떨어진 듯한 유기적 흔들림. 장면이 액체처럼 교체된다", "purposes": [ "전환", "분위기" ], "media": [ "설명 영상", "숏폼", "웹 UI" ], "level": "고급", "useWhen": [ "장면 전환에 물성과 부드러움을 주고 싶을 때", "클릭이나 터치 지점에서 화면이 바뀌는 UI 연출에서" ], "goodExample": "화면 중심에서 시작한 파문이 0.9초 동안 퍼지며 최대 30px 변위로 화면을 흔들고, 파문이 지나간 자리에 뒤 장면이 드러난다", "badExample": "파동 주파수가 높아 화면이 유리 깨진 듯 지글거리거나, 변위가 화면 가장자리까지 그대로 남는다", "avoid": [ "변위는 파문 반경이 커질수록 감쇠시킨다(가장자리에서 0)", "GPU 셰이더가 없는 환경에서는 clipPath 원 확장으로 폴백한다" ], "params": [ { "name": "전체 지속", "default": "0.9s", "range": "0.6~1.4s", "note": "파문이 화면 끝까지" }, { "name": "최대 변위", "default": "30px", "range": "10~40px", "note": "1920x1080 기준" }, { "name": "파동 수", "default": "3", "range": "2~5", "note": "반경당 개수" }, { "name": "중심", "default": "50% 50%", "range": "클릭 좌표", "note": "UI에서는 커서 위치" } ], "ease": "power2.out", "snippet": "const u = { p: 0 };\ntl.to(u, { p: 1, duration: 0.9, ease: 'power2.out', onUpdate: () => {\n const r = u.p * 1.2, amp = 30 * (1 - u.p);\n ripple.setAttribute('scale', amp); mask.setAttribute('r', r * 960);\n} });", "pairsWith": [ "ripple-distortion", "wave-dissolve", "iris-mask" ], "engines": { "hyperframes": "feDisplacementMap의 scale과 원 반경을 progress 객체 하나에서 계산한다. WebGL이면 uTime을 타임라인 값으로만 전달해 seek을 보장한다", "reelforge": "씬 워커 브리프에 centerX, centerY, amplitudePx, waves를 파라미터로 싣고 셰이더 캔버스는 씬 안에서만 만든다", "scrolline": "진행률 p를 반경과 진폭 감쇠에 직접 매핑한다. 감쇠식은 ease-out이며 스프링을 쓰지 않는다" }, "prompts": { "claude": "<대상A>에서 <대상B>로 리플 디졸브 전환을 만들어줘. 중심 50% 50%에서 파문이 0.9초 동안 퍼지고, 변위 진폭은 30px에서 0으로 감쇠하며, 파문 반경 안쪽에 B가 드러나게 해. 이징 power2.out, 진행값은 GSAP 상태 객체 하나로 두고 paused 타임라인에서 seek되게 해.", "codex": "<파일>에 ripple dissolve를 적용해. progress 0에서 1을 0.9초 power2.out으로 보간하고, 변위 진폭은 30*(1-p) px, 마스크 반경은 progress에 비례해 화면 대각선 이상까지 키운다. 0.3초, 0.6초, 0.9초 시점을 캡처해 파문 고리가 퍼지는지, 0.9초에 왜곡이 0인지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/ripple-waves/registry-item.json", "license": "Apache-2.0" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/ripple.glsl", "license": "MIT" }, { "title": "remotion-dev/remotion", "url": "https://www.remotion.dev/docs/transitions/presentations/ripple", "license": "Remotion License" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/WaterDrop.glsl", "license": "MIT" } ], "runtime": "webgl", "variants": [ "ripple", "WaterDrop", "워터 드롭", "전면 파문 혼합", "Ripple distortion", "Water drop reveal" ], "oneLinerEn": "Concentric ripples spread from one point, bending the frame while revealing the next scene.", "conveysEn": "An organic wobble like a stone dropped in water: the scene changes as if it were liquid.", "useWhenEn": [ "Give a scene change a soft, physical quality.", "Trigger the change from a click or touch point in UI demos." ], "promptsEn": { "claude": "Build a ripple dissolve from to . Ripples spread from the center (50% 50%) over 0.9 seconds, displacement amplitude decays from 30px to 0, and B is revealed inside the ripple radius. Use power2.out, drive it with a single GSAP state object, and keep it in one paused timeline that seeks.", "codex": "Apply a ripple dissolve in . Tween progress 0 to 1 over 0.9 seconds with power2.out, amplitude 30*(1-p) px, mask radius growing with progress beyond the frame diagonal. Capture at 0.3, 0.6, and 0.9 seconds to confirm the ring expands and that distortion is 0 at 0.9 seconds." }, "render": "planned", "files": {}, "no": "140" }, { "slug": "rotating-tile-dissolve", "family": "transitions", "ko": "회전 타일 디졸브", "en": "Rotating Tile Dissolve", "aka": [ "Rotating tiled dissolve", "회전 반복 타일 전환" ], "oneLiner": "반복된 영상 타일이 원점 주변으로 회전하며 다음 장면으로 섞인다", "conveys": "반복 패턴이 원점을 중심으로 돌며 다음 화면에 섞인다. 리듬 있는 디지털 전환이다", "purposes": [ "전환", "분위기" ], "media": [ "숏폼", "설명 영상" ], "level": "고급", "useWhen": [ "패턴이나 타일이 반복되는 그래픽 톤의 영상에서 장면을 섞어 넘길 때", "음악 비트에 맞춰 화면이 돌며 바뀌는 숏폼 컷에 쓴다" ], "goodExample": "화면이 8x8 타일로 반복되며 0.7초 동안 90도 회전하고, 절반쯤에서 다음 장면 타일이 교차한다", "badExample": "타일이 너무 작아 노이즈로 보이거나, 회전 후 화면 가장자리가 비어 검은 틈이 보인다", "avoid": [ "타일 경계가 원본 콘텐츠 글자를 가로지르지 않게 정보 장면 위에는 쓰지 않는다", "회전각은 90도의 배수로 둔다" ], "params": [ { "name": "지속", "default": "700ms", "range": "500~900ms", "note": "linear" }, { "name": "회전각", "default": "90deg", "range": "90 또는 180deg", "note": "원점 기준" }, { "name": "타일 반복", "default": "2x2", "range": "2~4", "note": "UV 반복" }, { "name": "교차 구간", "default": "진행 35~65%", "range": "30~70%", "note": "한 구간에서만 섞임" }, { "name": "확대", "default": "1.25", "range": "1.2~1.5", "note": "빈틈 방지" } ], "ease": "none", "snippet": "const s = { p: 0 };\ntl.to(s, { p: 1, duration: 0.7, ease: 'none', onUpdate: () => {\n gsap.set('.tiles', { rotation: 90 * s.p, scale: 1 + 0.25 * Math.sin(Math.PI * s.p) });\n gsap.set('.next', { opacity: gsap.utils.clamp(0, 1, (s.p - 0.35) / 0.3) });\n} }, 0);", "pairsWith": [ "zoom-through", "kaleidoscope-transition", "crossfade" ], "engines": { "hyperframes": "타일 반복은 CSS background-repeat 또는 복제 요소로 만들고 회전과 scale만 timeline에서 보간한다. onUpdate 대신 타임라인 값에서 결정론적으로 계산한다", "reelforge": "씬 워커 브리프에 회전각, 타일 반복 수, 교차 구간을 싣고 두 장면 모두 타일 소스로 쓸 수 있게 렌더하도록 한다", "scrolline": "scrub에서는 회전각을 진행률에 선형으로 묶고 교차 구간(0.35~0.65)만 opacity로 처리한다. 스프링은 쓰지 않는다" }, "prompts": { "claude": "<대상> 장면 전환에 회전 타일 디졸브를 넣어줘. 화면을 2x2 반복 타일로 만들고 0.7초 동안 linear로 90도 회전시키면서 scale을 1에서 1.25까지 갔다 돌아오게 해. 진행 35%에서 65% 사이에 다음 장면 opacity를 0에서 1로 올려. 빈 가장자리가 보이지 않는지 확인하고, GSAP 타임라인 하나로 seek 가능하게 해.", "codex": "<파일>에 회전 타일 디졸브를 구현해. p 0에서 1, 0.7초 linear, .tiles rotation 90*p, scale 1+0.25*sin(pi*p), .next opacity clamp((p-0.35)/0.3). 0.2초, 0.35초, 0.5초, 0.7초 시점을 캡처해 회전 중 가장자리에 빈 틈이 없는지, 0.7초에 다음 장면만 남는지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/rotateTransition.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "rotateTransition", "Rotating tiled dissolve" ], "oneLinerEn": "Tiled copies of the frame rotate around the origin while the next scene blends in.", "conveysEn": "Adds rotation and a flowing pattern that crosses the frame edge.", "useWhenEn": [ "When blending scenes in a pattern-driven, graphic-heavy video", "On beat-driven short-form cuts where the frame spins into the next shot" ], "promptsEn": { "claude": "Add a Rotating Tile Dissolve to . Repeat the frame as 2x2 tiles, rotate it 90deg over 0.7s with linear ease while scale goes 1 to 1.25 and back. Fade the next scene in from 0 to 1 between 35% and 65% of progress. Make sure no empty edges show and keep it on one seekable GSAP timeline.", "codex": "Implement Rotating Tile Dissolve in . Progress p 0 to 1 over 0.7s linear; .tiles rotation 90*p, scale 1+0.25*sin(pi*p); .next opacity clamp((p-0.35)/0.3). Capture at 0.2s, 0.35s, 0.5s, and 0.7s to confirm there are no gaps at the edges during rotation and only the next scene remains at 0.7s." }, "render": "planned", "files": {}, "no": "141" }, { "slug": "scale-swap", "family": "transitions", "ko": "스케일 스왑", "en": "Scale Swap", "aka": [ "Scale swap", "축소 확대 교체", "축소와 팝 교체", "scale-swap-transition", "Anchored scale replacement", "앵커 확대 축소 교체" ], "oneLiner": "기존 장면이 기준점으로 줄어드는 동안 새 장면이 커져 그 자리를 잇는 전환", "conveys": "같은 자리에서 상태가 바뀌어도 이어진다는 감각. 튀어나오는 팝 느낌", "purposes": [ "전환", "강조" ], "media": [ "웹 UI", "숏폼", "제품 시연" ], "level": "기본", "useWhen": [ "아이콘, 카드, 버튼 같은 요소가 다른 상태로 바뀔 때", "한 장면이 다른 장면으로 같은 위치에서 교체될 때" ], "goodExample": "앞 요소가 0.55초 동안 scale 1에서 0.2로 줄며 사라지고, 뒤 요소가 0.2에서 1로 커지며 50% 시점에 교차한다", "badExample": "앞 요소가 0으로 완전히 줄어 빈 구간이 생기거나, 오버슈트를 1.3배 넘게 줘서 통통 튀는 느낌이 과하다", "avoid": [ "scale 0까지 줄이지 않는다(0.2 아래 금지)", "오버슈트를 쓰려면 scale 1.05 이하로 제한한다" ], "params": [ { "name": "전체 지속", "default": "0.55s", "range": "0.35~0.8s", "note": "교차는 50%" }, { "name": "원점", "default": "50% 50%", "range": "요소 중심 또는 클릭점", "note": "두 요소 동일" }, { "name": "앞 scale", "default": "1→0.2", "range": "0.1~0.5", "note": "opacity 1→0 동반" }, { "name": "뒤 scale", "default": "0.2→1", "range": "0.2~0.6에서 시작", "note": "opacity 0→1 동반" } ], "ease": "back.out(1.4)", "snippet": "tl.to('.a', { scale: 0.2, opacity: 0, duration: 0.55, ease: 'power2.in', transformOrigin: '50% 50%' }, 0)\n .fromTo('.b', { scale: 0.2, opacity: 0, transformOrigin: '50% 50%' },\n { scale: 1, opacity: 1, duration: 0.55, ease: 'back.out(1.4)' }, 0.275);", "pairsWith": [ "scale-pop", "crossfade", "squeeze-transition" ], "engines": { "hyperframes": "형제 요소를 같은 위치에 겹치고 transform, opacity만 쓴다. back.out은 seek에서도 결정론적이다", "reelforge": "씬 워커 브리프에 originX, originY, fromScale, overshoot를 싣는다. 오브젝트 씬의 상태 교체 비트에 붙인다", "scrolline": "scrub에서는 back.out 대신 power3.out으로 바꿔 오버슈트 진동을 없앤다. 진행률 0.5에서 교차한다" }, "prompts": { "claude": "<대상A>를 <대상B>로 스케일 스왑해줘. 둘은 같은 위치에 겹쳐 두고 transformOrigin 50% 50%. A는 0.55초 동안 scale 1에서 0.2, opacity 1에서 0 (power2.in), B는 0.275초부터 0.55초 동안 scale 0.2에서 1, opacity 0에서 1 (back.out(1.4)). paused 타임라인으로 만들어.", "codex": "<파일>의 상태 교체에 scale swap을 적용해. 앞 요소 scale 1에서 0.2와 opacity 0 (0.55s, power2.in), 뒤 요소 scale 0.2에서 1과 opacity 1 (0.275초 시점부터 0.55s, back.out(1.4)). 0.3초에 두 요소가 함께 보이는지, 0.83초에 뒤 요소가 scale 1인지 캡처로 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/morph-swap/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/icon-swap/registry-item.json", "license": "Apache-2.0" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/Slides.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/rules/scale-swap-transition.md", "license": "unknown" } ], "runtime": "gsap", "variants": [ "아이콘 교체", "브랜드 팝", "Slides", "중심", "모서리", "변 기준", "Anchored scale replacement" ], "oneLinerEn": "The old element shrinks toward a pivot while the new one grows in to take its place.", "conveysEn": "Different states continue in the same spot, with a small pop.", "useWhenEn": [ "Change an icon, card, or button into another state.", "Swap one scene for another in the same position." ], "promptsEn": { "claude": "Scale-swap for . Stack them in the same spot with transformOrigin 50% 50%. A goes scale 1 to 0.2 and opacity 1 to 0 over 0.55 seconds (power2.in). B starts at 0.275 seconds and goes scale 0.2 to 1 and opacity 0 to 1 over 0.55 seconds (back.out(1.4)). Use one paused timeline.", "codex": "Apply a scale swap to the state change in . Outgoing element scale 1 to 0.2 with opacity to 0 (0.55s, power2.in); incoming element scale 0.2 to 1 with opacity to 1 (starts at 0.275s, 0.55s, back.out(1.4)). Capture at 0.3 seconds to confirm both are visible together, and at 0.83 seconds to confirm the incoming element is at scale 1." }, "render": "done", "files": { "html": "effects/scale-swap/index.html", "mp4": "effects/scale-swap/clip.mp4", "gif": "effects/scale-swap/preview.gif", "poster": "effects/scale-swap/poster.jpg" }, "no": "142" }, { "slug": "split-slide", "family": "transitions", "ko": "스플릿 슬라이드", "en": "Split Slide", "aka": [ "Split slide", "반쪽 슬라이드" ], "oneLiner": "두 반쪽 장면이 서로 반대쪽으로 벌어지거나 모여 다음 장면과 교체된다", "conveys": "화면이 문처럼 반으로 열리거나 닫힌다. 좌우 대칭의 정돈된 교체를 만든다", "purposes": [ "전환", "순서·흐름" ], "media": [ "발표", "설명 영상", "웹 UI" ], "level": "기본", "useWhen": [ "오프닝이나 섹션 시작에서 문이 열리듯 새 화면을 보여 줄 때", "좌우 대칭 레이아웃의 화면을 정돈된 느낌으로 교체할 때" ], "goodExample": "이전 장면이 가운데에서 갈라져 좌우로 각각 화면 폭의 50%씩 0.65초 동안 밀려 나가고, 아래에서 새 장면이 드러난다", "badExample": "양쪽 속도가 달라 중심선이 어긋나거나, 갈라진 틈에 검은 배경이 비친다", "avoid": [ "좌우 이동량은 같게 둔다(비대칭이면 의도 없는 흔들림)", "틈 뒤에는 반드시 다음 장면을 미리 깔아 둔다" ], "params": [ { "name": "지속", "default": "650ms", "range": "500~900ms", "note": "easeInOutCubic" }, { "name": "이동량", "default": "각 960px", "range": "각 960px", "note": "1920px 폭의 절반" }, { "name": "방향", "default": "가로(수평 분할)", "range": "가로 또는 세로", "note": "내용 흐름과 맞춘다" }, { "name": "모드", "default": "Out(열림)", "range": "In / Out / InOut", "note": "In은 닫힘, Out은 열림" }, { "name": "시차", "default": "0ms", "range": "0~60ms", "note": "양쪽 동시가 기본" } ], "ease": "power3.inOut", "snippet": "tl.to('.half-l', { x: -960, duration: 0.65, ease: 'power3.inOut' }, 0);\ntl.to('.half-r', { x: 960, duration: 0.65, ease: 'power3.inOut' }, 0);\n// 아래에는 .next가 이미 있고, 끝난 뒤 반쪽은 hidden", "pairsWith": [ "wipe", "iris-mask", "push-transition" ], "engines": { "hyperframes": "이전 장면을 clip-path inset으로 잘라 반쪽 두 개로 복제하고 x만 움직인다. paused 타임라인에서 두 트윈을 같은 시작 시각에 둔다", "reelforge": "씬 워커 브리프에 방향(가로/세로), 모드(열림/닫힘), 지속 650ms를 파라미터로 싣는다", "scrolline": "scrub에서 이동량을 진행률 0~1에 선형 매핑하면 문을 손으로 여는 느낌이 나온다. 이징은 ease-out만 쓴다" }, "prompts": { "claude": "<대상> 장면을 좌우로 갈라 여는 스플릿 슬라이드 전환을 넣어줘. 이전 장면을 반쪽 두 개로 복제해 왼쪽은 x -960, 오른쪽은 x +960으로 0.65초 동안 power3.inOut으로 밀고, 아래에는 다음 장면이 미리 깔려 있게 해. 양쪽 트윈은 같은 시작 시각, 끝난 뒤 반쪽은 숨겨. 타임라인 하나로 seek 가능하게.", "codex": "<파일>의 전환부에 스플릿 슬라이드를 구현해. .half-l x -960, .half-r x 960, duration 0.65, ease power3.inOut, 같은 position 0. 0.15초, 0.35초, 0.65초 시점을 캡처해 중심 틈이 좌우 대칭으로 벌어지는지, 틈 뒤에 다음 장면이 보이는지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/splitSlideInHorizontal.glsl", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/splitSlideInVertical.glsl", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/splitSlideOutHorizontal.glsl", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/splitSlideOutVertical.glsl", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/splitSlideInOutHorizontal.glsl", "license": "MIT" } ], "runtime": "gsap", "variants": [ "splitSlideInHorizontal", "splitSlideInVertical", "splitSlideOutHorizontal", "splitSlideOutVertical", "splitSlideInOutHorizontal", "splitSlideInOutVertical" ], "oneLinerEn": "Two halves of the frame slide apart or together in opposite directions and swap scenes.", "conveysEn": "Gives openings and closings a clear, symmetric rhythm.", "useWhenEn": [ "When opening a new screen like doors at a section start", "When swapping a symmetric layout with a tidy, orderly feel" ], "promptsEn": { "claude": "Add a Split Slide transition to . Duplicate the previous scene into two halves; slide the left half to x -960 and the right half to x +960 over 0.65s with power3.inOut, starting at the same position. The next scene must already sit underneath. Hide the halves when done. One seekable GSAP timeline.", "codex": "Implement Split Slide in . .half-l x -960, .half-r x 960, duration 0.65, ease power3.inOut, both at position 0. Capture at 0.15s, 0.35s, and 0.65s to confirm the center gap opens symmetrically and that the next scene is visible through it." }, "render": "done", "files": { "html": "effects/split-slide/index.html", "mp4": "effects/split-slide/clip.mp4", "gif": "effects/split-slide/preview.gif", "poster": "effects/split-slide/poster.jpg" }, "no": "143" }, { "slug": "squeeze-transition", "family": "transitions", "ko": "스퀴즈 전환", "en": "Squeeze Transition", "aka": [ "Squeeze transition", "압축 전환", "압착 전환", "Accordion fold", "맞접기 전환" ], "oneLiner": "앞 장면의 한 축이 눌려 사라지고 다음 장면이 같은 축으로 펼쳐지는 전환", "conveys": "자리가 압축과 확장으로 교체된다는 감각. 문이 닫혔다 열리는 듯한 리듬", "purposes": [ "전환", "강조" ], "media": [ "숏폼", "제품 시연", "웹 UI" ], "level": "기본", "useWhen": [ "화면 상태가 좌우 혹은 상하로 크게 바뀌는 순간을 강조할 때", "카드나 패널을 다른 콘텐츠로 교체할 때" ], "goodExample": "앞 장면이 0.3초 동안 scaleX 1에서 0으로 눌려 왼쪽 가장자리로 접히고, 뒤 장면이 같은 가장자리에서 0.35초 동안 펼쳐진다", "badExample": "두 장면이 동시에 눌려 화면이 비는 구간이 길거나, 글자가 늘어나 찌그러지는 채로 보인다", "avoid": [ "글자가 많은 장면은 scale이 아니라 clipPath로 바꾼다(글자 왜곡 방지)", "눌림 정점에서 빈 화면이 0.05초 넘게 남지 않게 한다" ], "params": [ { "name": "전체 지속", "default": "0.65s", "range": "0.45~0.9s", "note": "퇴장과 등장이 절반씩" }, { "name": "축", "default": "X", "range": "X/Y/중앙", "note": "원점은 맞닿는 가장자리" }, { "name": "최소 scale", "default": "0", "range": "0~0.02", "note": "0으로 하면 1프레임 정지" }, { "name": "이징", "default": "power2.inOut", "range": "power2~3", "note": "퇴장은 in, 등장은 out" } ], "ease": "power2.inOut", "snippet": "tl.set('.b', { scaleX: 0, transformOrigin: '0% 50%' })\n .to('.a', { scaleX: 0, transformOrigin: '0% 50%', duration: 0.3, ease: 'power2.in' })\n .to('.b', { scaleX: 1, duration: 0.35, ease: 'power2.out' }, 0.3);", "pairsWith": [ "push-transition", "scale-swap", "wipe" ], "engines": { "hyperframes": "transformOrigin을 시작 상태에서 고정하고 scaleX만 보간한다. 자식 글자는 역스케일하지 않고 clipPath 폴백을 준비한다", "reelforge": "씬 워커 브리프에 axis, originPct, durationMs를 싣고 두 씬 컨테이너에만 적용한다", "scrolline": "진행률 0~0.5는 앞 장면 눌림, 0.5~1은 뒤 장면 펼침으로 나눈다. scrub 역방향에서도 원점이 튀지 않게 origin을 고정한다" }, "prompts": { "claude": "<대상A>에서 <대상B>로 스퀴즈 전환을 만들어줘. A는 transformOrigin을 0% 50%로 두고 0.3초 동안 scaleX 1에서 0으로 power2.in, B는 같은 원점에서 scaleX 0에서 1로 0.35초 power2.out으로 펼쳐지게 해. 전체 타임라인은 paused 하나로 만들어.", "codex": "<파일>에 squeeze 전환을 넣어. 앞 요소 scaleX 1에서 0 (0.3s, power2.in), 뒤 요소 scaleX 0에서 1 (0.3초 시점부터 0.35s, power2.out), 둘 다 transformOrigin 0% 50%. 0.3초에 앞 요소가 보이지 않는지, 0.65초에 뒤 요소가 원래 크기인지 캡처로 확인하고 글자 왜곡 여부도 봐." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/squeeze.glsl", "license": "MIT" }, { "title": "FFmpeg/FFmpeg", "url": "https://ffmpeg.org/ffmpeg-filters.html#xfade", "license": "LGPL-2.1-or-later" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" }, { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/Fold.glsl", "license": "MIT" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:hyperframes-animation/transitions/css-push.md", "license": "unknown" } ], "runtime": "css", "variants": [ "squeeze", "squeezeh", "squeezev", "Fold", "중앙 압착", "가로 압축", "세로 압축", "맞닿은 가장자리 교체", "Accordion fold" ], "oneLinerEn": "The outgoing scene is squashed along one axis while the next scene unfolds from the same edge.", "conveysEn": "Space is exchanged by compression and expansion, like a door closing and reopening.", "useWhenEn": [ "Emphasize a big left-right or up-down state change.", "Swap one card or panel for different content." ], "promptsEn": { "claude": "Build a squeeze transition from to . Set transformOrigin to 0% 50%. A goes scaleX 1 to 0 over 0.3 seconds with power2.in, then B goes scaleX 0 to 1 over 0.35 seconds with power2.out from the same origin. Use one paused timeline.", "codex": "Add a squeeze transition in . Outgoing element scaleX 1 to 0 (0.3s, power2.in), incoming element scaleX 0 to 1 (starting at 0.3s, 0.35s, power2.out), both with transformOrigin 0% 50%. Capture at 0.3 seconds to confirm the outgoing element is gone, at 0.65 seconds to confirm the incoming one is full size, and check text for distortion." }, "render": "planned", "files": {}, "no": "144" }, { "slug": "stereo-viewer-swap", "family": "transitions", "ko": "스테레오 뷰어 전환", "en": "Stereo Viewer Swap", "aka": [ "Stereo viewer swap", "스테레오 뷰어 교체" ], "oneLiner": "장면이 둥근 모서리의 작은 창으로 축소되고 두 복제 창이 화면 밖 축을 따라 반대 방향으로 회전해 갈라진다. 검은 중간 구간 뒤 두 마스크가 새 장면을 드러내고 화면이 확대된다", "conveys": "슬라이드 뷰어에서 필름을 넘기는 듯한 물리적 교체감을 준다", "purposes": [ "전환", "브랜딩" ], "media": [ "제품 시연", "설명 영상", "발표" ], "level": "고급", "useWhen": [ "제품 소개 영상에서 화면 두 개를 뷰어 창처럼 넘기며 다음 기능으로 갈 때", "사진첩, 카탈로그처럼 한 장씩 넘겨 보는 소재의 영상" ], "goodExample": "장면이 zoom 0.88의 둥근 창으로 줄어든 뒤 복제 창 둘이 반대로 돌며 갈라지고, 검은 틈 뒤에서 새 장면이 열려 다시 확대된다(0.9초)", "badExample": "검은 중간 구간이 0.3초를 넘어 화면이 죽은 것처럼 보이거나, 모서리 반경이 너무 작아 창 느낌이 없다", "avoid": [ "검은 중간 구간은 전체의 30% 이하로 둔다", "내용 위주의 설명 장표 사이에는 쓰지 않는다" ], "params": [ { "name": "지속", "default": "900ms", "range": "700~1100ms", "note": "easeInOutCubic" }, { "name": "축소율", "default": "0.88", "range": "0.8~0.92", "note": "창으로 줄어듦" }, { "name": "모서리 반경", "default": "화면 높이 22%", "range": "15~25%", "note": "창 느낌" }, { "name": "검은 구간", "default": "진행 40~60%", "range": "전체 30% 이하", "note": "짧게" }, { "name": "복제 창 회전", "default": "±90deg", "range": "±60~90deg", "note": "반대 방향" } ], "ease": "power3.inOut", "snippet": "tl.to('.prev', { scale: 0.88, borderRadius: 238, duration: 0.3, ease: 'power2.out' }, 0);\ntl.to('.prev-l', { rotationY: -90, duration: 0.3, ease: 'power2.in' }, 0.3);\ntl.to('.prev-r', { rotationY: 90, duration: 0.3, ease: 'power2.in' }, 0.3);\ntl.fromTo('.next', { scale: 0.88, borderRadius: 238 }, { scale: 1, borderRadius: 0, duration: 0.3, ease: 'power2.out' }, 0.6);", "pairsWith": [ "cube-transition", "iris-mask", "dip-to-color" ], "engines": { "hyperframes": "3구간(축소, 갈라짐, 확대)을 한 paused 타임라인에 0.3초씩 붙인다. 복제 창은 clip 복제로 만들고 rotationY만 쓴다", "reelforge": "씬 워커 브리프에 축소율 0.88, 반경 22%, 검은 구간 길이를 넣는다. 복제 창 요소를 워커에 별도로 요구한다", "scrolline": "scrub에서는 3구간을 진행률 0~0.33, 0.33~0.66, 0.66~1로 나누고 각 구간에 ease-out을 쓴다. 되감으면 그대로 역재생된다" }, "prompts": { "claude": "<대상> 장면 전환에 스테레오 뷰어 전환을 넣어줘. 이전 장면을 0.3초에 scale 0.88, borderRadius 화면 높이의 22%로 줄이고, 복제 창 두 개가 다음 0.3초에 좌우 rotationY ±90도로 갈라지게 해. 마지막 0.3초에 새 장면이 scale 0.88에서 1로 확대돼. 검은 구간은 전체의 30% 이하, GSAP 타임라인 하나로 seek 가능하게 해.", "codex": "<파일>에 스테레오 뷰어 전환을 구현해. 3구간 각 0.3초: 축소(scale 0.88, radius 238px), 갈라짐(.prev-l -90, .prev-r 90 rotationY), 확대(.next 0.88에서 1). 0.15초, 0.45초, 0.75초, 0.9초 시점을 캡처해 창 형태가 보이는지, 검은 구간이 길지 않은지, 마지막에 모서리 둥글기가 0인지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/StereoViewer.glsl", "license": "BSD-2-Clause" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "StereoViewer" ], "oneLinerEn": "The scene shrinks into a rounded window, two copies rotate apart, and after a dark gap the new scene opens and zooms back up.", "conveysEn": "Gives a physical swap feel, like slides in a viewer.", "useWhenEn": [ "When flipping between two screens like a viewer window in a product tour", "For catalog or photo-album subjects where items are viewed one at a time" ], "promptsEn": { "claude": "Add a Stereo Viewer Swap to . First 0.3s: shrink the previous scene to scale 0.88 with a corner radius of 22% of frame height. Next 0.3s: two copies rotateY to -90 and +90. Last 0.3s: the new scene scales from 0.88 to 1 and its radius returns to 0. Keep the dark gap under 30% of the total. One seekable GSAP timeline.", "codex": "Implement Stereo Viewer Swap in . Three 0.3s phases: shrink (scale 0.88, radius 238px), split (.prev-l rotationY -90, .prev-r 90), expand (.next 0.88 to 1). Capture at 0.15s, 0.45s, 0.75s, and 0.9s to confirm the window shape is visible, the dark gap is short, and the corner radius is 0 at the end." }, "render": "planned", "files": {}, "no": "145" }, { "slug": "tangent-blur-spin", "family": "transitions", "ko": "회전 블러 전환", "en": "Tangent Motion Blur Spin", "aka": [ "Tangent motion blur spin", "접선 잔상 회전" ], "oneLiner": "장면이 모서리를 중심으로 회전하고 회전 궤도의 접선 방향으로 길게 흐려지며 교체된다", "conveys": "장면이 회전하며 진행 방향으로 길게 흐려진다. 속도와 관성이 강하게 느껴진다", "purposes": [ "전환", "주목 끌기" ], "media": [ "숏폼", "설명 영상" ], "level": "고급", "useWhen": [ "에너지 넘치는 오프닝이나 하이라이트 컷에서 속도감을 줄 때", "장면이 휙 돌아 다음 장으로 넘어가는 트레일러형 전환" ], "goodExample": "장면이 오른쪽 아래 모서리를 축으로 0.65초 동안 도는 동안 접선 방향으로 21샘플 블러가 걸리고, 끝에서 선명해지며 다음 장면이 자리 잡는다", "badExample": "블러가 처음부터 끝까지 일정해 정지감이 없거나, 회전축이 화면 중앙이라 단순 스핀처럼 보인다", "avoid": [ "블러 세기는 회전 속도 정점(진행 40~60%)에서 최대로 둔다", "정보 읽기가 필요한 장면 직전에는 쓰지 않는다" ], "params": [ { "name": "지속", "default": "650ms", "range": "500~800ms", "note": "cubicBezier(0.68,0.01,0.17,0.98)" }, { "name": "회전각", "default": "75deg", "range": "60~90deg", "note": "모서리 축" }, { "name": "블러 길이", "default": "최대 48px", "range": "24~72px", "note": "속도에 비례" }, { "name": "샘플 수", "default": "21", "range": "12~24", "note": "WebGL 다중 샘플" }, { "name": "회전축", "default": "우하단 모서리", "range": "모서리 4곳", "note": "읽는 방향과 반대" } ], "ease": "cubic-bezier(0.68,0.01,0.17,0.98)", "snippet": "// CSS 근사: 회전과 blur를 속도 곡선에 연동\ntl.fromTo('.prev', { rotation: 0, transformOrigin: '100% 100%' }, { rotation: -75, duration: 0.65, ease: 'power3.in' }, 0);\ntl.fromTo('.next', { rotation: 75, transformOrigin: '100% 100%' }, { rotation: 0, duration: 0.65, ease: 'power3.out' }, 0);\ntl.to(['.prev','.next'], { filter: 'blur(6px)', duration: 0.32, yoyo: true, repeat: 1, ease: 'sine.inOut' }, 0);", "pairsWith": [ "motion-blur", "whip-pan", "glitch-transition" ], "engines": { "hyperframes": "WebGL이면 샘플 수를 고정(21)하고 이전 진행률과의 차이로 속도를 구해 접선 블러를 만든다. CSS 근사는 blur filter를 yoyo로 걸고 paused 타임라인에서 seek한다", "reelforge": "씬 워커 브리프에 회전각, 축 위치, 블러 최대 길이, 지속 650ms를 싣는다. 무거우면 샘플 수를 12로 낮춘다", "scrolline": "scrub에서는 블러 세기를 진행 속도가 아니라 진행률의 사인 곡선으로 고정해 스크롤 속도에 흔들리지 않게 한다" }, "prompts": { "claude": "<대상> 장면 전환에 회전 블러 전환을 넣어줘. 이전 장면을 우하단 모서리 축으로 0에서 -75도, 다음 장면은 75도에서 0도로 0.65초 동안 돌리고, 전환 중간(0.32초)에 blur가 6px까지 올랐다 돌아오게 해. 이징은 cubic-bezier(0.68,0.01,0.17,0.98). 타임라인 하나로 seek 가능하게 해줘.", "codex": "<파일>에 회전 블러 전환을 구현해. transformOrigin 100% 100%, .prev rotation 0에서 -75, .next 75에서 0, duration 0.65, blur 최대 6px(중간 0.32초 정점). 0.15초, 0.32초, 0.5초, 0.65초 시점을 캡처해 중간에 블러가 최대인지, 끝에서 다음 장면이 선명한지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/tangentMotionBlur.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "tangentMotionBlur" ], "oneLinerEn": "The scene spins around a corner and smears along the tangent of its path as it is replaced.", "conveysEn": "Conveys speed and inertia of rotation strongly.", "useWhenEn": [ "For energetic openings or highlight cuts that need a sense of speed", "For trailer-style transitions where the frame whips around into the next scene" ], "promptsEn": { "claude": "Add a Tangent Blur Spin to . Rotate the previous scene 0 to -75deg around its bottom-right corner and the next scene 75deg to 0 over 0.65s, with blur peaking at 6px at the midpoint (0.32s) and returning to 0. Use cubic-bezier(0.68,0.01,0.17,0.98) and a single seekable GSAP timeline.", "codex": "Implement Tangent Blur Spin in . transformOrigin 100% 100%; .prev rotation 0 to -75, .next 75 to 0; duration 0.65; blur peaks at 6px at 0.32s. Capture at 0.15s, 0.32s, 0.5s, and 0.65s to confirm blur is maximal mid-transition and that the next scene is sharp at the end." }, "render": "planned", "files": {}, "no": "146" }, { "slug": "additive-dissolve", "family": "transitions", "ko": "가산 디졸브", "en": "Additive Dissolve", "aka": [ "Additive dissolve" ], "oneLiner": "두 장면의 밝은 색이 더해져 중간이 밝아졌다가 다음 장면 밝기로 돌아온다", "conveys": "두 장면의 밝은 색이 더해져 중간이 환해졌다가 다음 장면 밝기로 돌아온다", "purposes": [ "전환", "분위기" ], "media": [ "설명 영상", "숏폼", "제품 시연" ], "level": "중급", "useWhen": [ "밝고 가벼운 분위기의 회상, 꿈, 도입부에서 장면을 부드럽게 넘길 때", "제품 소개 영상에서 화면을 빛이 번지듯 이어 갈 때" ], "goodExample": "600ms 동안 두 장면이 mix-blend-mode screen 또는 plus-lighter로 겹쳐 중간에 밝아지고, 밝기 상한 1을 넘지 않게 눌러 준다", "badExample": "중간이 하얗게 날아가 디테일이 사라지거나, 어두운 장면끼리 써서 차이가 보이지 않는다", "avoid": [ "중간 밝기가 클리핑되지 않도록 한 장면에 brightness 0.85를 곱한다", "어두운 화면 위주 장면에는 효과가 약하다" ], "params": [ { "name": "지속", "default": "600ms", "range": "400~900ms", "note": "linear" }, { "name": "혼합 모드", "default": "plus-lighter 또는 screen", "range": "", "note": "가산" }, { "name": "중간 밝기 제한", "default": "1.0", "range": "0.9~1.1", "note": "클리핑 방지" }, { "name": "앞 장면 opacity", "default": "1에서 0", "range": "", "note": "선형" }, { "name": "뒤 장면 opacity", "default": "0에서 1", "range": "", "note": "선형" } ], "ease": "none", "snippet": "gsap.set('.next', { mixBlendMode: 'plus-lighter' });\ntl.fromTo('.next', { opacity: 0 }, { opacity: 1, duration: 0.6, ease: 'none' }, 0);\ntl.to('.prev', { opacity: 0, duration: 0.6, ease: 'none' }, 0);\ntl.to(['.prev','.next'], { filter: 'brightness(0.85)', duration: 0.3, yoyo: true, repeat: 1, ease: 'sine.inOut' }, 0);", "pairsWith": [ "crossfade", "blur-dissolve", "light-leak-transition" ], "engines": { "hyperframes": "CSS mix-blend-mode를 쓰면 렌더 엔진에서 결정론적이다. 두 opacity를 같은 duration으로 묶어 paused 타임라인에서 seek한다", "reelforge": "씬 워커 브리프에 혼합 모드, 지속 600ms, brightness 0.85를 싣는다", "scrolline": "scrub에서 opacity를 진행률에 선형 매핑한다. 스프링을 쓰지 않으면 스크롤 속도와 무관하게 안정적이다" }, "prompts": { "claude": "<대상> 장면 전환에 가산 디졸브를 넣어줘. .next에 mix-blend-mode plus-lighter를 걸고, .prev opacity 1에서 0, .next opacity 0에서 1로 0.6초 linear로 진행해. 중간이 클리핑되지 않게 두 장면에 brightness 0.85를 0.3초 yoyo로 걸어줘. paused 타임라인으로 seek 가능하게 해.", "codex": "<파일>에 가산 디졸브를 구현해. .next mixBlendMode plus-lighter, opacity 0에서 1, .prev 1에서 0, 0.6초 none, brightness 0.85 yoyo. 0.15초, 0.3초, 0.45초, 0.6초 시점을 캡처해 중간이 밝아지되 흰색으로 날아가지 않는지, 0.6초에 다음 장면 밝기로 돌아오는지 확인해." }, "sources": [ { "title": "Adobe", "url": "https://helpx.adobe.com/premiere/desktop/add-video-effects/effects-and-transitions-library/list-of-video-dissolve-transitions.html", "license": "unknown" }, { "title": "Blackmagic Design", "url": "https://documents.blackmagicdesign.com/UserManuals/DaVinci-Resolve-15-Advanced-Editing.pdf", "license": "unknown" } ], "runtime": "webgl", "variants": [], "oneLinerEn": "The bright colors of two scenes add together, brightening the middle before settling to the next scene.", "conveysEn": "Adds light and liveliness to a scene change.", "useWhenEn": [ "To ease a light, airy scene change such as a memory, dream, or intro", "To carry a product video from one screen to the next like spreading light" ], "promptsEn": { "claude": "Add an Additive Dissolve to . Give .next mix-blend-mode plus-lighter, then tween .prev opacity 1 to 0 and .next 0 to 1 over 0.6s with linear ease. Apply brightness 0.85 with a 0.3s yoyo so the middle does not clip. Keep it on a paused, seekable timeline.", "codex": "Implement Additive Dissolve in . .next mixBlendMode plus-lighter, opacity 0 to 1; .prev 1 to 0; 0.6s ease none; brightness 0.85 yoyo. Capture at 0.15s, 0.3s, 0.45s, and 0.6s to confirm the middle brightens without blowing out to white and that the brightness returns to the next scene by 0.6s." }, "render": "planned", "files": {}, "no": "147" }, { "slug": "band-slide", "family": "transitions", "ko": "밴드 슬라이드", "en": "Band Slide", "aka": [ "Band slide", "띠 슬라이드" ], "oneLiner": "가로나 세로로 나눈 영상 띠가 조금씩 다른 시점이나 방향으로 이동하며 다음 화면이 나타난다", "conveys": "영상 띠들이 조금씩 다른 타이밍으로 밀려 나가고 다음 화면이 드러난다. 줄 단위의 리듬이 있다", "purposes": [ "전환", "순서·흐름" ], "media": [ "발표", "설명 영상", "웹 UI" ], "level": "기본", "useWhen": [ "목록, 표, 가로줄 구조의 화면을 줄 단위로 바꿔 넘길 때", "발표 섹션 전환에서 정돈된 리듬을 만들 때" ], "goodExample": "화면을 가로 띠 8개로 나눠 40ms 간격으로 순서대로 오른쪽으로 밀려 나가며 750ms 안에 다음 화면이 완전히 드러난다", "badExample": "띠 시차가 0.15초 이상이라 전체가 늦어지거나, 띠 경계에 1px 틈이 생겨 선이 보인다", "avoid": [ "띠 시차는 0.02~0.06초로 한다", "띠 경계는 1px 겹치게 clip한다" ], "params": [ { "name": "지속", "default": "750ms", "range": "600~900ms", "note": "띠 하나당 0.5s" }, { "name": "띠 수", "default": "8", "range": "6~12", "note": "135px 높이" }, { "name": "시차", "default": "40ms", "range": "20~60ms", "note": "순차" }, { "name": "이동량", "default": "1920px", "range": "", "note": "화면 폭" }, { "name": "이징", "default": "power3.inOut", "range": "power2~power4", "note": "" } ], "ease": "power3.inOut", "snippet": "for (let i = 0; i < 8; i++) {\n tl.to(`.band-${i}`, { x: 1920, duration: 0.47, ease: 'power3.inOut' }, i * 0.04);\n} // 마지막 띠 종료 = 0.28 + 0.47 = 0.75s", "pairsWith": [ "split-slide", "stagger", "wipe" ], "engines": { "hyperframes": "띠 8개를 clipPath 또는 overflow hidden 복제로 만들고 tl.to의 position을 i*0.04로 깐다. paused 타임라인에서 seek해도 순차가 유지된다", "reelforge": "씬 워커 브리프에 띠 수 8, 시차 40ms, 방향을 싣고 다음 장면이 아래에 깔려 있도록 한다", "scrolline": "scrub에서는 시차를 진행률 간격 0.05로 환산하고 각 띠의 이징은 ease-out 하나만 쓴다" }, "prompts": { "claude": "<대상> 화면 전환에 밴드 슬라이드를 넣어줘. 화면을 가로 띠 8개(각 135px)로 나눠 각 띠가 x 1920까지 0.47초 power3.inOut으로 밀려 나가고, 띠 i는 i*0.04초 늦게 출발해. 마지막 띠가 0.75초에 끝나게 하고, 다음 장면은 아래에 미리 깔아 줘. 타임라인 하나로 seek 가능하게 해줘.", "codex": "<파일>에 밴드 슬라이드를 구현해. 띠 8개, .band-i x 0에서 1920, duration 0.47, power3.inOut, position i*0.04. 0.15초, 0.3초, 0.5초, 0.75초 시점을 캡처해 띠가 위에서 아래로 순서대로 밀려 나가는지, 0.75초에 이전 장면이 없는지, 띠 경계에 틈이 없는지 확인해." }, "sources": [ { "title": "Adobe", "url": "https://helpx.adobe.com/premiere/desktop/add-video-effects/effects-and-transitions-library/list-of-video-transitions.html", "license": "unknown" } ], "runtime": "gsap", "variants": [], "oneLinerEn": "Video bands split horizontally or vertically slide at slightly different timings to reveal the next screen.", "conveysEn": "Gives a line-by-line rhythm to the whole frame.", "useWhenEn": [ "When switching a list, table, or row-structured screen line by line", "To create a tidy rhythm in presentation section transitions" ], "promptsEn": { "claude": "Add a Band Slide to . Split the frame into 8 horizontal bands of 135px; each slides to x 1920 over 0.47s with power3.inOut, band i starting i*0.04s later, so the last band ends at 0.75s. Place the next scene underneath in advance. One seekable GSAP timeline.", "codex": "Implement Band Slide in . 8 bands; .band-i x 0 to 1920, duration 0.47, power3.inOut, position i*0.04. Capture at 0.15s, 0.3s, 0.5s, and 0.75s to confirm bands leave in order from top to bottom, the previous scene is gone at 0.75s, and there are no gaps at band boundaries." }, "render": "done", "files": { "html": "effects/band-slide/index.html", "mp4": "effects/band-slide/clip.mp4", "gif": "effects/band-slide/preview.gif", "poster": "effects/band-slide/poster.jpg" }, "no": "148" }, { "slug": "channel-phase-dissolve", "family": "transitions", "ko": "색 채널 순차 디졸브", "en": "Channel Phase Dissolve", "aka": [ "Channel phase dissolve", "색 채널 시차 페이드" ], "oneLiner": "빨강과 초록과 파랑 채널이 서로 다른 시점에 새 장면으로 넘어가는 전환", "conveys": "디지털 처리와 색 분리가 드러나는 교체. 채널 순서가 어긋나며 색이 번진다", "purposes": [ "전환", "주목 끌기" ], "media": [ "숏폼", "제품 시연" ], "level": "고급", "useWhen": [ "기술적이고 디지털한 톤의 영상에서 크로스페이드에 개성을 더할 때", "글리치보다 부드러운 색 분리 연출에서" ], "goodExample": "0.7초 동안 R은 0.0~0.6, G는 0.2~0.8, B는 0.4~1.0 구간에 각각 새 장면으로 넘어가며 중간에 색이 번진다", "badExample": "채널 시차를 화면 전체 지속만큼 벌려 앞뒤 장면이 서로 다른 색으로 어둡게 물든 채 머문다", "avoid": [ "채널 시차 각각 0.4 진행률 초과 금지", "글자 중심 장면은 사용하지 않는다" ], "params": [ { "name": "전체 지속", "default": "0.7s", "range": "0.5~1.0s", "note": "선형" }, { "name": "R 구간", "default": "0.0~0.6", "range": "", "note": "먼저 시작" }, { "name": "G 구간", "default": "0.2~0.8", "range": "", "note": "중간" }, { "name": "B 구간", "default": "0.4~1.0", "range": "", "note": "나중 끝" } ], "ease": "none", "snippet": "const ch = { r: 0, g: 0, b: 0 };\nconst seg = (k, s, e) => tl.to(ch, { [k]: 1, duration: (e - s) * 0.7, ease: 'none' }, s * 0.7);\nseg('r', 0, 0.6); seg('g', 0.2, 0.8); seg('b', 0.4, 1.0);\ntl.eventCallback('onUpdate', () => shader.set(ch.r, ch.g, ch.b));", "pairsWith": [ "chromatic-wipe", "glitch-rgb-split", "crossfade" ], "engines": { "hyperframes": "채널별 혼합률을 상태 객체에 담아 셰이더에 전달한다. CSS로는 SVG feColorMatrix 세 채널 분리 사용", "reelforge": "씬 워커 브리프에 rangeR, rangeG, rangeB, durationMs를 실어 구간을 조정 가능하게 한다", "scrolline": "진행률 p에서 채널별 clamp((p-s)/(e-s))를 계산한다. 이징은 linear" }, "prompts": { "claude": "<대상A>에서 <대상B>로 채널 페이즈 디졸브를 만들어줘. 0.7초 전체 동안 R 채널은 0.0~0.6, G 채널은 0.2~0.8, B 채널은 0.4~1.0 구간(0.7초 기준 비율)에서 각각 A에서 B로 넘어가게 해. 채널 혼합률은 상태 객체 3개로 두고 이징 none, paused 타임라인 하나로 seek되게 만들어.", "codex": "<파일>에 channel phase dissolve를 적용해. r,g,b 혼합률을 각각 0에서 1로 (0~0.42s, 0.14~0.56s, 0.28~0.7s) ease none으로 보간하고 셰이더에서 채널별 mix를 계산한다. 0.2초와 0.35초 캡처에서 색 번짐이 보이는지, 0.7초에 B와 동일한지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/colorphase.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "colorphase" ], "oneLinerEn": "Red, green, and blue channels hand over to the new scene at different times.", "conveysEn": "A swap that exposes digital processing: channels fall out of step and color bleeds.", "useWhenEn": [ "Add character to a crossfade in a technical, digital-toned video.", "Get a gentler color-split effect than a glitch." ], "promptsEn": { "claude": "Build a channel phase dissolve from to over 0.7 seconds. The R channel hands over during 0.0 to 0.6 of the duration, G during 0.2 to 0.8, and B during 0.4 to 1.0. Keep the three channel mix values in state objects, use ease none, and keep everything in one paused timeline that seeks.", "codex": "Apply a channel phase dissolve in . Tween r, g, b mix values 0 to 1 over (0 to 0.42s, 0.14 to 0.56s, 0.28 to 0.7s) with ease none and compute per-channel mix in the shader. Capture at 0.2 and 0.35 seconds to confirm visible color fringing, and at 0.7 seconds to confirm the frame matches B." }, "render": "planned", "files": {}, "no": "149" }, { "slug": "chapter-interstitial", "family": "transitions", "ko": "챕터 인터스티셜", "en": "Chapter Interstitial", "aka": [ "Demo chapter interstitial", "시연 챕터 전환 카드" ], "oneLiner": "장면 사이에 단계 제목 카드가 나타나 잠시 유지된 뒤 다음 장면으로 넘어간다", "conveys": "장면 사이에 단계 제목 카드가 잠깐 나와 긴 시연이나 설명의 경계를 분명히 한다", "purposes": [ "순서·흐름", "설명" ], "media": [ "제품 시연", "설명 영상", "발표" ], "level": "기본", "useWhen": [ "긴 시연이나 튜토리얼을 단계 1, 단계 2처럼 나눌 때", "챕터가 바뀌는 지점에서 지금 어디인지 알릴 때" ], "goodExample": "제목 카드가 250ms 동안 y +24px에서 올라오며 나타나 1200ms 머물고, 250ms 동안 위로 빠지며 다음 장면으로 이어진다", "badExample": "카드가 2초 이상 머물러 흐름이 끊기거나, 단계 번호가 없어 어디까지 왔는지 알 수 없다", "avoid": [ "카드 유지는 1.0~1.5초", "단계 번호와 제목을 함께 넣는다(예: 02 설정하기)" ], "params": [ { "name": "카드 유지", "default": "1200ms", "range": "1000~1500ms", "note": "읽는 시간" }, { "name": "등장", "default": "250ms", "range": "200~350ms", "note": "y +24에서 0, opacity" }, { "name": "퇴장", "default": "250ms", "range": "200~350ms", "note": "y 0에서 -24" }, { "name": "이징", "default": "power2.out / power2.in", "range": "", "note": "등장 out, 퇴장 in" }, { "name": "번호 크기", "default": "제목의 1.6배", "range": "", "note": "왼쪽 정렬" } ], "ease": "power2.out", "snippet": "tl.fromTo('.chapter', { y: 24, opacity: 0 }, { y: 0, opacity: 1, duration: 0.25, ease: 'power2.out' }, 0);\ntl.to('.chapter', { y: -24, opacity: 0, duration: 0.25, ease: 'power2.in' }, 1.45);\ntl.set('.prev', { autoAlpha: 0 }, 0.25).set('.next', { autoAlpha: 1 }, 1.7);", "pairsWith": [ "progressive-disclosure", "step-panel-walkthrough", "fade-slide" ], "engines": { "hyperframes": "카드는 독립 레이어로 두고 등장, 유지, 퇴장을 절대 시각(0, 1.45)으로 얹는다. 제목은 데이터 변수로 받아 회차마다 바꾼다", "reelforge": "씬 워커 브리프에 단계 번호, 제목 문구, 유지 1200ms, 카드 스타일(전체/작은 배지)을 싣는다", "scrolline": "scrub에서는 카드를 진행률 구간(0.4~0.6)에서만 보이고 등장은 ease-out, 퇴장은 ease-in으로 처리한다" }, "prompts": { "claude": "<대상> 영상의 단계 경계에 챕터 인터스티셜을 넣어줘. .chapter(번호와 제목)가 y +24px, opacity 0에서 0.25초 power2.out으로 나타나 1.2초 머물고, 1.45초부터 0.25초 동안 y -24px, opacity 0으로 power2.in으로 빠지게 해. 카드가 사라진 뒤 1.7초에 다음 장면을 표시해. 제목은 변수로 받아줘.", "codex": "<파일>에 챕터 인터스티셜을 구현해. .chapter y 24에서 0, opacity 0에서 1 (0.25초 power2.out), 1.45초부터 0.25초 y -24 opacity 0 (power2.in), 1.7초에 .next 표시. 0.15초, 0.8초, 1.55초, 1.8초 시점을 캡처해 0.8초에 번호와 제목이 잘 읽히는지, 1.8초에 카드가 없는지 확인해." }, "sources": [ { "title": "Supademo", "url": "https://supademo.com/features/demo-editor", "license": "unknown" } ], "runtime": "gsap", "variants": [ "전체 제목", "작은 챕터 배지", "Demo chapter interstitial" ], "oneLinerEn": "A step-title card appears between scenes, holds briefly, then hands off to the next scene.", "conveysEn": "Makes the boundaries of long demos or explanations clear.", "useWhenEn": [ "To split a long demo or tutorial into step 1, step 2, and so on", "At chapter changes to show where the viewer is" ], "promptsEn": { "claude": "Add a Chapter Interstitial between steps in . .chapter (number and title) enters from y +24px, opacity 0 over 0.25s with power2.out, holds 1.2s, then exits from 1.45s over 0.25s to y -24px, opacity 0 with power2.in. Show the next scene at 1.7s. Take the title as a variable.", "codex": "Implement Chapter Interstitial in . .chapter y 24 to 0 and opacity 0 to 1 (0.25s power2.out); from 1.45s, y 0 to -24 and opacity 1 to 0 (0.25s power2.in); show .next at 1.7s. Capture at 0.15s, 0.8s, 1.55s, and 1.8s to confirm the number and title are legible at 0.8s and the card is gone at 1.8s." }, "render": "done", "files": { "html": "effects/chapter-interstitial/index.html", "mp4": "effects/chapter-interstitial/clip.mp4", "gif": "effects/chapter-interstitial/preview.gif", "poster": "effects/chapter-interstitial/poster.jpg" }, "no": "150" }, { "slug": "clone-wall-wipe", "family": "transitions", "ko": "타일 월 와이프", "en": "Clone Wall Wipe", "aka": [ "Clone wall cover", "복제 글자벽 전환" ], "oneLiner": "같은 단어의 타일 벽이 화면을 채우고 반전된 뒤 걷히는 전환", "conveys": "메시지를 반복해 각인시키는 강한 경계. 타이포 중심 스타일", "purposes": [ "전환", "강조" ], "media": [ "숏폼", "발표", "설명 영상" ], "level": "중급", "useWhen": [ "핵심 단어를 강하게 남기며 장면을 바꿀 때", "타이포 위주 오프닝이나 챕터 카드에서" ], "goodExample": "8x6 격자의 같은 단어 타일이 20ms 간격으로 채워지고 difference 혼합으로 반전되어 0.8초 안에 컷 후 걷힌다", "badExample": "타일 단어가 읽히지 않는 작은 크기이거나, 채우는 시간이 길어 오히려 지루하다", "avoid": [ "단어는 1개, 한 화면에 반복은 48개 이하", "반전 유지는 0.1초 이내" ], "params": [ { "name": "전체 지속", "default": "0.8s", "range": "0.6~1.2s", "note": "채우기 0.4s 걷기 0.4s" }, { "name": "격자", "default": "8x6", "range": "6x4~10x8", "note": "타일 하나 240px 폭" }, { "name": "타일 간격", "default": "20ms", "range": "10~40ms", "note": "좌상에서 우하 순서" }, { "name": "혼합", "default": "difference", "range": "difference/exclusion", "note": "반전 표현" } ], "ease": "power2.out", "snippet": "tl.from('.tile', { opacity: 0, scale: 0.8, duration: 0.2, ease: 'power2.out',\n stagger: { each: 0.02, grid: [6, 8], from: 'start' } })\n .set('.a', { display: 'none' })\n .to('.tile', { opacity: 0, duration: 0.2, stagger: { each: 0.02, grid: [6, 8], from: 'start' } }, '+=0.1');", "pairsWith": [ "token-split", "split-lockup", "stagger" ], "engines": { "hyperframes": "GSAP grid stagger로 타일 순서를 계산한다. 타일은 DOM 48개까지 문제없다", "reelforge": "씬 워커 브리프에 word, gridCols, gridRows, staggerMs를 싣고 단어를 씬 텍스트에서 받는다", "scrolline": "진행률 0~0.45 채우기, 0.5 컷, 0.55~1 걷기로 나눈다. 타일 지연은 진행률 0.01 간격" }, "prompts": { "claude": "<대상A>에서 <대상B>로 클론 월 와이프를 만들어줘. '<단어>'를 8x6 격자로 48개 복제해 좌상에서 우하 순서로 20ms 간격, opacity 0과 scale 0.8에서 등장(0.2초, power2.out)시켜 화면을 채우고, 다 찬 뒤 A를 B로 교체하고 같은 순서로 걷어 줘. 타일 벽은 mix-blend-mode difference, 전체 paused 타임라인 하나.", "codex": "<파일>에 clone wall wipe를 넣어. .tile 48개를 grid [6, 8], each 0.02s로 opacity 0, scale 0.8에서 등장(0.2s), 채워진 뒤 장면 교체, 이후 0.1초 홀드하고 같은 순서로 opacity 0. 0.3초에 타일이 채워지는 중인지, 0.5초에 화면 전체가 타일인지, 0.9초에 타일이 없는지 캡처로 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/mk-clone-wall-transition/registry-item.json", "license": "Apache-2.0" } ], "runtime": "css", "variants": [ "Clone wall cover" ], "oneLinerEn": "A wall of tiles repeating the same word fills the frame, inverts, then clears.", "conveysEn": "A hard boundary that hammers a message by repetition: typography-led style.", "useWhenEn": [ "Leave a key word behind strongly as scenes change.", "Use in type-driven openers or chapter cards." ], "promptsEn": { "claude": "Build a clone wall wipe from to . Clone '' into an 8x6 grid of 48 tiles, fill from top left to bottom right at 20ms intervals with each tile going opacity 0 and scale 0.8 to full over 0.2 seconds (power2.out). Once full, swap A for B and clear in the same order. Use mix-blend-mode difference and one paused timeline.", "codex": "Add a clone wall wipe to . Animate 48 .tile elements with grid [6, 8] and each 0.02s from opacity 0 and scale 0.8 (0.2s), swap scenes once filled, hold 0.1s, then fade out in the same order. Capture at 0.3 seconds to confirm filling is in progress, at 0.5 seconds for a full-frame wall, and at 0.9 seconds to confirm no tiles remain." }, "render": "planned", "files": {}, "no": "151" }, { "slug": "color-distance-dissolve", "family": "transitions", "ko": "색차 디졸브", "en": "Color Distance Dissolve", "aka": [ "Color distance dissolve", "색 거리 전환" ], "oneLiner": "두 장면의 색 차이가 큰 부분과 작은 부분이 서로 다른 속도로 교체되는 전환", "conveys": "그림의 내용이 전환 순서를 정한다는 감각. 비슷한 색부터 먼저 스며든다", "purposes": [ "전환", "분위기" ], "media": [ "설명 영상", "숏폼" ], "level": "고급", "useWhen": [ "같은 구도에서 색만 다른 두 장면(전과 후)을 자연스럽게 잇고 싶을 때", "콘텐츠 자체가 전환의 결을 정하는 디졸브를 원할 때" ], "goodExample": "0.8초 동안 색이 비슷한 영역은 먼저, 색차가 큰 영역은 나중에 뒤 장면으로 바뀌며 power 5의 곡선으로 경계가 형성된다", "badExample": "두 장면의 색이 거의 같아 차이가 없거나, 노이즈가 많은 사진에서 반짝임이 생긴다", "avoid": [ "power 값 8 초과 금지(경계가 톱니처럼 거칠어진다)", "두 장면 색이 무관하면 다른 디졸브를 쓴다" ], "params": [ { "name": "전체 지속", "default": "0.8s", "range": "0.6~1.2s", "note": "선형" }, { "name": "power", "default": "5", "range": "2~8", "note": "클수록 차이 부분이 늦게 교체" }, { "name": "진행 임계", "default": "p", "range": "0~1", "note": "진행률과 색거리 비교" }, { "name": "혼합", "default": "smoothstep", "range": "", "note": "경계 부드럽게" } ], "ease": "none", "snippet": "const u = { p: 0 };\ntl.to(u, { p: 1, duration: 0.8, ease: 'none', onUpdate: () => {\n // GLSL: m = smoothstep(0.0, 0.1, p * 1.1 - pow(dist(a,b), 5.0));\n shader.set('progress', u.p);\n} });", "pairsWith": [ "luma-wipe", "noise-dissolve", "hsv-dissolve" ], "engines": { "hyperframes": "progress 하나만 셰이더에 넘기고 나머지 계산은 픽셀에서 한다. 두 장면 텍스처를 미리 올려 seek 후 즉시 그린다", "reelforge": "씬 워커 브리프에 power, durationMs, softness를 싣고 비교용 정지 이미지 두 장을 지정한다", "scrolline": "진행률 p를 그대로 셰이더 progress로 쓴다. 스크롤을 멈춘 프레임이 혼합 상태여도 자연스럽다" }, "prompts": { "claude": "<대상A>에서 <대상B>로 색 거리 디졸브를 만들어줘. 진행값 p를 0.8초 동안 0에서 1로 ease none으로 올리고, 픽셀마다 두 장면 색의 거리 d를 구해 mix 가중치를 smoothstep(0.0, 0.1, p*1.1 - pow(d, 5.0))로 계산하게 해. 진행값은 GSAP 상태 객체 하나에서 읽어 paused 타임라인에서 seek되게 해.", "codex": "<파일>에 color distance dissolve를 적용해. progress를 0에서 1로 0.8s ease none, 셰이더에서 m = smoothstep(0.0, 0.1, p*1.1 - pow(distance(a,b), 5.0)). 0.3초와 0.5초 캡처에서 색이 비슷한 영역이 먼저 B로 바뀌는지, 0.8초에 전면이 B인지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/ColourDistance.glsl", "license": "MIT" }, { "title": "FFmpeg/FFmpeg", "url": "https://ffmpeg.org/ffmpeg-filters.html#xfade", "license": "LGPL-2.1-or-later" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "ColourDistance", "distance" ], "oneLinerEn": "Areas where the two scenes differ most in color swap at different speeds from areas where they match.", "conveysEn": "The picture's own content decides the order of the change: similar colors melt across first.", "useWhenEn": [ "Connect two scenes with the same layout but different colors, such as before and after.", "Want a dissolve whose grain is set by the content itself." ], "promptsEn": { "claude": "Build a color distance dissolve from to . Ramp progress p from 0 to 1 over 0.8 seconds with ease none. Per pixel, compute the color distance d between the two scenes and mix with smoothstep(0.0, 0.1, p*1.1 - pow(d, 5.0)). Read progress from one GSAP state object in a paused timeline that seeks.", "codex": "Apply a color distance dissolve in . Tween progress 0 to 1 over 0.8s with ease none and use m = smoothstep(0.0, 0.1, p*1.1 - pow(distance(a,b), 5.0)) in the shader. Capture at 0.3 and 0.5 seconds to confirm similar-color areas switch to B first, and at 0.8 seconds to confirm the whole frame is B." }, "render": "planned", "files": {}, "no": "152" }, { "slug": "cross-cutting", "family": "transitions", "ko": "교차 편집", "en": "Cross Cutting", "aka": [ "Cross cutting" ], "oneLiner": "두 장소나 두 행동의 장면을 번갈아 보여준다", "conveys": "두 장소의 사건을 번갈아 보여 동시 진행과 긴장 관계를 이해시킨다", "purposes": [ "비교", "순서·흐름" ], "media": [ "숏폼", "설명 영상", "발표" ], "level": "중급", "useWhen": [ "서로 다른 두 곳의 진행 상황(예: 개발과 마케팅)을 동시에 보여 줄 때", "추격, 마감 카운트다운처럼 두 흐름이 만나는 순간으로 긴장을 쌓을 때" ], "goodExample": "A와 B를 1.5초씩 번갈아 보이다가 컷 간격을 1.5, 1.1, 0.8, 0.5초로 줄여 가며 두 흐름이 만나는 장면으로 모인다", "badExample": "간격이 계속 같아 긴장이 오르지 않거나, 두 장면의 색과 구도가 같아 어느 쪽인지 구분이 안 된다", "avoid": [ "A와 B는 색 온도나 구도로 구분한다", "컷 수는 6~8개 안에 끝낸다" ], "params": [ { "name": "전환 길이", "default": "0ms", "range": "고정", "note": "즉시" }, { "name": "시작 간격", "default": "1500ms", "range": "1200~1800ms", "note": "초반" }, { "name": "종료 간격", "default": "500ms", "range": "400~700ms", "note": "수렴" }, { "name": "감소 단계", "default": "4", "range": "3~5", "note": "1.5, 1.1, 0.8, 0.5" }, { "name": "공통 오디오", "default": "연속 음악 하나", "range": "", "note": "컷과 별개로 이어짐" } ], "ease": "none", "snippet": "const gaps = [1.5, 1.1, 0.8, 0.5, 0.5];\nlet t = 0;\ngaps.forEach((g, i) => {\n tl.set(i % 2 ? '.b' : '.a', { autoAlpha: 1 }, t).set(i % 2 ? '.a' : '.b', { autoAlpha: 0 }, t);\n t += g;\n});", "pairsWith": [ "montage", "split-compare", "shot-reverse-shot" ], "engines": { "hyperframes": "간격 배열로 누적 시각을 계산해 tl.set을 깐다. 두 장면 내부 시계는 계속 진행되게 하고 가시성만 교체한다", "reelforge": "씬 워커 브리프에 간격 배열 [1.5,1.1,0.8,0.5,0.5]과 A/B 구분 색을 싣는다", "scrolline": "scrub에서는 컷 시각을 진행률 누적값(0, 0.3, 0.52, 0.68, 0.84)으로 바꿔 넣는다. 간격이 줄어드는 리듬은 그대로 유지한다" }, "prompts": { "claude": "<대상> 영상에 교차 편집을 넣어줘. .a와 .b를 간격 배열 [1.5, 1.1, 0.8, 0.5, 0.5]초로 번갈아 보이게 하고 tl.set으로 즉시 교체해. 두 장면의 내부 시간은 계속 흐르게 하고 색 온도로 구분해. 마지막 컷 뒤에는 두 흐름이 만나는 합류 장면을 표시해.", "codex": "<파일>에 교차 편집을 구현해. gaps=[1.5,1.1,0.8,0.5,0.5]에서 누적 시각마다 .a와 .b 가시성을 tl.set으로 교대. 0.5초, 1.8초, 2.9초, 3.6초 시점을 캡처해 어느 시점에 A 또는 B인지 순서가 맞는지, 컷 간격이 점점 짧아지는지 확인해." }, "sources": [ { "title": "Adobe", "url": "https://www.adobe.com/creativecloud/video/post-production/cuts-in-film/cross-cut.html", "license": "unknown" } ], "runtime": "gsap", "variants": [ "Parallel editing", "주제 대비 교차", "동시 사건 교차" ], "oneLinerEn": "Scenes from two places or actions are shown alternately.", "conveysEn": "Helps viewers grasp simultaneous progress, comparison, or tension.", "useWhenEn": [ "When showing two parallel efforts at once, such as development and marketing", "When building tension toward a moment where two lines meet, like a chase or deadline countdown" ], "promptsEn": { "claude": "Add Cross Cutting to . Alternate .a and .b using the gap array [1.5, 1.1, 0.8, 0.5, 0.5] seconds, swapping instantly with tl.set. Keep the internal time of both scenes running, distinguish them by color temperature, and show a merge scene after the last cut.", "codex": "Implement Cross Cutting in . With gaps=[1.5,1.1,0.8,0.5,0.5], toggle .a and .b visibility via tl.set at each cumulative time. Capture at 0.5s, 1.8s, 2.9s, and 3.6s to confirm the A and B order is correct and that the cut intervals shorten." }, "render": "planned", "files": {}, "no": "153" }, { "slug": "cutaway", "family": "transitions", "ko": "컷어웨이", "en": "Cutaway", "aka": [ "Cutaway Shot", "Humor reaction insert", "짧은 반응 삽입" ], "oneLiner": "주행동을 보여주다가 관련 대상이나 반응 장면을 잠깐 삽입하고 원래 행동으로 돌아온다", "conveys": "주행동 중에 관련 화면이나 반응을 잠깐 끼워 맥락을 채우고 편집 공백을 자연스럽게 덮는다", "purposes": [ "설명", "분위기" ], "media": [ "설명 영상", "숏폼", "발표" ], "level": "기본", "useWhen": [ "설명 중 관련 화면이나 참고 영상을 잠깐 보여 줄 때", "말실수나 편집 점프를 다른 화면으로 자연스럽게 덮을 때" ], "goodExample": "주 영상이 계속 흐르는 동안 관련 화면이 1.2초 동안 전체로 들어왔다 나가고 0.7초 더 원래 행동을 보여 준다", "badExample": "삽입이 짧아 알아볼 수 없거나(0.3초), 삽입 중 주 영상 시간이 멈춰 복귀 후 같은 말이 반복된다", "avoid": [ "삽입 중에도 주 영상의 시간은 흐르게 한다", "유머 반응 삽입은 500ms 이하로 짧게" ], "params": [ { "name": "입출 컷", "default": "0ms", "range": "고정", "note": "즉시" }, { "name": "삽입 홀드", "default": "1200ms", "range": "800~1600ms", "note": "유머 반응은 500ms" }, { "name": "복귀 홀드", "default": "700ms", "range": "500~1000ms", "note": "" }, { "name": "작은 창 크기", "default": "화면의 28%", "range": "20~35%", "note": "전체 화면 대신 사용할 때" }, { "name": "작은 창 위치", "default": "우하단 여백 48px", "range": "", "note": "대체 형태" } ], "ease": "none", "snippet": "tl.set('.cut', { autoAlpha: 1 }, 3.0);\ntl.set('.cut', { autoAlpha: 0 }, 4.2);\n// 주 영상 .main 은 컷 구간에도 계속 재생(시계 공유)", "pairsWith": [ "insert-shot", "reaction-cut", "cross-cutting" ], "engines": { "hyperframes": "주 영상은 항상 재생하고 .cut 가시성만 교체한다. 두 레이어를 하나의 paused 타임라인 시계에 두면 seek 시 시간이 어긋나지 않는다", "reelforge": "씬 워커 브리프에 삽입 시각 3.0초, 홀드 1.2초, 전체/작은 창 여부를 싣는다", "scrolline": "scrub에서는 진행률 구간 하나에서만 .cut을 보이고 주 영상 진행은 계속된다. 구간 앞뒤에 보간은 넣지 않는다" }, "prompts": { "claude": "<대상> 영상 3.0초에 컷어웨이를 넣어줘. 3.0초에 관련 화면 .cut을 표시하고 4.2초에 숨겨. 주 영상은 컷 구간에도 계속 재생돼야 하고, 복귀 뒤 0.7초 유지해. 전환 트윈 없이 tl.set만 사용하고 paused 타임라인으로 seek 가능하게 해.", "codex": "<파일>에 컷어웨이를 구현해. 3.0초 .cut 표시, 4.2초 숨김, tl.set. .main은 계속 재생. 2.9초, 3.6초, 4.3초, 5.0초 시점을 캡처해 3.6초에 .cut이 보이는지, 4.3초에 .main으로 돌아왔는지, .main의 진행이 3.6초 이전보다 진행돼 있는지 확인해." }, "sources": [ { "title": "CapCut", "url": "https://www.capcut.com/resource/types-of-filmmaking-transitions", "license": "unknown" }, { "title": "Fireship", "url": "https://www.youtube.com/watch?v=vKJpN5FAeF4", "license": "unknown" }, { "title": "motion dictionary 2-transitions-camera.md#11. 컷어웨이 · Cutaway Shot", "url": "", "license": "own" } ], "runtime": "gsap", "variants": [ "전체 화면 반응", "작은 반응 창", "관련 맥락", "유머 반응 삽입", "Cutaway Shot", "Humor reaction insert" ], "oneLinerEn": "The main action is interrupted by a brief related shot or reaction, then it returns.", "conveysEn": "Fills in context and naturally hides edit gaps.", "useWhenEn": [ "When briefly showing a related screen or reference during an explanation", "When covering a slip of the tongue or an edit jump with another shot" ], "promptsEn": { "claude": "Add a Cutaway to at 3.0s. Show the related layer .cut at 3.0s and hide it at 4.2s. The main video keeps playing during the cutaway; hold 0.7s after returning. Use tl.set only with no transition tweens, and make it seekable on a paused timeline.", "codex": "Implement Cutaway in . .cut shown at 3.0s, hidden at 4.2s via tl.set; .main keeps playing. Capture at 2.9s, 3.6s, 4.3s, and 5.0s to confirm .cut is visible at 3.6s, .main is back at 4.3s, and that .main has progressed beyond where it was before the cutaway." }, "render": "planned", "files": {}, "no": "154" }, { "slug": "datamosh-transition", "family": "transitions", "ko": "데이터모시 전환", "en": "Datamosh Transition", "aka": [ "Strip datamosh", "띠 데이터모시" ], "oneLiner": "가로 띠와 세로 틈이 찢기고 이전 영상 잔해가 늘어 붙으며 다음 화면으로 바뀐다", "conveys": "압축이 깨진 영상처럼 이전 화면이 늘어 붙으며 다음 장면으로 끌려간다. 파괴감이 강하다", "purposes": [ "전환", "주목 끌기" ], "media": [ "숏폼", "설명 영상" ], "level": "고급", "useWhen": [ "뮤직비디오, 게임, 실험 영상에서 장면을 찢듯 넘길 때", "충격적인 반전 컷에서 붕괴하는 느낌이 필요할 때" ], "goodExample": "600ms 동안 가로 띠 42개가 서로 다른 거리로 밀려 나가고, 이전 영상의 잔해가 62% 세기로 늘어 붙었다가 다음 화면에 자리를 내준다", "badExample": "띠가 균일하게 밀려 단순 슬라이드처럼 보이거나, 잔해와 노이즈가 너무 세서 다음 장면이 읽히지 않는다", "avoid": [ "잔해 세기는 0.7 이하, 끝 프레임에서는 0으로 정리한다", "장식용으로 반복하지 않는다. 한 영상에 한두 번만 쓴다" ], "params": [ { "name": "지속", "default": "600ms", "range": "450~800ms", "note": "linear" }, { "name": "가로 띠 수", "default": "42", "range": "24~60", "note": "띠별 밀림 거리 다름" }, { "name": "세로 틈 수", "default": "18", "range": "8~24", "note": "찢어지는 균열" }, { "name": "잔해 세기", "default": "0.62", "range": "0.4~0.7", "note": "이전 프레임 잔상" }, { "name": "색 분리", "default": "화면 폭 3.2%", "range": "2~4%", "note": "RGB 오프셋" } ], "ease": "none", "snippet": "const h = i => Math.abs(Math.sin(i * 91.7 + 3) * 43758.5) % 1;\nfor (let i = 0; i < 42; i++) {\n tl.to(`.bar-${i}`, { x: (h(i) - 0.5) * 300, duration: 0.6, ease: 'none' }, 0);\n tl.to(`.bar-${i}`, { opacity: 0, duration: 0.2 }, 0.4);\n}\ntl.to('.residue', { opacity: 0.62, duration: 0.3, yoyo: true, repeat: 1, ease: 'none' }, 0);", "pairsWith": [ "glitch-transition", "codec-glitch", "tv-static-transition" ], "engines": { "hyperframes": "띠 42개를 이전 장면 clip 복제로 만들고 x 이동량은 시드 해시로 고정한다. 잔해 레이어는 이전 프레임의 복제 하나만 쓴다", "reelforge": "씬 워커 브리프에 띠 수 42, 잔해 세기 0.62, 색 분리 3.2%, 시드 1을 싣는다. 실제 코덱 오류를 흉내내지 않고 레이어 합성으로 처리하도록 명시한다", "scrolline": "scrub에서는 띠 이동량을 진행률 p에 선형으로 곱하고 잔해는 진행률 0.5에서 정점인 삼각 곡선으로 둔다. 스프링 금지" }, "prompts": { "claude": "<대상> 장면 전환에 데이터모시 전환을 넣어줘. 이전 장면을 가로 띠 42개로 복제해 각 띠를 시드 해시로 정한 -150에서 +150px 사이 거리로 0.6초 동안 linear로 밀고, 0.4초부터 0.2초간 opacity를 0으로 낮춰. 이전 프레임 잔해 레이어는 opacity 0.62까지 올렸다 내려. 난수는 시드 고정 해시만 쓰고 paused 타임라인으로 seek 가능하게 해.", "codex": "<파일>에 데이터모시 전환을 구현해. 띠 42개 x=(h(i)-0.5)*300, duration 0.6 linear, 0.4초부터 opacity 0. .residue는 0.62까지 올라갔다 내려오는 yoyo. 0.15초, 0.3초, 0.45초, 0.6초 시점을 캡처해 띠마다 밀림이 다른지, 0.6초에 잔해와 띠가 남지 않는지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/StripDatamoshGlitch.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "StripDatamoshGlitch", "Strip datamosh" ], "oneLinerEn": "Horizontal strips and vertical slits tear, old footage residue smears, and the next screen takes over.", "conveysEn": "Produces the friction and destruction of corrupted compressed video.", "useWhenEn": [ "In music videos, games, and experimental pieces when tearing between scenes", "On a shocking reversal cut that needs a collapsing feel" ], "promptsEn": { "claude": "Add a Datamosh Transition to . Duplicate the previous scene into 42 horizontal strips; push each by a seeded-hash offset between -150 and +150px over 0.6s with linear ease, and fade each out over 0.2s starting at 0.4s. Bring a residue layer up to opacity 0.62 and back. Use only a seeded hash for randomness and keep it on a paused, seekable GSAP timeline.", "codex": "Implement Datamosh Transition in . 42 strips with x=(h(i)-0.5)*300, duration 0.6 linear, opacity to 0 from 0.4s. .residue yoyos to 0.62 and back. Capture at 0.15s, 0.3s, 0.45s, and 0.6s to confirm each strip is displaced differently and that no residue or strips remain at 0.6s." }, "render": "planned", "files": {}, "no": "155" }, { "slug": "edge-trace-transition", "family": "transitions", "ko": "윤곽 추출 스타일 전환", "en": "Edge Trace Stylization", "aka": [ "Find Edges" ], "oneLiner": "사진의 면이 사라지고 경계선만 남은 선화로 바뀌는 윤곽 추출 전환", "conveys": "사진에서 구조로. 형태의 뼈대가 드러나 도해적이고 분석적인 인상을 준다", "purposes": [ "전환", "설명" ], "media": [ "설명 영상", "발표", "제품 시연" ], "level": "고급", "useWhen": [ "제품 사진에서 구조 도해 설명으로 넘어갈 때", "실사 이미지를 설계도나 스케치 스타일로 바꿔 보여 줄 때" ], "goodExample": "제품 사진이 0.8초 동안 색이 빠지며 윤곽선만 남은 선화로 바뀌고 그 위에 주석이 붙는다", "badExample": "임계값이 너무 낮아 노이즈 선이 화면을 덮거나, 임계값이 높아 윤곽이 끊겨 형태를 알 수 없다", "avoid": [ "edge threshold 0.08 미만 금지(노이즈 과다)", "선화 상태에서 최소 0.6초 정지해 읽게 한다" ], "params": [ { "name": "지속", "default": "0.8s", "range": "0.5~1.2s", "note": "사진에서 선화까지" }, { "name": "경계 임계값", "default": "0.15", "range": "0.1~0.25", "note": "Sobel 출력 컷" }, { "name": "혼합", "default": "0 to 1", "range": "0~1", "note": "원본색과 선화 mix" }, { "name": "선 색", "default": "#111827", "range": "고정", "note": "흰 바탕 기준" }, { "name": "선 두께", "default": "1.5px", "range": "1~2.5px", "note": "Sobel 샘플 간격" } ], "ease": "power2.inOut", "snippet": "const u = { m: 0 };\ntl.to(u, { m: 1, duration: 0.8, ease: 'power2.inOut', onUpdate: draw }, 0.3);\n// GLSL: float g = length(vec2(sobelX(tex, uv), sobelY(tex, uv)));\n// float line = smoothstep(0.15, 0.30, g);\n// col = mix(texture(tex, uv).rgb, mix(vec3(1.0), ink, line), m);", "pairsWith": [ "outline-then-fill", "outline-dissolve", "shape-morph", "line-draw" ], "engines": { "hyperframes": "m을 paused 타임라인에서 tween하고 Sobel은 원본 텍스처만 읽으니 상태가 없다. 텍스처 로드가 끝난 뒤 첫 프레임을 그린다", "reelforge": "씬 브리프에 이미지 경로, 임계값 0.15, 전환 0.8초, 선 색과 종이색, 선화 정지 0.6초를 싣는다", "scrolline": "진행률 0~0.5에 m 0→1을 걸고 이후 홀드한다. 스크럽에서 선이 깜빡이지 않도록 임계 범위를 0.15~0.30으로 넓힌다" }, "prompts": { "claude": "<대상> 사진에 윤곽 추출 전환을 넣어줘. 0.3초에 시작해 0.8초 동안 power2.inOut으로 원본에서 Sobel 경계선 그림(흰 바탕, 진한 남색 선 #111827)으로 mix가 0에서 1이 되게 해. 임계값은 smoothstep(0.15, 0.30)이고 선화 상태로 0.6초 정지한 뒤 주석 텍스트가 나오게 해.", "codex": "<파일>의 이미지 캔버스에 edge 셰이더를 추가해. g=length(vec2(sobelX,sobelY)), line=smoothstep(0.15,0.30,g), col=mix(tex, mix(white, ink, line), m). m은 0.3초부터 0.8초 power2.inOut로 0→1. 0.5초·0.9초·1.8초 캡처로 색이 빠지며 윤곽만 남고 노이즈 선이 화면을 덮지 않는지 확인해." }, "sources": [ { "title": "helpx.adobe.com", "url": "https://helpx.adobe.com/after-effects/desktop/apply-effects-and-animation-presets/list-of-effects/stylize-effects.html", "license": "unknown" } ], "runtime": "webgl", "variants": [ "밝은 윤곽", "어두운 윤곽" ], "oneLinerEn": "The photo's surfaces vanish, leaving only edges as a line drawing.", "conveysEn": "From photo to structure. The skeleton of the form is exposed, giving a diagrammatic, analytical impression.", "useWhenEn": [ "Move from a product photo to a structural explanation.", "Turn a realistic image into a blueprint or sketch look." ], "promptsEn": { "claude": "Add an edge trace transition to the photo in . Starting at 0.3 seconds, mix from the original to a Sobel edge drawing (white paper, dark navy lines #111827) from 0 to 1 over 0.8 seconds with power2.inOut. Threshold smoothstep(0.15, 0.30). Hold the line drawing for 0.6 seconds, then bring in annotation text.", "codex": "Add an edge shader to the image canvas in . g=length(vec2(sobelX,sobelY)); line=smoothstep(0.15,0.30,g); col=mix(tex, mix(white, ink, line), m). Tween m 0 to 1 from 0.3 s over 0.8 s power2.inOut. Capture 0.5 s, 0.9 s and 1.8 s to confirm color drains, only outlines remain, and noise lines do not flood the frame." }, "render": "planned", "files": {}, "no": "156" }, { "slug": "exit-before-enter", "family": "transitions", "ko": "퇴장 후 등장", "en": "Exit Before Enter", "aka": [ "Exit before enter" ], "oneLiner": "이전 요소가 완전히 사라진 다음에야 새 요소가 나타나는 순차 교체", "conveys": "상태가 순서대로 바뀐다는 분명함. 두 요소가 겹쳐 흐트러지지 않는다", "purposes": [ "전환", "순서·흐름" ], "media": [ "웹 UI", "제품 시연", "설명 영상" ], "level": "기본", "useWhen": [ "같은 자리에서 카드, 문구, 탭 내용을 바꿀 때", "두 요소가 겹치면 읽기 어려운 텍스트 교체에서" ], "goodExample": "이전 문구가 0.25초 동안 위로 12px 빠지며 사라지고, 완전히 사라진 뒤 새 문구가 아래에서 12px 올라오며 0.4초 동안 나타난다", "badExample": "퇴장이 끝나기 전에 등장이 시작되어 글자가 겹치거나, 퇴장에 0.8초 넘게 걸려 답답하다", "avoid": [ "퇴장이 끝나기 전에 등장을 시작하지 않는다", "총 소요 0.8초 초과 금지(UI에서는 0.65초 이내)" ], "params": [ { "name": "퇴장", "default": "0.25s", "range": "0.15~0.35s", "note": "opacity와 y 이동" }, { "name": "등장", "default": "0.4s", "range": "0.25~0.55s", "note": "퇴장보다 길게" }, { "name": "이동 거리", "default": "12px", "range": "8~24px", "note": "방향은 일관되게" }, { "name": "대기", "default": "0s", "range": "0~0.08s", "note": "퇴장과 등장 사이" } ], "ease": "power2.out", "snippet": "tl.to('.old', { opacity: 0, y: -12, duration: 0.25, ease: 'power2.in' })\n .set('.old', { display: 'none' })\n .fromTo('.new', { opacity: 0, y: 12 },\n { opacity: 1, y: 0, duration: 0.4, ease: 'power2.out' });", "pairsWith": [ "crossfade", "dip-to-color", "stagger" ], "engines": { "hyperframes": "타임라인에서 퇴장 tween 다음에 등장 tween을 순서대로 붙인다(position을 생략). display 전환도 타임라인에 넣어 seek에서 남지 않게 한다", "reelforge": "씬 워커 브리프에 exitMs, enterMs, gapMs, offsetPx를 노출한다. 텍스트 교체 비트에 반복 사용한다", "scrolline": "진행률 0~0.4 퇴장, 0.4~1 등장으로 분할한다. 겹침이 없어야 하므로 경계에서 opacity가 정확히 0이 되게 한다" }, "prompts": { "claude": "<이전 요소>를 <새 요소>로 교체하는데 exit-before-enter로 해줘. 이전 요소는 0.25초 동안 opacity 0, y -12px (power2.in)로 빠지고 display none, 그 직후 새 요소가 opacity 0, y +12px에서 0.4초 동안 나타나게 해 (power2.out). 두 요소가 동시에 보이는 프레임이 없어야 하고 paused 타임라인 하나로 만들어.", "codex": "<파일>의 텍스트 교체에 exit-before-enter를 적용해. .old opacity 0과 y -12px (0.25s, power2.in) 뒤 display none, .new opacity 0과 y 12px에서 원위치 (0.4s, power2.out)를 순서대로 붙인다. 0.25초 시점에 둘 다 안 보이는 프레임이 있는지, 0.2초와 0.3초에 두 요소가 동시에 보이지 않는지 캡처로 확인해." }, "sources": [ { "title": "motiondivision/motion", "url": "https://motion.dev/docs/react-animate-presence", "license": "MIT" }, { "title": "pmndrs/react-spring", "url": "https://www.react-spring.dev/docs/components/use-transition", "license": "MIT" }, { "title": "motion.dev examples", "url": "https://motion.dev/examples/react-animate-presence-modes", "license": "unknown" } ], "runtime": "gsap", "variants": [ "대기 전환", "동시 전환", "순차 문구 교체" ], "oneLinerEn": "The previous element fully disappears before the new one appears.", "conveysEn": "A clear, sequential change of state, with no overlap to muddy the read.", "useWhenEn": [ "Change the content of a card, line of text, or tab in the same spot.", "Replace text where overlapping elements would be hard to read." ], "promptsEn": { "claude": "Replace with using exit-before-enter. The old element fades to opacity 0 and moves y -12px over 0.25 seconds (power2.in), then display none. Right after, the new element goes from opacity 0 and y +12px to rest over 0.4 seconds (power2.out). No frame should show both elements, and everything sits in one paused timeline.", "codex": "Apply exit-before-enter to the text swap in . Chain .old opacity 0 and y -12px (0.25s, power2.in) then display none, followed by .new from opacity 0 and y 12px to rest (0.4s, power2.out). Capture at 0.2 and 0.3 seconds to confirm both elements are never visible together, and check the gap frame at 0.25 seconds." }, "render": "planned", "files": {}, "no": "157" }, { "slug": "eyeline-match", "family": "transitions", "ko": "시선 연결 컷", "en": "Eyeline Match", "aka": [ "Eyeline cut" ], "oneLiner": "인물이 화면 밖을 보는 장면 다음에 그 시선 방향의 대상이 나타난다", "conveys": "인물이 화면 밖을 본 다음, 그 시선 방향의 대상이 나온다. 누가 무엇을 보는지 알린다", "purposes": [ "설명", "순서·흐름" ], "media": [ "설명 영상", "숏폼", "제품 시연" ], "level": "기본", "useWhen": [ "인물이 화면 밖을 보다가 그가 본 대상으로 이어질 때", "사용자가 위를 올려다본 뒤 화면 위쪽의 알림이나 메뉴를 보여 줄 때" ], "goodExample": "인물이 화면 오른쪽 위를 1.0초 보고 0ms로 컷하면 같은 방향에 있는 대상이 나타나 1.0초 유지된다", "badExample": "시선 방향과 대상 위치가 반대라 눈이 어색하거나, 시선이 머무는 시간이 너무 짧아 관계가 연결되지 않는다", "avoid": [ "시선 방향과 대상의 화면 위치를 같은 쪽으로 둔다", "시선 유지는 0.8초 이상" ], "params": [ { "name": "전환 길이", "default": "0ms", "range": "고정", "note": "즉시" }, { "name": "시선 유지", "default": "1000ms", "range": "800~1500ms", "note": "컷 전" }, { "name": "대상 유지", "default": "1000ms", "range": "800~1400ms", "note": "컷 후" }, { "name": "시선 방향", "default": "오른쪽 위 30도", "range": "15~45도", "note": "대상이 있는 쪽" }, { "name": "대상 위치", "default": "화면 오른쪽 위", "range": "", "note": "시선과 같은 방향" } ], "ease": "none", "snippet": "tl.fromTo('.gaze', { x: 0 }, { x: 24, duration: 0.5, ease: 'power2.out' }, 0.2); // 시선 이동\ntl.set('.person', { autoAlpha: 0 }, 1.0);\ntl.set('.target', { autoAlpha: 1 }, 1.0);\ntl.from('.target', { scale: 1.06, duration: 0.6, ease: 'power2.out' }, 1.0);", "pairsWith": [ "reaction-cut", "shot-reverse-shot", "focus-handoff" ], "engines": { "hyperframes": "시선 이동과 컷을 한 타임라인에 두고 컷 시각 1.0초에 tl.set으로 교체한다. 시선 벡터와 대상 위치를 같은 방향으로 배치한다", "reelforge": "씬 워커 브리프에 시선 방향(오른쪽 위 30도), 유지 시간, 대상 위치를 싣는다", "scrolline": "scrub에서는 시선 이동은 진행률 0~0.4, 컷은 0.5에서 즉시 교체. 대상에는 ease-out 확대만 넣는다" }, "prompts": { "claude": "<대상> 영상에 시선 연결 컷을 넣어줘. 0.2초에서 0.7초까지 인물의 시선을 오른쪽 위 방향으로 옮기고 1.0초에 tl.set으로 대상 장면 .target으로 컷해. 대상은 화면 오른쪽 위에 위치하고 scale 1.06에서 1로 0.6초 power2.out. 컷 뒤 1.0초 유지해.", "codex": "<파일>에 시선 연결 컷을 구현해. .gaze x 0에서 24 (0.2~0.7초), 1.0초에 .person 숨김, .target 표시, .target scale 1.06에서 1로 0.6초 power2.out. 0.5초, 0.95초, 1.05초, 1.8초 시점을 캡처해 컷 전후로 시선 방향과 대상 위치가 같은 쪽인지 확인해." }, "sources": [ { "title": "Adobe", "url": "https://www.adobe.com/creativecloud/video/post-production/cuts-in-film.html", "license": "unknown" }, { "title": "Adobe", "url": "https://www.adobe.com/creativecloud/video/hub/ideas/what-is-continuity-editing-in-film.html", "license": "unknown" } ], "runtime": "gsap", "variants": [ "Eyeline cut" ], "oneLinerEn": "After a person looks off-screen, the object in that direction appears.", "conveysEn": "Lets viewers understand who is looking at what.", "useWhenEn": [ "When a person looks off-screen and the shot moves to what they saw", "When a user looks up and the alert or menu at the top of the screen is shown next" ], "promptsEn": { "claude": "Add an Eyeline Match to . Move the character gaze toward the upper right from 0.2s to 0.7s, then cut to the target scene .target at 1.0s using tl.set. Place the target in the upper right of the frame and ease it from scale 1.06 to 1 over 0.6s with power2.out. Hold 1.0s after the cut.", "codex": "Implement Eyeline Match in . .gaze x 0 to 24 (0.2 to 0.7s); at 1.0s hide .person and show .target; .target scale 1.06 to 1 over 0.6s power2.out. Capture at 0.5s, 0.95s, 1.05s, and 1.8s to confirm that gaze direction and target position are on the same side across the cut." }, "render": "planned", "files": {}, "no": "158" }, { "slug": "fly-eye-transition", "family": "transitions", "ko": "플라이 아이 전환", "en": "Fly Eye Transition", "aka": [ "Fly eye mosaic", "곤충 눈 굴절" ], "oneLiner": "영상이 작은 렌즈들로 쪼개져 확대 굴절되고 색이 분리되며 교차하는 전환", "conveys": "복안과 유리 표면 같은 낯선 시야. 프리즘을 통해 보는 듯한 굴절", "purposes": [ "전환", "주목 끌기" ], "media": [ "숏폼", "제품 시연" ], "level": "고급", "useWhen": [ "실험적이고 기술적인 톤의 영상에서 개성 있는 전환이 필요할 때", "유리, 렌즈, 곤충 같은 주제 연출에서" ], "goodExample": "0.8초 동안 화면이 셀 크기 0.04의 렌즈 격자로 나뉘어 zoom 50까지 굴절하고 색 분리 0.3이 생겼다가 뒤 장면으로 풀린다", "badExample": "셀이 너무 작아 노이즈처럼 보이거나, 굴절 강도가 커서 중심 피사체를 알아볼 수 없다", "avoid": [ "셀 크기 0.02 미만 금지", "굴절 정점은 0.15초 이내" ], "params": [ { "name": "전체 지속", "default": "0.8s", "range": "0.6~1.2s", "note": "선형" }, { "name": "셀 크기", "default": "0.04", "range": "0.03~0.08", "note": "화면 비율" }, { "name": "zoom", "default": "50", "range": "20~60", "note": "굴절 강도" }, { "name": "색 분리", "default": "0.3", "range": "0~0.4", "note": "RGB 오프셋" } ], "ease": "none", "snippet": "const u = { p: 0 };\ntl.to(u, { p: 1, duration: 0.8, ease: 'none', onUpdate: () => {\n const k = Math.sin(Math.PI * u.p);\n eye.set({ size: 0.04, zoom: 50 * k, sep: 0.3 * k, mix: u.p });\n} });", "pairsWith": [ "kaleidoscope-transition", "mosaic-traverse", "chromatic-wipe" ], "engines": { "hyperframes": "셰이더 uniform을 진행값 하나에서 계산한다. 셀 격자는 uv를 고정 크기로 반복해 seed 없이 결정론적이다", "reelforge": "씬 워커 브리프에 cellSize, zoom, colorSep, durationMs를 싣는다", "scrolline": "진행률 삼각파 sin(pi p)로 굴절과 색 분리를 주고 mix는 p로 선형" }, "prompts": { "claude": "<대상A>에서 <대상B>로 플라이 아이 전환을 만들어줘. 화면을 셀 크기 0.04의 렌즈 격자로 나누고 0.8초 동안 진행값 p를 linear로 0에서 1로 올리면서 굴절 zoom을 50*sin(pi*p), 색 분리를 0.3*sin(pi*p), 혼합을 p로 걸어. 값은 상태 객체 하나에서 읽고 paused 타임라인에서 seek되게 해.", "codex": "<파일>에 fly eye transition을 적용해. p 0에서 1을 0.8s ease none, 셰이더에서 cell=0.04, zoom=50*sin(pi p), colorSep=0.3*sin(pi p), mix(A,B,p). 0.4초 캡처에서 렌즈 셀과 색 분리가 최대인지, 0초와 0.8초에 굴절이 0인지 확인해." }, "sources": [ { "title": "gl-transitions/gl-transitions", "url": "https://github.com/gl-transitions/gl-transitions/blob/master/transitions/flyeye.glsl", "license": "MIT" }, { "title": "mifi/editly", "url": "https://github.com/mifi/editly#transition-types", "license": "MIT" } ], "runtime": "webgl", "variants": [ "flyeye", "Fly eye mosaic" ], "oneLinerEn": "The frame breaks into tiny lenses that magnify and refract, split colors, and cross into the next scene.", "conveysEn": "An unfamiliar view like a compound eye or glass surface: refraction as through a prism.", "useWhenEn": [ "Give an experimental, technical video a distinctive transition.", "Themes such as glass, lenses, or insects." ], "promptsEn": { "claude": "Build a fly-eye transition from to . Divide the frame into a lens grid with cell size 0.04, ramp progress p from 0 to 1 linearly over 0.8 seconds, and apply refraction zoom 50*sin(pi*p), color separation 0.3*sin(pi*p), and blend by p. Read values from one state object in a paused timeline that seeks.", "codex": "Apply a fly eye transition in . Tween p 0 to 1 over 0.8s with ease none; in the shader use cell=0.04, zoom=50*sin(pi p), colorSep=0.3*sin(pi p), mix(A,B,p). Capture at 0.4 seconds to confirm lens cells and color separation peak, and at 0 and 0.8 seconds to confirm zero refraction." }, "render": "planned", "files": {}, "no": "159" }, { "slug": "frame-border-transition", "family": "transitions", "ko": "프레임 테두리 전환", "en": "Frame Border Transition", "aka": [ "Frame border transition" ], "oneLiner": "두꺼운 프레임이나 테두리가 이동하거나 커져 컷을 가리고 새 화면을 둘러싼다", "conveys": "두꺼운 테두리가 움직이거나 커져 컷을 가리고 새 화면을 감싼다. 브랜드 프레임 안에서 장면이 바뀐다", "purposes": [ "전환", "브랜딩" ], "media": [ "설명 영상", "제품 시연", "발표" ], "level": "기본", "useWhen": [ "브랜드 색 프레임이 화면을 채우다 열리며 다음 장면이 들어올 때", "시리즈 영상에서 같은 프레임 모티프로 코너 전환을 통일할 때" ], "goodExample": "80px 두께의 브랜드 색 테두리가 650ms 동안 안쪽으로 조여들어 중앙 컷을 가린 뒤, 다시 바깥으로 벌어지며 새 장면을 감싼다", "badExample": "테두리가 컷 시점에 화면을 덮지 못해 컷이 그대로 보이거나, 두께가 20px 미만이라 프레임으로 읽히지 않는다", "avoid": [ "컷 시점에 테두리가 화면을 완전히 덮게 한다", "브랜드 색 하나만 쓴다" ], "params": [ { "name": "지속", "default": "650ms", "range": "500~850ms", "note": "easeInOutCubic" }, { "name": "두께", "default": "80px", "range": "60~120px", "note": "최종 프레임 두께" }, { "name": "컷 시점", "default": "325ms", "range": "", "note": "테두리 덮음" }, { "name": "색", "default": "브랜드 단색", "range": "", "note": "대비 확보" }, { "name": "모서리 반경", "default": "0 또는 24px", "range": "", "note": "브랜드 문법" } ], "ease": "power3.inOut", "snippet": "tl.fromTo('.frame-fill', { scale: 1.6, opacity: 1 }, { scale: 1, duration: 0.325, ease: 'power3.in' }, 0);\ntl.set('.prev', { autoAlpha: 0 }, 0.325).set('.next', { autoAlpha: 1 }, 0.325);\ntl.to('.frame-fill', { scale: 1.6, duration: 0.325, ease: 'power3.out' }, 0.325);\ntl.to('.frame-ring', { borderWidth: 80, duration: 0.325, ease: 'power3.out' }, 0.325);", "pairsWith": [ "overlay-bridge", "iris-mask", "wipe" ], "engines": { "hyperframes": "프레임은 SVG rect의 stroke-width와 scale을 보간한다. 컷은 프레임이 완전히 화면을 덮는 0.325초에 tl.set", "reelforge": "씬 워커 브리프에 브랜드 색 hex, 두께 80px, 컷 시점 325ms를 싣는다", "scrolline": "scrub에서는 진행률 0~0.5에 조여들기, 0.5~1에 벌어짐. 컷은 0.5에서 교체하고 프레임은 ease-out만 쓴다" }, "prompts": { "claude": "<대상> 장면 전환에 프레임 테두리 전환을 넣어줘. 브랜드 색 사각 테두리(.frame-fill)가 scale 1.6에서 1로 0.325초 power3.in으로 조여들어 화면을 덮고, 0.325초에 장면을 교체한 뒤 다시 scale 1.6으로 0.325초 power3.out으로 벌어져 새 화면을 감싸게 해. 최종 테두리 두께는 80px. paused 타임라인으로 seek 가능하게 해.", "codex": "<파일>에 프레임 테두리 전환을 구현해. .frame-fill scale 1.6에서 1 (0.325초 power3.in), 0.325초에 장면 교체, scale 1에서 1.6 (0.325초 power3.out), .frame-ring borderWidth 80. 0.16초, 0.325초, 0.49초, 0.65초 시점을 캡처해 0.325초에 화면이 완전히 덮이는지, 0.65초에 80px 프레임이 남는지 확인해." }, "sources": [ { "title": "Adobe", "url": "https://helpx.adobe.com/premiere/desktop/add-video-effects/types-of-effects/transitions.html", "license": "unknown" } ], "runtime": "svg", "variants": [], "oneLinerEn": "A thick frame or border moves or grows to hide the cut and enclose the new screen.", "conveysEn": "Makes viewers feel scenes change inside a branded frame.", "useWhenEn": [ "When a brand-colored frame fills the screen and opens onto the next scene", "To unify corner transitions in a series with one frame motif" ], "promptsEn": { "claude": "Add a Frame Border Transition to . A brand-colored rectangular border (.frame-fill) closes in from scale 1.6 to 1 over 0.325s with power3.in, covering the frame. Swap scenes at 0.325s, then expand it back to scale 1.6 over 0.325s with power3.out, leaving an 80px border around the new scene. Paused, seekable timeline.", "codex": "Implement Frame Border Transition in . .frame-fill scale 1.6 to 1 (0.325s power3.in), swap scenes at 0.325s, then scale 1 to 1.6 (0.325s power3.out); .frame-ring borderWidth 80. Capture at 0.16s, 0.325s, 0.49s, and 0.65s to confirm full coverage at 0.325s and an 80px frame left at 0.65s." }, "render": "planned", "files": {}, "no": "160" }, { "slug": "freeze-cut", "family": "transitions", "ko": "프리즈 컷", "en": "Freeze Cut", "aka": [ "Freeze punctuated cut", "정지 프레임 구두점 컷" ], "oneLiner": "동작의 한 순간이 멈춰 짧게 유지된 뒤 다른 장면으로 바뀐다", "conveys": "동작의 한 순간이 멈춘 뒤 다른 장면으로 바뀐다. 핵심 순간과 문단 끝을 강조한다", "purposes": [ "강조", "전환" ], "media": [ "숏폼", "설명 영상", "제품 시연" ], "level": "기본", "useWhen": [ "결과 순간(골, 완료, 성공)에서 화면을 멈추고 자막이나 설명을 붙일 때", "섹션의 마지막 장면을 정지 프레임으로 끝내 문단이 끝났다고 알릴 때" ], "goodExample": "동작 중 2.0초 프레임을 정지해 400ms 유지한 뒤 0ms 컷으로 다음 장면으로 넘어간다. 정지 순간에 살짝 확대가 들어간다", "badExample": "정지 프레임이 1초를 넘어 재생이 고장난 것처럼 보이거나, 정지 프레임이 흐릿해 핵심 순간이 아니다", "avoid": [ "정지는 300~600ms로 한다", "정지 프레임은 동작 정점(가장 읽기 좋은 자세)에서 뽑는다" ], "params": [ { "name": "정지 유지", "default": "400ms", "range": "300~600ms", "note": "컷 전" }, { "name": "컷", "default": "0ms", "range": "고정", "note": "즉시" }, { "name": "확대", "default": "1.0에서 1.04", "range": "1.02~1.06", "note": "정지 동안 미세 확대" }, { "name": "정지 시각", "default": "동작 정점", "range": "", "note": "읽기 좋은 프레임" }, { "name": "흑백 처리", "default": "saturation 0.6", "range": "0.4~1", "note": "선택 사항" } ], "ease": "none", "snippet": "tl.call(() => drawFrameTo('.freeze', 2.0), null, 2.0); // canvas에 2.0초 프레임 복사\ntl.set('.freeze', { autoAlpha: 1 }, 2.0).fromTo('.freeze', { scale: 1 }, { scale: 1.04, duration: 0.4, ease: 'none' }, 2.0);\ntl.set('.freeze', { autoAlpha: 0 }, 2.4).set('.next', { autoAlpha: 1 }, 2.4);", "pairsWith": [ "smash-cut", "speed-ramp", "jump-cut" ], "engines": { "hyperframes": "프레임 복사는 seek 후 정확한 시각의 프레임을 canvas에 그리도록 구현한다. 영상 재생 중이 아니라 시각 기준으로 그려야 결정론적이다", "reelforge": "씬 워커 브리프에 정지 시각 2.0초, 유지 400ms, 확대 4%를 싣는다", "scrolline": "scrub에서는 진행률 임계값 이전 프레임을 정지 프레임으로 고정하고 그 구간 진행률을 소비한다" }, "prompts": { "claude": "<대상> 영상 2.0초에 프리즈 컷을 넣어줘. 그 시각의 프레임을 canvas(.freeze)에 복사해 0.4초 동안 표시하면서 scale 1에서 1.04로 ease none으로 서서히 확대하고, 2.4초에 .next로 즉시 컷해. 프레임 복사는 seek 후 시각 기준으로 그려 결정론을 유지해.", "codex": "<파일>에 프리즈 컷을 구현해. 2.0초에 drawFrameTo(.freeze, 2.0), .freeze scale 1에서 1.04를 0.4초 none, 2.4초에 .freeze 숨김 및 .next 표시. 1.95초, 2.2초, 2.4초, 2.6초 시점을 캡처해 정지 동안 영상이 움직이지 않는지, 2.4초에 컷 전환이 즉시인지 확인해." }, "sources": [ { "title": "remotion-dev/remotion", "url": "https://www.remotion.dev/docs/freeze", "license": "Remotion License" } ], "runtime": "canvas", "variants": [ "Freeze punctuated cut" ], "oneLinerEn": "A moment of action freezes and holds briefly before the scene changes.", "conveysEn": "Emphasizes a key moment or the end of a paragraph.", "useWhenEn": [ "To freeze on a result moment (a goal, completion, success) and add a caption", "To end a section on a freeze frame that signals the paragraph is done" ], "promptsEn": { "claude": "Add a Freeze Cut to at 2.0s. Copy the frame at that time to a canvas (.freeze), show it for 0.4s while slowly scaling from 1 to 1.04 with ease none, and cut to .next at 2.4s. Draw the frame from the seeked time so it stays deterministic.", "codex": "Implement Freeze Cut in . At 2.0s call drawFrameTo(.freeze, 2.0); .freeze scale 1 to 1.04 over 0.4s ease none; at 2.4s hide .freeze and show .next. Capture at 1.95s, 2.2s, 2.4s, and 2.6s to confirm nothing moves during the freeze and that the cut at 2.4s is instant." }, "render": "planned", "files": {}, "no": "161" }, { "slug": "freeze-frame-dressing", "family": "transitions", "ko": "프리즈 프레임 장식", "en": "Freeze Frame Dressing", "aka": [ "Freeze frame dressing", "정지 화면 장식" ], "oneLiner": "움직임이 박자에 멈추고 종이, 테이프, 라벨이 정지된 인물 주변에 붙는 연출", "conveys": "이 순간이 중요하다는 표시. 잡지 표지나 다큐 인트로의 소개 컷", "purposes": [ "강조", "브랜딩" ], "media": [ "숏폼", "설명 영상", "발표" ], "level": "중급", "useWhen": [ "인물이나 제품을 처음 소개하는 컷에서", "결정적 순간을 기억에 남기고 싶을 때" ], "goodExample": "박자에서 영상이 멈추고 80ms 간격으로 테이프, 종이 조각, 라벨이 붙어 1초 유지된 뒤 재생이 이어진다", "badExample": "정지 시간이 3초 넘게 이어져 영상이 멈춘 것처럼 보이거나, 장식이 인물 얼굴을 가린다", "avoid": [ "정지 유지 1.5초 초과 금지", "장식은 피사체 얼굴 영역을 침범하지 않는다" ], "params": [ { "name": "정지 시점", "default": "0ms", "range": "비트에 맞춤", "note": "프레임 고정" }, { "name": "유지", "default": "1000ms", "range": "600~1500ms", "note": "장식 완성 후 홀드" }, { "name": "장식 시작차", "default": "80ms", "range": "50~120ms", "note": "순서대로 붙임" }, { "name": "장식 진입", "default": "0.25s", "range": "0.15~0.35s", "note": "살짝 회전 -3~3도" } ], "ease": "back.out(1.6)", "snippet": "tl.set('.video', { display: 'none' }, 0).set('.still', { display: 'block' }, 0)\n .from('.deco', { scale: 0.6, rotation: -6, opacity: 0, y: -24,\n duration: 0.25, ease: 'back.out(1.6)', stagger: 0.08 }, 0.05);", "pairsWith": [ "stagger", "spring-overshoot", "strobe-flash" ], "engines": { "hyperframes": "정지 프레임은 이미지로 미리 뽑아 교체한다(비디오 seek 오차 회피). 장식은 transform과 opacity만 움직인다", "reelforge": "씬 워커 브리프에 freezeSec, holdMs, decoStaggerMs, decoAssets를 싣는다", "scrolline": "진행률 구간 하나를 정지 구간으로 정하고 장식 진입을 그 안의 진행률 0.02 간격으로 둔다. scrub에서는 back 대신 power3.out 사용" }, "prompts": { "claude": "<영상>의 <시각> 초에 프리즈 프레임 장식을 넣어줘. 그 프레임을 정지 이미지로 교체하고, 테이프, 종이, 라벨 3개 요소가 80ms 간격으로 scale 0.6에서 1, rotation -6도에서 0, y -24px에서 0으로 0.25초 동안 back.out(1.6)으로 붙게 해. 1초 홀드 뒤 재생 재개, paused 타임라인 하나로 만들어.", "codex": "<파일>의 <시각> 초에 freeze-frame dressing을 적용해. 그 프레임을 이미지로 고정하고 .deco 3개를 scale 0.6에서 1, rotation -6도에서 0, opacity 0에서 1로 0.25s back.out(1.6), stagger 0.08s로 등장시킨다. 0.05초, 0.5초, 1.2초 캡처로 정지 여부와 장식 순서, 얼굴 가림이 없는지 확인해." }, "sources": [ { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/freeze-frame-dressing/registry-item.json", "license": "Apache-2.0" }, { "title": "heygen-com/hyperframes", "url": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/blocks/beat-freeze-cut/registry-item.json", "license": "Apache-2.0" }, { "title": "local/HyperFrames-skills", "url": "claude-skill:music-to-video/references/motion-primitive-catalog.md", "license": "unknown" } ], "runtime": "gsap", "variants": [ "정지 히트", "종이 테이프", "인물 분리" ], "oneLinerEn": "Motion freezes on the beat and paper, tape, and labels stick around the frozen subject.", "conveysEn": "A marker that says this moment matters: a magazine cover or documentary intro shot.", "useWhenEn": [ "Introduce a person or product for the first time.", "Make a decisive moment memorable." ], "promptsEn": { "claude": "Add freeze-frame dressing to