main
1#!/usr/bin/env python3
2"""
3Shake Animation Template - Creates shaking/vibrating motion.
4
5Use this for impact effects, emphasis, or nervous/excited reactions.
6"""
7
8import sys
9import math
10from pathlib import Path
11
12sys.path.append(str(Path(__file__).parent.parent))
13
14from core.gif_builder import GIFBuilder
15from core.frame_composer import create_blank_frame, draw_circle, draw_emoji, draw_text
16from core.easing import ease_out_quad
17
18
19def create_shake_animation(
20 object_type: str = 'emoji',
21 object_data: dict = None,
22 num_frames: int = 20,
23 shake_intensity: int = 15,
24 center_x: int = 240,
25 center_y: int = 240,
26 direction: str = 'horizontal', # 'horizontal', 'vertical', or 'both'
27 frame_width: int = 480,
28 frame_height: int = 480,
29 bg_color: tuple[int, int, int] = (255, 255, 255)
30) -> list:
31 """
32 Create frames for a shaking animation.
33
34 Args:
35 object_type: 'circle', 'emoji', 'text', or 'custom'
36 object_data: Data for the object
37 num_frames: Number of frames
38 shake_intensity: Maximum shake displacement in pixels
39 center_x: Center X position
40 center_y: Center Y position
41 direction: 'horizontal', 'vertical', or 'both'
42 frame_width: Frame width
43 frame_height: Frame height
44 bg_color: Background color
45
46 Returns:
47 List of frames
48 """
49 frames = []
50
51 # Default object data
52 if object_data is None:
53 if object_type == 'emoji':
54 object_data = {'emoji': '😱', 'size': 80}
55 elif object_type == 'text':
56 object_data = {'text': 'SHAKE!', 'font_size': 50, 'color': (255, 0, 0)}
57
58 for i in range(num_frames):
59 frame = create_blank_frame(frame_width, frame_height, bg_color)
60
61 # Calculate progress
62 t = i / (num_frames - 1) if num_frames > 1 else 0
63
64 # Decay shake intensity over time
65 intensity = shake_intensity * (1 - ease_out_quad(t))
66
67 # Calculate shake offset using sine wave for smooth oscillation
68 freq = 3 # Oscillation frequency
69 offset_x = 0
70 offset_y = 0
71
72 if direction in ['horizontal', 'both']:
73 offset_x = int(math.sin(t * freq * 2 * math.pi) * intensity)
74
75 if direction in ['vertical', 'both']:
76 offset_y = int(math.cos(t * freq * 2 * math.pi) * intensity)
77
78 # Apply offset
79 x = center_x + offset_x
80 y = center_y + offset_y
81
82 # Draw object
83 if object_type == 'emoji':
84 draw_emoji(
85 frame,
86 emoji=object_data['emoji'],
87 position=(x - object_data['size'] // 2, y - object_data['size'] // 2),
88 size=object_data['size']
89 )
90 elif object_type == 'text':
91 draw_text(
92 frame,
93 text=object_data['text'],
94 position=(x, y),
95 font_size=object_data['font_size'],
96 color=object_data['color'],
97 centered=True
98 )
99 elif object_type == 'circle':
100 draw_circle(
101 frame,
102 center=(x, y),
103 radius=object_data.get('radius', 30),
104 fill_color=object_data.get('color', (100, 100, 255))
105 )
106
107 frames.append(frame)
108
109 return frames
110
111
112# Example usage
113if __name__ == '__main__':
114 print("Creating shake GIF...")
115
116 builder = GIFBuilder(width=480, height=480, fps=24)
117
118 frames = create_shake_animation(
119 object_type='emoji',
120 object_data={'emoji': '😱', 'size': 100},
121 num_frames=30,
122 shake_intensity=20,
123 direction='both'
124 )
125
126 builder.add_frames(frames)
127 builder.save('shake_test.gif', num_colors=128)