Animation System
Централизованная система анимаций через Tailwind Config + Global CSS.
Цель: Единая система, без дубликатов, быстрые transitions для native app feeling.
Quick Reference
| Use Case | Tailwind Class | Duration | Notes |
|---|---|---|---|
| Modal overlay | animate-fade-in | 150ms | Стандарт для центральных модалов |
| Modal overlay (медленнее) | animate-fade-in-normal | 300ms | Для тяжёлого контента |
| Modal close | animate-fade-out | 150ms | Исчезновение |
| Card appear | animate-slide-up | 150ms | Лёгкая анимация (10px translateY) |
| Button scale | animate-scale-in | 150ms | Fast feedback |
| Fade + scale combo | animate-scale-fade | 150ms | Комбинированный эффект |
| Attention grabber | animate-pulse-slow | 3s infinite | Для important elements |
| Loading breathing | animate-breathe | 2.5s infinite | Мягкое "дыхание" для loading screens |
| Bottom sheet | .animate-slide-up | 300ms | Global CSS (100% translateY) |
| Shimmer effect | .skeleton-shimmer (CSS class) | 2s infinite | Skeleton loaders (gradient sweep) |
| Gradient shift | animate-gradient-shift | 3s infinite | Animated gradients |
Speed Variants
Fast (default): 150ms
Для UI feedback и быстрых transitions:
animate-fade-in(150ms)animate-slide-up(150ms)animate-scale-in(150ms)animate-fade-out(150ms)animate-scale-fade(150ms)
Когда использовать: Модалы, overlays, tooltips, quick feedback
Normal: 300ms
Для content transitions и тяжёлых анимаций:
animate-fade-in-normal(300ms)animate-slide-up-normal(300ms)
Когда использовать: Bottom sheets, сложные компоненты, когда нужна плавность
Long: 2.5s+
Для attention effects и loading states:
animate-breathe(2.5s infinite) — мягкое "дыхание" для loading screensanimate-pulse-slow(3s infinite)animate-shimmer(3s infinite)animate-gradient-shift(3s infinite)
Когда использовать: Loading screens, skeleton loaders, promotional banners
Animation Categories
🎭 Opacity
| Class | Keyframe | Effect |
|---|---|---|
animate-fade-in | fadeIn | 0% opacity → 100% opacity (150ms) |
animate-fade-in-normal | fadeIn | 0% opacity → 100% opacity (300ms) |
animate-fade-out | fadeOut | 100% opacity → 0% opacity (150ms) |
До рефакторинга: fadeIn включал scale(0.95 → 1) transform
После рефакторинга: fadeIn — только opacity (БЕЗ scale)
Если нужен scale: используй animate-scale-fade
🚀 Transform
| Class | Keyframe | Effect |
|---|---|---|
animate-slide-up | slideUp | translateY(10px) + opacity (150ms) |
animate-slide-up-normal | slideUp | translateY(10px) + opacity (300ms) |
animate-scale-in | scaleIn | scale(0.9 → 1) + opacity (150ms) |
🔥 Combined
| Class | Keyframe | Effect |
|---|---|---|
animate-scale-fade | scaleFade | scale(0.95 → 1) + fade in (150ms) |
Когда использовать: Для "выпрыгивающих" элементов (modals, cards)
🫁 Breathing
| Class | Keyframe | Effect |
|---|---|---|
animate-breathe | breathe | scale(1 → 1.05 → 1) + opacity(1 → 0.85 → 1) (2.5s infinite) |
Когда использовать: Loading screens с иконками, мягкие привлекающие внимание эффекты
Пример использования:
{/* BrandedLoadingScreen - иконка с breathing эффектом */}
<img
src="/images/items/scrap.webp"
className="w-24 h-24 object-contain animate-breathe"
/>
Global CSS vs Tailwind
В системе есть ДВА класса с одинаковым именем, но разными источниками и эффектами!
Tailwind: animate-slide-up
Источник: frontend/tailwind.config.js
Keyframe: slideUp
Эффект:
0% { transform: translateY(10px); opacity: 0; }
100% { transform: translateY(0); opacity: 1; }
Duration: 150ms
Use Case: Лёгкая анимация для cards, small components
Пример:
<div className="animate-slide-up">Card content</div>
Global CSS: .animate-slide-up
Источник: frontend/src/index.css:431-444
Keyframe: slide-up
Эффект:
@keyframes slide-up {
from { transform: translateY(100%); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.animate-slide-up {
animation: slide-up 0.3s ease-out;
}
Duration: 300ms
Use Case: Bottom sheet modals (heavy animations)
Пример:
{/* Global CSS класс используется для bottom sheets */}
<div className="... animate-slide-up">
Bottom sheet content (слайдится снизу на 100% высоты)
</div>
Компоненты использующие Global CSS:
CasePreviewModal.tsxStreakModal.tsxRaffleModal.tsxReferralModal.tsxSeasonModal.tsxSalvageModal.tsx,BuffActivateModal.tsx, etc.
DO NOT USE ❌
Эти паттерны ломают переключение тем и создают дубликаты:
| Плохой паттерн | Проблема | Замени на |
|---|---|---|
Inline @keyframes | Дублирование кода | Tailwind classes |
style={{ animation: '...' }} | Не централизовано | Tailwind classes |
| Hardcoded durations | Inconsistent timing | Стандартные варианты |
@keyframes в компонентах | Дубликаты | Tailwind config или Global CSS |
Примеры:
// ❌ НЕПРАВИЛЬНО: inline styles
<div style={{ animation: 'fade-in 0.3s ease-out' }}>
// ❌ НЕПРАВИЛЬНО: inline @keyframes
<style>{`
@keyframes fade-in { ... }
.animate-fade-in { animation: fade-in 0.3s ease-out; }
`}</style>
// ✅ ПРАВИЛЬНО: Tailwind class
<div className="animate-fade-in">
Architecture
Слои системы
┌─────────────────────────────────────────────────┐
│ Components (используй в JSX) │
│ className="animate-fade-in" │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Tailwind Config (animation definitions) │
│ 'fade-in': 'fadeIn 150ms ease-out' │
└─────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────┐
│ Keyframes (CSS animations) │
│ @keyframes fadeIn { 0% {...} 100% {...} } │
└─────────────────────────────────────────────────┘
Файлы системы
| Файл | Назначение |
|---|---|
frontend/tailwind.config.js:96-143 | Tailwind animations + keyframes |
frontend/src/index.css:431-444 | Global CSS для bottom sheets |
Adding New Animations
1. Добавь в Tailwind Config
Файл: frontend/tailwind.config.js
animation: {
'my-animation': 'myKeyframe 200ms ease-out',
},
keyframes: {
myKeyframe: {
'0%': { /* start state */ },
'100%': { /* end state */ },
},
}
2. Используй в компоненте
<div className="animate-my-animation">
Content
</div>
3. Избегай дубликатов
Перед добавлением:
- Проверь существующие animations в config
- Проверь Global CSS (
index.css) - Можно ли использовать существующий вариант?
Performance
Что анимировать
✅ Можно:
opacitytransform(translate, scale, rotate)
❌ Избегай:
width,height(triggers layout)left,top(triggers layout)margin,padding(triggers layout)
GPU Acceleration
Tailwind animations автоматически используют GPU acceleration через transform и opacity.
Не нужно добавлять вручную:
/* ❌ НЕ НУЖНО */
.my-element {
will-change: transform;
transform: translateZ(0);
}
Accessibility
Prefers Reduced Motion
Добавить поддержку prefers-reduced-motion в frontend/src/index.css:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Когда реализовано: Пользователи с motion sickness получат мгновенные transitions.
Migration from Inline Animations
До рефакторинга
// ResultModal.tsx (старая версия)
<div className="... animate-fade-in">
<style>{`
@keyframes fade-in {
0% { opacity: 0; transform: scale(0.95); }
100% { opacity: 1; transform: scale(1); }
}
.animate-fade-in {
animation: fade-in 0.3s ease-out;
}
`}</style>
</div>
После рефакторинга
// ResultModal.tsx (новая версия)
<div className="... animate-fade-in">
{/* Inline styles удалены - используется Tailwind */}
</div>
Результат:
- 60+ строк дублированного кода удалено
- Все модалы используют единую систему
- Timing консистентный (150ms для всех fade-in)
Breaking Changes
⚡ Duration Changes:
| Animation | Было | Стало | Изменение |
|---|---|---|---|
animate-fade-in | 500ms | 150ms | 3x быстрее |
animate-slide-up | 300ms | 150ms | 2x быстрее |
animate-scale-in | 200ms | 150ms | Немного быстрее |
🎯 Behavior Changes:
| Animation | Было | Стало |
|---|---|---|
fadeIn keyframe | opacity + scale(0.95→1) | только opacity |
Если нужен старый behavior:
- Медленнее: используй
-normalварианты (animate-fade-in-normal→ 300ms) - С scale: используй
animate-scale-fade(scale 0.95→1 + fade)
Examples
Modal Overlay
// Центральный modal (быстро)
<div className="fixed inset-0 bg-black/80 animate-fade-in">
<div className="bg-white rounded-lg">
Modal content
</div>
</div>
// Bottom sheet (медленнее, полный slide)
<div className="fixed inset-0 bg-black/80 animate-fade-in">
<div className="w-full bg-white rounded-t-3xl animate-slide-up">
Sheet content (300ms, 100% translateY from Global CSS)
</div>
</div>
Card List
// Карточки появляются с лёгким slide-up
{items.map((item, i) => (
<div
key={item.id}
className="animate-slide-up"
style={{ animationDelay: `${i * 50}ms` }}
>
{item.name}
</div>
))}
Button Feedback
<button className="hover:animate-scale-in active:scale-95 transition-all">
Click me
</button>
Loading Tips System
Система подсказок на экране загрузки с fade-анимацией и ротацией.
Расположение файлов
| Файл | Назначение |
|---|---|
frontend/src/constants/loading-tips.ts | Контент подсказок — редактировать здесь |
frontend/src/components/BrandedLoadingScreen.tsx | Компонент LoadingTip |
frontend/src/styles/loading-animations.css | CSS для tip-fade-in/out |
Категории и веса
| Категория | Вес | Описание |
|---|---|---|
app | 50% | Советы по приложению goLoot |
funny | 30% | Смешные/ироничные про Rust |
rust_game | 20% | Полезные советы по игре Rust |
Конфигурация
// frontend/src/constants/loading-tips.ts
/** Интервал смены подсказки (мс) */
export const TIP_ROTATION_INTERVAL = 5000; // 5 секунд
/** Длительность fade анимации (мс) */
export const TIP_FADE_DURATION = 300;
Как добавить подсказку
Файл: frontend/src/constants/loading-tips.ts
export const LOADING_TIPS: LoadingTip[] = [
// APP — советы по goLoot
{ text: 'Твоя новая подсказка про приложение', category: 'app' },
// FUNNY — смешные про Rust
{ text: 'Шутка про naked с факелом', category: 'funny' },
// RUST_GAME — советы по игре
{ text: 'Всегда делай airlock', category: 'rust_game' },
];
Как изменить веса категорий
// frontend/src/constants/loading-tips.ts
export const CATEGORY_WEIGHTS: Record<TipCategory, number> = {
app: 50, // 50% — советы по приложению
funny: 30, // 30% — шутки
rust_game: 20, // 20% — советы по игре
};
Текущее количество
| Категория | Количество |
|---|---|
app | ~25 подсказок |
funny | ~15 подсказок |
rust_game | ~10 подсказок |
| Всего | ~50 подсказок |
CSS Анимации
/* frontend/src/styles/loading-animations.css */
.tip-fade-in {
animation: tipFadeIn 0.3s ease-out forwards;
}
.tip-fade-out {
animation: tipFadeOut 0.3s ease-in forwards;
}
Skeleton Shimmer System
Skeleton компоненты используют современный shimmer sweep эффект (градиент слева направо) вместо animate-pulse.
CSS Class: .skeleton-shimmer
Источник: frontend/src/index.css
.skeleton-shimmer {
background: linear-gradient(
90deg,
rgba(var(--skeleton-base), 1) 0%,
rgba(var(--skeleton-shimmer), 0.6) 50%,
rgba(var(--skeleton-base), 1) 100%
);
background-size: 200% 100%;
animation: shimmer 2s ease-in-out infinite;
}
Визуальный эффект: Серый фон с плавным sweep-градиентом слева направо, аналогичный Instagram/YouTube.
Skeleton Primitives
| Компонент | Форма | Класс |
|---|---|---|
SkeletonCard | Прямоугольник | skeleton-shimmer rounded-lg |
SkeletonText | Полоска текста | skeleton-shimmer rounded |
SkeletonAvatar | Круг | skeleton-shimmer rounded-full |
SkeletonButton | Кнопка | skeleton-shimmer rounded |
Composed Skeletons
| Компонент | Имитирует |
|---|---|
HomeSkeleton | BalanceSection + QuestGrid (3 карточки) |
ProfileSkeleton | Профиль пользователя |
InventorySkeleton | 3×3 сетка карточек предметов |
CasesSkeleton | Карусель кейсов |
AchievementsSkeleton | 2 карточки достижений + footer |
Theme Support
Цвета skeleton адаптируются к теме через CSS variables (--skeleton-base, --skeleton-shimmer).
См. Theme Guide — Color Palette для конкретных значений.
Skeleton отображается только при падении bootstrap — если bootstrap успешен, данные берутся из кеша и skeleton не появляется.
Related
- Theme Guide - цветовая система
- Design Principles - UI/UX принципы