Skip to content

Commit 2c4fe08

Browse files
committed
Add smart transition to keyframe generator
Implement keyframe generation logic that converts Figma's SMART_ANIMATE transitions to CSS keyframe animations. Features: - New keyframe.ts module with comprehensive keyframe generation functions - Enhanced getSelectorProps() with optional keyframe generation - generateKeyframeFromTransition() for single transition conversion - generateKeyframesForEffects() for multiple effect handling - Smart animation name generation based on effect and properties - Full unit test coverage (14 tests, 100% function coverage) - Comprehensive usage documentation in KEYFRAME_USAGE.md The feature is opt-in via useKeyframes option, maintaining backward compatibility with existing CSS transition behavior.
1 parent 4b8a5fd commit 2c4fe08

File tree

4 files changed

+788
-15
lines changed

4 files changed

+788
-15
lines changed

KEYFRAME_USAGE.md

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
# Smart Transition to Keyframe Generator
2+
3+
이 기능은 Figma의 reactions(상호작용) 속성에서 SMART_ANIMATE 트랜지션을 감지하고 CSS 키프레임 애니메이션으로 변환합니다.
4+
5+
## 개요
6+
7+
Figma에서 컴포넌트 세트(Component Set)의 variants 간에 SMART_ANIMATE 트랜지션을 설정하면, 이를 다음 두 가지 방식으로 변환할 수 있습니다:
8+
9+
1. **CSS Transition** (기본값): hover, active 등의 상태 변화 시 자동으로 적용되는 트랜지션
10+
2. **CSS Keyframe Animation** (새로운 기능): 더 세밀한 제어가 가능한 키프레임 애니메이션
11+
12+
## 사용 방법
13+
14+
### 1. 기본 사용 (CSS Transition)
15+
16+
```typescript
17+
import { getSelectorProps } from './codegen/props/selector'
18+
19+
// 기본 동작 - CSS transition 생성
20+
const result = await getSelectorProps(componentSetNode)
21+
22+
// 결과:
23+
// {
24+
// props: {
25+
// _hover: { opacity: "0.8" },
26+
// transition: "300ms ease-in-out",
27+
// transitionProperty: "opacity"
28+
// },
29+
// variants: { ... }
30+
// }
31+
```
32+
33+
### 2. 키프레임 애니메이션 사용
34+
35+
```typescript
36+
import { getSelectorProps } from './codegen/props/selector'
37+
38+
// useKeyframes 옵션 활성화
39+
const result = await getSelectorProps(componentSetNode, { useKeyframes: true })
40+
41+
// 결과:
42+
// {
43+
// props: {
44+
// _hover: { opacity: "0.8" }
45+
// },
46+
// variants: { ... },
47+
// keyframes: [
48+
// {
49+
// name: "hover-animation-abc123",
50+
// keyframes: "@keyframes hover-animation-abc123 { ... }",
51+
// animation: "hover-animation-abc123 300ms ease-in-out forwards",
52+
// properties: ["opacity"]
53+
// }
54+
// ]
55+
// }
56+
```
57+
58+
## 키프레임 모듈 API
59+
60+
### `generateKeyframeFromTransition()`
61+
62+
단일 트랜지션에서 키프레임 애니메이션을 생성합니다.
63+
64+
```typescript
65+
import { generateKeyframeFromTransition } from './codegen/props/keyframe'
66+
67+
const keyframe = generateKeyframeFromTransition(
68+
{ opacity: '1' }, // defaultProps - 시작 상태
69+
{ opacity: '0.8' }, // targetProps - 끝 상태
70+
{ // transition - Figma 트랜지션 객체
71+
type: 'SMART_ANIMATE',
72+
duration: 0.3, // 초 단위
73+
easing: { type: 'EASE_IN_OUT' }
74+
},
75+
'hover', // effect - 효과 타입
76+
'node-id-123' // nodeId - 고유 ID
77+
)
78+
79+
// 결과:
80+
// {
81+
// name: "hover-animation-pemt2n",
82+
// keyframes: `@keyframes hover-animation-pemt2n {
83+
// from {
84+
// opacity: 1;
85+
// }
86+
// to {
87+
// opacity: 0.8;
88+
// }
89+
// }`,
90+
// animation: "hover-animation-pemt2n 300ms ease-in-out forwards",
91+
// properties: ["opacity"]
92+
// }
93+
```
94+
95+
### `generateKeyframesForEffects()`
96+
97+
여러 효과(hover, active 등)에 대한 키프레임을 한 번에 생성합니다.
98+
99+
```typescript
100+
import { generateKeyframesForEffects } from './codegen/props/keyframe'
101+
102+
const defaultProps = { opacity: '1', backgroundColor: '#ffffff' }
103+
const effectProps = new Map([
104+
['hover', { opacity: '0.8' }],
105+
['active', { opacity: '0.6', backgroundColor: '#eeeeee' }]
106+
])
107+
108+
const animations = generateKeyframesForEffects(
109+
defaultProps,
110+
effectProps,
111+
transition,
112+
'component-id'
113+
)
114+
115+
// 결과: KeyframeAnimation[]
116+
// [
117+
// { name: "hover-animation-...", ... },
118+
// { name: "active-animation-...", ... }
119+
// ]
120+
```
121+
122+
### 유틸리티 함수
123+
124+
#### `isSmartAnimateTransition()`
125+
126+
트랜지션이 SMART_ANIMATE 타입인지 확인합니다.
127+
128+
```typescript
129+
import { isSmartAnimateTransition } from './codegen/props/keyframe'
130+
131+
if (isSmartAnimateTransition(transition)) {
132+
// SMART_ANIMATE 트랜지션 처리
133+
}
134+
```
135+
136+
#### `extractTransitionFromReactions()`
137+
138+
Figma reactions 배열에서 트랜지션을 추출합니다.
139+
140+
```typescript
141+
import { extractTransitionFromReactions } from './codegen/props/keyframe'
142+
143+
const transition = extractTransitionFromReactions(node.reactions)
144+
```
145+
146+
## 생성되는 키프레임 구조
147+
148+
### KeyframeAnimation 인터페이스
149+
150+
```typescript
151+
interface KeyframeAnimation {
152+
/** 고유한 애니메이션 이름 (예: "hover-animation-abc123") */
153+
name: string
154+
155+
/** CSS @keyframes 정의 문자열 */
156+
keyframes: string
157+
158+
/** CSS animation 속성 값 */
159+
animation: string
160+
161+
/** 애니메이션되는 속성 배열 */
162+
properties: string[]
163+
}
164+
```
165+
166+
### 생성 예시
167+
168+
```css
169+
/* keyframes 속성 */
170+
@keyframes hover-animation-abc123 {
171+
from {
172+
opacity: 1;
173+
transform: translateX(0px);
174+
}
175+
to {
176+
opacity: 0.8;
177+
transform: translateX(10px);
178+
}
179+
}
180+
181+
/* animation 속성 */
182+
animation: hover-animation-abc123 300ms ease-in-out forwards;
183+
```
184+
185+
## Figma 설정 방법
186+
187+
1. **Component Set 생성**: 버튼 등의 컴포넌트를 Component Set으로 만듭니다
188+
2. **Variants 추가**: Default, Hover, Active 등의 variants를 추가합니다
189+
3. **Properties 변경**: 각 variant에서 애니메이션할 속성(opacity, position 등)을 설정합니다
190+
4. **Prototype 설정**:
191+
- Default variant를 선택
192+
- Prototype 패널에서 interaction 추가
193+
- Trigger: "On hover" 또는 "On press" 선택
194+
- Action: 대상 variant 선택
195+
- Animation: "Smart animate" 선택
196+
- Duration과 Easing 설정
197+
198+
## 지원되는 Trigger 타입
199+
200+
- `ON_HOVER` → `hover` 효과
201+
- `ON_PRESS` → `active` 효과
202+
203+
## 지원되는 Easing 타입
204+
205+
모든 Figma easing 타입이 지원됩니다:
206+
207+
- `EASE_IN` → `ease-in`
208+
- `EASE_OUT` → `ease-out`
209+
- `EASE_IN_OUT` → `ease-in-out`
210+
- `LINEAR` → `linear`
211+
- 기타 등등 (언더스코어는 하이픈으로 변환됨)
212+
213+
## 고급 사용 사례
214+
215+
### 1. 복잡한 속성 애니메이션
216+
217+
```typescript
218+
const defaultProps = {
219+
opacity: '1',
220+
transform: 'scale(1) rotate(0deg)',
221+
backgroundColor: '#ffffff',
222+
boxShadow: '0 0 0 rgba(0,0,0,0)'
223+
}
224+
225+
const hoverProps = {
226+
opacity: '0.9',
227+
transform: 'scale(1.05) rotate(5deg)',
228+
backgroundColor: '#f0f0f0',
229+
boxShadow: '0 4px 8px rgba(0,0,0,0.2)'
230+
}
231+
232+
const keyframe = generateKeyframeFromTransition(
233+
defaultProps,
234+
hoverProps,
235+
transition,
236+
'hover',
237+
nodeId
238+
)
239+
```
240+
241+
### 2. 여러 상태 처리
242+
243+
```typescript
244+
const effectProps = new Map([
245+
['hover', { opacity: '0.8' }],
246+
['active', { opacity: '0.6' }],
247+
['focus', { opacity: '0.9', outline: '2px solid blue' }]
248+
])
249+
250+
const animations = generateKeyframesForEffects(
251+
defaultProps,
252+
effectProps,
253+
transition,
254+
nodeId
255+
)
256+
```
257+
258+
## 테스트
259+
260+
키프레임 생성 기능은 완전한 단위 테스트를 포함합니다:
261+
262+
```bash
263+
bun test src/codegen/props/__tests__/keyframe.test.ts
264+
```
265+
266+
테스트 커버리지:
267+
- 함수 커버리지: 100%
268+
- 라인 커버리지: 93.22%
269+
270+
## 주의사항
271+
272+
1. **Duration 단위**: Figma의 duration은 초 단위이며, 자동으로 밀리초(ms)로 변환됩니다
273+
2. **고유 이름**: 각 애니메이션은 effect 타입, 속성, nodeId를 기반으로 고유한 이름을 생성합니다
274+
3. **animation-fill-mode**: 기본적으로 `forwards`가 적용되어 애니메이션 종료 상태가 유지됩니다
275+
4. **속성 필터링**: 시작 상태와 끝 상태가 동일한 속성은 자동으로 제외됩니다
276+
277+
## 향후 개선 사항
278+
279+
- [ ] 중간 키프레임 지원 (0%, 50%, 100% 등)
280+
- [ ] animation-iteration-count 옵션
281+
- [ ] animation-direction 옵션
282+
- [ ] 커스텀 easing 함수 (cubic-bezier)
283+
- [ ] 다중 애니메이션 체인
284+
- [ ] 조건부 애니메이션 (media query)

0 commit comments

Comments
 (0)