Slack GIF Creator - 유연한 도구킷
슬랙에 최적화된 애니메이션 GIF를 생성하기 위한 도구킷입니다. 슬랙의 제약 조건을 검증하는 검증기, 조합 가능한 애니메이션 프리미티브, 그리고 선택적 헬퍼 유틸리티를 제공합니다. 필요에 따라 이 도구들을 자유롭게 조합하여 창의적인 비전을 실현하세요.
Slack의 요구사항
슬랙은 GIF 사용 목적에 따라 특정 요구사항을 가지고 있습니다:
메시지 GIF:
- 최대 크기: 약 2MB
- 최적 해상도: 480x480
- 일반 FPS: 15-20
- 색상 제한: 128-256
- 지속 시간: 2-5초
이모지 GIF:
- 최대 크기: 64KB (엄격한 제한)
- 최적 해상도: 128x128
- 일반 FPS: 10-12
- 색상 제한: 32-48
- 지속 시간: 1-2초
이모지 GIF는 까다롭습니다 - 64KB 제한은 엄격합니다. 도움이 되는 전략:
- 총 프레임을 10-15개로 제한
- 최대 32-48색 사용
- 디자인을 단순하게 유지
- 그라데이션 피하기
- 파일 크기를 자주 확인
도구킷 구조
이 스킬은 세 가지 유형의 도구를 제공합니다:
- 검증기 - GIF가 Slack 요구사항을 충족하는지 확인
- 애니메이션 프리미티브 - 조합 가능한 모션 빌딩 블록 (흔들기, 튕기기, 이동, 만화경)
- 헬퍼 유틸리티 - 일반적인 요구사항을 위한 선택적 함수 (텍스트, 색상, 효과)
이 도구들을 어떻게 적용할지에 대한 완전한 창작의 자유가 있습니다.
핵심 검증기
GIF가 Slack 제약 조건을 충족하는지 확인하려면 다음 검증기를 사용하세요:
from core.gif_builder import GIFBuilder
# GIF 생성 후, 요구사항 충족 여부 확인
builder = GIFBuilder(width=128, height=128, fps=10)
# ... 원하는 방식으로 프레임 추가 ...
# 저장 및 크기 확인
info = builder.save('emoji.gif', num_colors=48, optimize_for_emoji=True)
# save 메소드는 자동으로 파일이 제한을 초과하면 경고
# info 딕셔너리에는: size_kb, size_mb, frame_count, duration_seconds 포함
파일 크기 검증기:
from core.validators import check_slack_size
# GIF가 크기 제한을 충족하는지 확인
passes, info = check_slack_size('emoji.gif', is_emoji=True)
# 반환: (True/False, 크기 정보 딕셔너리)
해상도 검증기:
from core.validators import validate_dimensions
# 해상도 확인
passes, info = validate_dimensions(128, 128, is_emoji=True)
# 반환: (True/False, 해상도 정보 딕셔너리)
전체 검증:
from core.validators import validate_gif, is_slack_ready
# 모든 검증 실행
all_pass, results = validate_gif('emoji.gif', is_emoji=True)
# 또는 빠른 확인
if is_slack_ready('emoji.gif', is_emoji=True):
print("업로드 준비 완료!")
애니메이션 프리미티브
이것들은 모션을 위한 조합 가능한 빌딩 블록입니다. 어떤 객체에든 원하는 조합으로 적용하세요:
흔들기
from templates.shake import create_shake_animation
# 이모지 흔들기
frames = create_shake_animation(
object_type='emoji',
object_data={'emoji': '😱', 'size': 80},
num_frames=20,
shake_intensity=15,
direction='both' # 또는 'horizontal', 'vertical'
)
튕기기
from templates.bounce import create_bounce_animation
# 원 튕기기
frames = create_bounce_animation(
object_type='circle',
object_data={'radius': 40, 'color': (255, 100, 100)},
num_frames=30,
bounce_height=150
)
회전 / 돌리기
from templates.spin import create_spin_animation, create_loading_spinner
# 시계 방향 회전
frames = create_spin_animation(
object_type='emoji',
object_data={'emoji': '🔄', 'size': 100},
rotation_type='clockwise',
full_rotations=2
)
# 흔들리는 회전
frames = create_spin_animation(rotation_type='wobble', full_rotations=3)
# 로딩 스피너
frames = create_loading_spinner(spinner_type='dots')
맥박 / 하트비트
from templates.pulse import create_pulse_animation, create_attention_pulse
# 부드러운 맥박
frames = create_pulse_animation(
object_data={'emoji': '❤️', 'size': 100},
pulse_type='smooth',
scale_range=(0.8, 1.2)
)
# 하트비트 (이중 펌프)
frames = create_pulse_animation(pulse_type='heartbeat')
# 이모지 GIF용 주의 맥박
frames = create_attention_pulse(emoji='⚠️', num_frames=20)
페이드
from templates.fade import create_fade_animation, create_crossfade
# 페이드 인
frames = create_fade_animation(fade_type='in')
# 페이드 아웃
frames = create_fade_animation(fade_type='out')
# 두 이모지 간 크로스페이드
frames = create_crossfade(
object1_data={'emoji': '😊', 'size': 100},
object2_data={'emoji': '😂', 'size': 100}
)
줌
from templates.zoom import create_zoom_animation, create_explosion_zoom
# 극적으로 줌 인
frames = create_zoom_animation(
zoom_type='in',
scale_range=(0.1, 2.0),
add_motion_blur=True
)
# 줌 아웃
frames = create_zoom_animation(zoom_type='out')
# 폭발 줌
frames = create_explosion_zoom(emoji='💥')
폭발 / 산산조각
from templates.explode import create_explode_animation, create_particle_burst
# 버스트 폭발
frames = create_explode_animation(
explode_type='burst',
num_pieces=25
)
# 산산조각 효과
frames = create_explode_animation(explode_type='shatter')
# 입자로 해체
frames = create_explode_animation(explode_type='dissolve')
# 입자 버스트
frames = create_particle_burst(particle_count=30)
흔들흔들 / 까딱까딱
from templates.wiggle import create_wiggle_animation, create_excited_wiggle
# 젤리 흔들림
frames = create_wiggle_animation(
wiggle_type='jello',
intensity=1.0,
cycles=2
)
# 파도 모션
frames = create_wiggle_animation(wiggle_type='wave')
# 이모지 GIF용 신난 흔들림
frames = create_excited_wiggle(emoji='🎉')
슬라이드
from templates.slide import create_slide_animation, create_multi_slide
# 왼쪽에서 슬라이드 인 (오버슛 포함)
frames = create_slide_animation(
direction='left',
slide_type='in',
overshoot=True
)
# 가로질러 슬라이드
frames = create_slide_animation(direction='left', slide_type='across')
# 여러 객체가 순차적으로 슬라이드
objects = [
{'data': {'emoji': '🎯', 'size': 60}, 'direction': 'left', 'final_pos': (120, 240)},
{'data': {'emoji': '🎪', 'size': 60}, 'direction': 'right', 'final_pos': (240, 240)}
]
frames = create_multi_slide(objects, stagger_delay=5)
뒤집기
from templates.flip import create_flip_animation, create_quick_flip
# 두 이모지 간 수평 뒤집기
frames = create_flip_animation(
object1_data={'emoji': '😊', 'size': 120},
object2_data={'emoji': '😂', 'size': 120},
flip_axis='horizontal'
)
# 수직 뒤집기
frames = create_flip_animation(flip_axis='vertical')
# 이모지 GIF용 빠른 뒤집기
frames = create_quick_flip('👍', '👎')
변형 / 변환
from templates.morph import create_morph_animation, create_reaction_morph
# 크로스페이드 변형
frames = create_morph_animation(
object1_data={'emoji': '😊', 'size': 100},
object2_data={'emoji': '😂', 'size': 100},
morph_type='crossfade'
)
# 스케일 변형 (하나는 줄어들고 다른 하나는 커짐)
frames = create_morph_animation(morph_type='scale')
# 회전 변형 (3D 뒤집기 스타일)
frames = create_morph_animation(morph_type='spin_morph')
이동 효과
from templates.move import create_move_animation
# 선형 이동
frames = create_move_animation(
object_type='emoji',
object_data={'emoji': '🚀', 'size': 60},
start_pos=(50, 240),
end_pos=(430, 240),
motion_type='linear',
easing='ease_out'
)
# 아크 이동 (포물선 궤적)
frames = create_move_animation(
object_type='emoji',
object_data={'emoji': '⚽', 'size': 60},
start_pos=(50, 350),
end_pos=(430, 350),
motion_type='arc',
motion_params={'arc_height': 150}
)
# 원형 이동
frames = create_move_animation(
object_type='emoji',
object_data={'emoji': '🌍', 'size': 50},
motion_type='circle',
motion_params={
'center': (240, 240),
'radius': 120,
'angle_range': 360 # 완전 원
}
)
# 파도 이동
frames = create_move_animation(
motion_type='wave',
motion_params={
'wave_amplitude': 50,
'wave_frequency': 2
}
)
# 또는 저수준 이징 함수 사용
from core.easing import interpolate, calculate_arc_motion
for i in range(num_frames):
t = i / (num_frames - 1)
x = interpolate(start_x, end_x, t, easing='ease_out')
# 또는: x, y = calculate_arc_motion(start, end, height, t)
만화경 효과
from templates.kaleidoscope import apply_kaleidoscope, create_kaleidoscope_animation
# 단일 프레임에 적용
kaleido_frame = apply_kaleidoscope(frame, segments=8)
# 또는 애니메이션 만화경 생성
frames = create_kaleidoscope_animation(
base_frame=my_frame, # 또는 None으로 데모 패턴
num_frames=30,
segments=8,
rotation_speed=1.0
)
# 간단한 미러 효과 (더 빠름)
from templates.kaleidoscope import apply_simple_mirror
mirrored = apply_simple_mirror(frame, mode='quad') # 4방향 미러
# 모드: 'horizontal', 'vertical', 'quad', 'radial'
프리미티브를 자유롭게 조합하려면 다음 패턴을 따르세요:
# 예: 충격을 위한 튕기기 + 흔들기
for i in range(num_frames):
frame = create_blank_frame(480, 480, bg_color)
# 튕기기 모션
t_bounce = i / (num_frames - 1)
y = interpolate(start_y, ground_y, t_bounce, 'bounce_out')
# 충격 시 흔들기 추가 (y가 지면에 닿을 때)
if y >= ground_y - 5:
shake_x = math.sin(i * 2) * 10
x = center_x + shake_x
else:
x = center_x
draw_emoji(frame, '⚽', (x, y), size=60)
builder.add_frame(frame)
헬퍼 유틸리티
이것들은 일반적인 요구사항을 위한 선택적 헬퍼입니다. 필요에 따라 사용, 수정, 또는 커스텀 구현으로 대체하세요.
GIF 빌더 (조립 및 최적화)
from core.gif_builder import GIFBuilder
# 원하는 설정으로 빌더 생성
builder = GIFBuilder(width=480, height=480, fps=20)
# 프레임 추가 (어떻게 만들든 상관없음)
for frame in my_frames:
builder.add_frame(frame)
# 최적화하여 저장
builder.save('output.gif',
num_colors=128,
optimize_for_emoji=False)
주요 기능:
- 자동 색상 양자화
- 중복 프레임 제거
- Slack 제한에 대한 크기 경고
- 이모지 모드 (강력한 최적화)
텍스트 렌더링
이모지와 같은 작은 GIF에서는 텍스트 가독성이 어렵습니다. 일반적인 해결책은 외곽선을 추가하는 것입니다:
from core.typography import draw_text_with_outline, TYPOGRAPHY_SCALE
# 외곽선이 있는 텍스트 (가독성 향상)
draw_text_with_outline(
frame, "BONK!",
position=(240, 100),
font_size=TYPOGRAPHY_SCALE['h1'], # 60px
text_color=(255, 68, 68),
outline_color=(0, 0, 0),
outline_width=4,
centered=True
)
커스텀 텍스트 렌더링을 구현하려면 PIL의 ImageDraw.text()를 사용하세요. 큰 GIF에서는 잘 작동합니다.
색상 관리
전문적인 GIF는 종종 일관된 색상 팔레트를 사용합니다:
from core.color_palettes import get_palette
# 미리 만들어진 팔레트 가져오기
palette = get_palette('vibrant') # 또는 'pastel', 'dark', 'neon', 'professional'
bg_color = palette['background']
text_color = palette['primary']
accent_color = palette['accent']
색상을 직접 다루려면 RGB 튜플을 사용하세요 - 상황에 맞게 자유롭게 사용하세요.
시각 효과
임팩트 순간을 위한 선택적 효과:
from core.visual_effects import ParticleSystem, create_impact_flash, create_shockwave_rings
# 입자 시스템
particles = ParticleSystem()
particles.emit_sparkles(x=240, y=200, count=15)
particles.emit_confetti(x=240, y=200, count=20)
# 각 프레임 업데이트 및 렌더링
particles.update()
particles.render(frame)
# 플래시 효과
frame = create_impact_flash(frame, position=(240, 200), radius=100)
# 충격파 링
frame = create_shockwave_rings(frame, position=(240, 200), radii=[30, 60, 90])
이징 함수
부드러운 모션은 선형 보간 대신 이징을 사용합니다:
from core.easing import interpolate
# 물체 낙하 (가속)
y = interpolate(start=0, end=400, t=progress, easing='ease_in')
# 물체 착지 (감속)
y = interpolate(start=0, end=400, t=progress, easing='ease_out')
# 튕기기
y = interpolate(start=0, end=400, t=progress, easing='bounce_out')
# 오버슛 (탄성)
scale = interpolate(start=0.5, end=1.0, t=progress, easing='elastic_out')
사용 가능한 이징: linear, ease_in, ease_out, ease_in_out, bounce_out, elastic_out, back_out (오버슛), 그리고 core/easing.py에 더 있습니다.
프레임 구성
필요시 기본 드로잉 유틸리티:
from core.frame_composer import (
create_gradient_background, # 그라데이션 배경
draw_emoji_enhanced, # 그림자 옵션이 있는 이모지
draw_circle_with_shadow, # 깊이감 있는 도형
draw_star # 5각 별
)
# 그라데이션 배경
frame = create_gradient_background(480, 480, top_color, bottom_color)
# 그림자가 있는 이모지
draw_emoji_enhanced(frame, '🎉', position=(200, 200), size=80, shadow=True)
최적화 전략
GIF가 너무 클 때:
메시지 GIF (>2MB):
- 프레임 줄이기 (FPS 낮추기 또는 지속 시간 단축)
- 색상 줄이기 (128 → 64색)
- 해상도 줄이기 (480x480 → 320x320)
- 중복 프레임 제거 활성화
이모지 GIF (>64KB) - 적극적으로:
- 총 프레임을 10-12개로 제한
- 최대 32-40색 사용
- 그라데이션 피하기 (단색이 압축에 유리)
- 디자인 단순화 (더 적은 요소)
- 저장 메소드에서
optimize_for_emoji=True사용
예제 조합 패턴
간단한 반응 (맥박)
builder = GIFBuilder(128, 128, 10)
for i in range(12):
frame = Image.new('RGB', (128, 128), (240, 248, 255))
# 맥박 스케일
scale = 1.0 + math.sin(i * 0.5) * 0.15
size = int(60 * scale)
draw_emoji_enhanced(frame, '😱', position=(64-size//2, 64-size//2),
size=size, shadow=False)
builder.add_frame(frame)
builder.save('reaction.gif', num_colors=40, optimize_for_emoji=True)
# 검증
from core.validators import check_slack_size
check_slack_size('reaction.gif', is_emoji=True)
텍스트가 있는 알림
builder = GIFBuilder(480, 480, 15)
for i in range(30):
frame = create_gradient_background(480, 480, (255, 200, 100), (255, 100, 50))
# 튀어나오는 텍스트
t = i / 29
scale = interpolate(3.0, 1.0, t, 'back_out')
font_size = int(60 * scale)
draw_text_with_outline(
frame, "중요!",
position=(240, 150),
font_size=font_size,
text_color=(255, 255, 255),
outline_color=(0, 0, 0)
)
builder.add_frame(frame)
info = builder.save('alert.gif', num_colors=128)
if info['size_mb'] > 2:
print("최적화 필요")