feat(shop): 9 premium animated backgrounds
Doubles the bg catalogue from 10 to 19 with richer multi-layer animations. Every keyframe pack is CSS-only and respects the existing prefers-reduced-motion fallback. sunset 550 slow hue cycle through warm palette rain 650 2-layer vertical streaks at different speeds snow 700 3-layer drifting flakes pattern clouds 750 drifting white blobs on day sky (only LIGHT one) fireflies 800 pulsing glowing dots, opposing drift cyber-grid 850 neon grid scrolling down with vignette kaleidoscope 1000 two huge conic-gradients in opposite rotation ocean 1100 layered blobs drift like undulating waves aurora-dance 1500 multi-band aurora — new premium top-tier Tonal classification mirrored in api.js DARK_BG_SLUGS so the veil picks the right contrast: clouds is light, the other 8 join the dark set (alongside dark, stars, aurora, nebula, grid). Each background also gains a matching .bg-preview.bg-<slug> rule that reuses the same animation at the shop's 90px swatch — WYSIWYG. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
-- 036: 9 premium animated backgrounds
|
||||
--
|
||||
-- Builds on the 10 bg seed from migration 035 with a wave of richer,
|
||||
-- multi-layer animations: weather (rain, snow, clouds), atmospheric
|
||||
-- (sunset, fireflies), and stylized (aurora-dance, ocean, kaleidoscope,
|
||||
-- cyber-grid). All CSS-only with prefers-reduced-motion fallbacks.
|
||||
--
|
||||
-- Price ladder: 550 → 1500, slotting just above the existing 'aurora'
|
||||
-- (900) and 'nebula' (1200) so the new top-tier 'aurora-dance' becomes
|
||||
-- the most aspirational unlock at 1500 coins.
|
||||
--
|
||||
-- Re-runnable: each row gated by WHERE NOT EXISTS (name, type).
|
||||
-- ═══════════════════════════════════════════════════════════════
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Закат', 'Медленно меняющая цвет вечерняя палитра', 'background', 'cosmetic', 550,
|
||||
'{"slug":"sunset"}', 'sunset', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Закат' AND type='background');
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Дождь', 'Быстрые вертикальные капли на тёмном небе', 'background', 'cosmetic', 650,
|
||||
'{"slug":"rain"}', 'cloud-rain', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Дождь' AND type='background');
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Снегопад', 'Медленно падающие снежинки в два слоя', 'background', 'cosmetic', 700,
|
||||
'{"slug":"snow"}', 'snowflake', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Снегопад' AND type='background');
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Облака', 'Плывущие облака на дневном небе', 'background', 'cosmetic', 750,
|
||||
'{"slug":"clouds"}', 'cloud', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Облака' AND type='background');
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Светлячки', 'Тёплые огоньки пульсируют и блуждают', 'background', 'cosmetic', 800,
|
||||
'{"slug":"fireflies"}', 'lightbulb', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Светлячки' AND type='background');
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Кибер-сетка', 'Светящаяся неоновая сетка с разверткой', 'background', 'cosmetic', 850,
|
||||
'{"slug":"cyber-grid"}', 'cpu', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Кибер-сетка' AND type='background');
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Калейдоскоп', 'Два встречных вращения цветовых вихрей', 'background', 'cosmetic', 1000,
|
||||
'{"slug":"kaleidoscope"}', 'rotate-3d', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Калейдоскоп' AND type='background');
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Океан', 'Глубокие волны цвета у горизонта', 'background', 'cosmetic', 1100,
|
||||
'{"slug":"ocean"}', 'waves', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Океан' AND type='background');
|
||||
|
||||
INSERT INTO shop_items (name, description, type, category, price, data, icon, is_active)
|
||||
SELECT 'Танец сияния', 'Многослойное северное сияние с волнами и блюром', 'background', 'cosmetic', 1500,
|
||||
'{"slug":"aurora-dance"}', 'wand-sparkles', 1
|
||||
WHERE NOT EXISTS (SELECT 1 FROM shop_items WHERE name='Танец сияния' AND type='background');
|
||||
@@ -1468,6 +1468,278 @@ body[data-bg-tone="dark"] .frame-unlock-hint {
|
||||
animation: ls-bg-nebula-pan 30s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════
|
||||
PREMIUM ANIMATED BACKGROUNDS (migration 036)
|
||||
══════════════════════════════════════════ */
|
||||
|
||||
/* ── 11. sunset (paid, animated) ────────────────────────────────
|
||||
A warm three-stop gradient that slowly hue-rotates through a
|
||||
sunset cycle — same palette, different ambient temperature. */
|
||||
@keyframes ls-bg-sunset-cycle {
|
||||
0%, 100% { filter: hue-rotate(0deg) saturate(1.05); }
|
||||
50% { filter: hue-rotate(35deg) saturate(1.20); }
|
||||
}
|
||||
.bg-sunset,
|
||||
.bg-preview.bg-sunset {
|
||||
background: linear-gradient(180deg, #1a1240 0%, #ff6b6b 55%, #ffa07a 100%);
|
||||
animation: ls-bg-sunset-cycle 60s linear infinite;
|
||||
}
|
||||
|
||||
/* ── 12. rain (paid, animated) ──────────────────────────────────
|
||||
Two layers of vertical streaks moving down at different rates
|
||||
for parallax. The streaks are linear-gradients tiled at small
|
||||
sizes so they read as drops. */
|
||||
@keyframes ls-bg-rain-front {
|
||||
from { background-position: 0 0; }
|
||||
to { background-position: 0 80px; }
|
||||
}
|
||||
@keyframes ls-bg-rain-back {
|
||||
from { background-position: 0 0; }
|
||||
to { background-position: 0 120px; }
|
||||
}
|
||||
.bg-rain,
|
||||
.bg-preview.bg-rain {
|
||||
background-color: #0a1828;
|
||||
background-image:
|
||||
linear-gradient(180deg, transparent 0%, transparent 40%, rgba(255,255,255,0.55) 50%, rgba(255,255,255,0.20) 70%, transparent 80%),
|
||||
linear-gradient(180deg, transparent 0%, transparent 30%, rgba(255,255,255,0.30) 45%, rgba(255,255,255,0.10) 60%, transparent 70%);
|
||||
background-size: 3px 80px, 5px 120px;
|
||||
background-position: 0 0, 25% 0;
|
||||
background-repeat: repeat, repeat;
|
||||
animation: ls-bg-rain-front 0.6s linear infinite, ls-bg-rain-back 1.1s linear infinite;
|
||||
}
|
||||
|
||||
/* ── 13. snow (paid, animated) ──────────────────────────────────
|
||||
Pattern of small white circles drifting downward; multi-size
|
||||
layers create depth. */
|
||||
@keyframes ls-bg-snow-fall {
|
||||
from { background-position: 0 0, 0 0, 0 0; }
|
||||
to { background-position: 0 200px, 0 300px, 0 250px; }
|
||||
}
|
||||
.bg-snow,
|
||||
.bg-preview.bg-snow {
|
||||
background-color: #0f1729;
|
||||
background-image:
|
||||
radial-gradient(circle 1.5px at 20% 30%, white 100%, transparent 100%),
|
||||
radial-gradient(circle 2.5px at 60% 70%, white 100%, transparent 100%),
|
||||
radial-gradient(circle 2px at 80% 20%, white 100%, transparent 100%);
|
||||
background-size: 200px 200px, 300px 300px, 250px 250px;
|
||||
animation: ls-bg-snow-fall 22s linear infinite;
|
||||
}
|
||||
|
||||
/* ── 14. clouds (paid, animated, LIGHT) ─────────────────────────
|
||||
Drifting white blobs on a sky gradient. Two layers cross the
|
||||
screen at different speeds for parallax. */
|
||||
@keyframes ls-bg-clouds-drift {
|
||||
from { transform: translateX(-30%); }
|
||||
to { transform: translateX(30%); }
|
||||
}
|
||||
.bg-clouds,
|
||||
.bg-preview.bg-clouds {
|
||||
background: linear-gradient(180deg, #87ceeb 0%, #e0f6ff 60%, #ffffff 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bg-clouds::before,
|
||||
.bg-clouds::after,
|
||||
.bg-preview.bg-clouds::before,
|
||||
.bg-preview.bg-clouds::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -10%;
|
||||
background-image:
|
||||
radial-gradient(ellipse 18% 8% at 15% 25%, rgba(255,255,255,0.9), transparent 60%),
|
||||
radial-gradient(ellipse 14% 7% at 45% 35%, rgba(255,255,255,0.8), transparent 60%),
|
||||
radial-gradient(ellipse 22% 9% at 75% 22%, rgba(255,255,255,0.85), transparent 60%);
|
||||
animation: ls-bg-clouds-drift 80s linear infinite alternate;
|
||||
}
|
||||
.bg-clouds::after,
|
||||
.bg-preview.bg-clouds::after {
|
||||
background-image:
|
||||
radial-gradient(ellipse 16% 7% at 25% 60%, rgba(255,255,255,0.55), transparent 60%),
|
||||
radial-gradient(ellipse 20% 8% at 65% 75%, rgba(255,255,255,0.5), transparent 60%);
|
||||
animation-duration: 120s;
|
||||
animation-delay: -20s;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ── 15. fireflies (paid, animated) ─────────────────────────────
|
||||
Pulsing glowing dots that drift in opposing directions. Two
|
||||
pseudo-element layers for variety; soft blur + opacity pulse. */
|
||||
@keyframes ls-bg-firefly-drift {
|
||||
0% { transform: translate(0, 0); }
|
||||
25% { transform: translate(3%, -4%); }
|
||||
50% { transform: translate(-2%, -2%); }
|
||||
75% { transform: translate(2%, 3%); }
|
||||
100% { transform: translate(0, 0); }
|
||||
}
|
||||
@keyframes ls-bg-firefly-pulse {
|
||||
0%, 100% { opacity: 0.35; }
|
||||
50% { opacity: 1; }
|
||||
}
|
||||
.bg-fireflies,
|
||||
.bg-preview.bg-fireflies {
|
||||
background: radial-gradient(ellipse at center, #1a2942 0%, #0a1828 70%, #050b14 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bg-fireflies::before,
|
||||
.bg-fireflies::after,
|
||||
.bg-preview.bg-fireflies::before,
|
||||
.bg-preview.bg-fireflies::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image:
|
||||
radial-gradient(circle 4px at 12% 22%, #ffd166 100%, transparent 100%),
|
||||
radial-gradient(circle 3px at 38% 48%, #ffd166 100%, transparent 100%),
|
||||
radial-gradient(circle 5px at 62% 18%, #ffd166 100%, transparent 100%),
|
||||
radial-gradient(circle 3px at 88% 55%, #ffd166 100%, transparent 100%),
|
||||
radial-gradient(circle 4px at 22% 78%, #ffd166 100%, transparent 100%),
|
||||
radial-gradient(circle 3px at 68% 85%, #ffd166 100%, transparent 100%);
|
||||
filter: blur(2px);
|
||||
animation: ls-bg-firefly-drift 9s ease-in-out infinite, ls-bg-firefly-pulse 3.6s ease-in-out infinite;
|
||||
}
|
||||
.bg-fireflies::after,
|
||||
.bg-preview.bg-fireflies::after {
|
||||
background-image:
|
||||
radial-gradient(circle 3px at 28% 38%, #ffe599 100%, transparent 100%),
|
||||
radial-gradient(circle 4px at 52% 68%, #ffe599 100%, transparent 100%),
|
||||
radial-gradient(circle 3px at 78% 30%, #ffe599 100%, transparent 100%);
|
||||
animation-duration: 13s, 4.8s;
|
||||
animation-delay: -2s, -1s;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ── 16. cyber-grid (paid, animated) ────────────────────────────
|
||||
Neon-blue grid scrolling downward; perspective is faked via a
|
||||
vertical gradient overlay that fades into darkness. */
|
||||
@keyframes ls-bg-cyber-scan {
|
||||
from { background-position: 0 0, 0 0, 0 0; }
|
||||
to { background-position: 0 100%, 0 50px, 0 50px; }
|
||||
}
|
||||
.bg-cyber-grid,
|
||||
.bg-preview.bg-cyber-grid {
|
||||
background-color: #060a1f;
|
||||
background-image:
|
||||
linear-gradient(180deg, rgba(6,214,224,0.10) 0%, transparent 60%),
|
||||
linear-gradient(0deg, rgba(6,214,224,0.30) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(6,214,224,0.30) 1px, transparent 1px);
|
||||
background-size: 100% 100%, 50px 50px, 50px 50px;
|
||||
animation: ls-bg-cyber-scan 6s linear infinite;
|
||||
}
|
||||
|
||||
/* ── 17. kaleidoscope (paid, animated) ──────────────────────────
|
||||
Two heavily-blurred conic-gradients rotating in opposite
|
||||
directions, blended via mix-blend-mode. */
|
||||
@keyframes ls-bg-kal-cw { to { transform: rotate(360deg); } }
|
||||
@keyframes ls-bg-kal-ccw { to { transform: rotate(-360deg); } }
|
||||
.bg-kaleidoscope,
|
||||
.bg-preview.bg-kaleidoscope {
|
||||
background: #0a0a1f;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bg-kaleidoscope::before,
|
||||
.bg-preview.bg-kaleidoscope::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -50%;
|
||||
background: conic-gradient(
|
||||
from 0deg,
|
||||
transparent 0%, rgba(155,93,229,0.45) 20%,
|
||||
transparent 33%, rgba(6,214,224,0.45) 53%,
|
||||
transparent 66%, rgba(255,107,53,0.45) 86%,
|
||||
transparent 100%
|
||||
);
|
||||
filter: blur(40px);
|
||||
animation: ls-bg-kal-cw 28s linear infinite;
|
||||
}
|
||||
.bg-kaleidoscope::after,
|
||||
.bg-preview.bg-kaleidoscope::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -50%;
|
||||
background: conic-gradient(
|
||||
from 90deg,
|
||||
transparent 0%, rgba(6,214,160,0.35) 25%,
|
||||
transparent 40%, rgba(255,209,102,0.35) 65%,
|
||||
transparent 100%
|
||||
);
|
||||
filter: blur(55px);
|
||||
mix-blend-mode: screen;
|
||||
animation: ls-bg-kal-ccw 44s linear infinite;
|
||||
}
|
||||
|
||||
/* ── 18. ocean (paid, animated) ─────────────────────────────────
|
||||
Layered radial blobs slowly drift to fake undulating waves of
|
||||
color near the bottom of the screen. */
|
||||
@keyframes ls-bg-ocean-flow {
|
||||
0% { background-position: 0% 100%, 0% 100%, 100% 100%; }
|
||||
50% { background-position: 50% 60%, 50% 80%, 50% 90%; }
|
||||
100% { background-position: 0% 100%, 0% 100%, 100% 100%; }
|
||||
}
|
||||
.bg-ocean,
|
||||
.bg-preview.bg-ocean {
|
||||
background-color: #0d2a4a;
|
||||
background-image:
|
||||
radial-gradient(ellipse 60% 40% at 50% 100%, rgba(6,214,224,0.55), transparent 60%),
|
||||
radial-gradient(ellipse 50% 35% at 30% 100%, rgba(6,214,160,0.50), transparent 65%),
|
||||
radial-gradient(ellipse 70% 35% at 70% 100%, rgba(155,93,229,0.40), transparent 65%);
|
||||
background-size: 220% 220%, 260% 260%, 240% 240%;
|
||||
background-repeat: no-repeat;
|
||||
animation: ls-bg-ocean-flow 22s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ── 19. aurora-dance (premium top, animated) ───────────────────
|
||||
Multi-layered aurora — two huge skewed bands waving in opposite
|
||||
phases, plus a slow conic underlay for depth. */
|
||||
@keyframes ls-bg-aurora-band-a {
|
||||
0%, 100% { transform: translateX(-12%) skewY(-4deg); }
|
||||
50% { transform: translateX( 12%) skewY( 4deg); }
|
||||
}
|
||||
@keyframes ls-bg-aurora-band-b {
|
||||
0%, 100% { transform: translateX( 14%) skewY( 6deg); }
|
||||
50% { transform: translateX(-14%) skewY(-6deg); }
|
||||
}
|
||||
.bg-aurora-dance,
|
||||
.bg-preview.bg-aurora-dance {
|
||||
background: radial-gradient(ellipse at top, #1a0a4a 0%, #050214 70%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bg-aurora-dance::before,
|
||||
.bg-preview.bg-aurora-dance::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -20%;
|
||||
background: linear-gradient(180deg,
|
||||
transparent 0%,
|
||||
rgba(6,214,224,0.55) 30%,
|
||||
rgba(155,93,229,0.65) 50%,
|
||||
rgba(6,214,160,0.55) 72%,
|
||||
transparent 100%
|
||||
);
|
||||
filter: blur(34px);
|
||||
animation: ls-bg-aurora-band-a 9s ease-in-out infinite;
|
||||
}
|
||||
.bg-aurora-dance::after,
|
||||
.bg-preview.bg-aurora-dance::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: -20%;
|
||||
background: linear-gradient(180deg,
|
||||
transparent 0%,
|
||||
rgba(255,107,53,0.40) 28%,
|
||||
rgba(255,209,102,0.35) 48%,
|
||||
rgba(155,93,229,0.50) 70%,
|
||||
transparent 100%
|
||||
);
|
||||
filter: blur(40px);
|
||||
mix-blend-mode: screen;
|
||||
animation: ls-bg-aurora-band-b 13s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ── Reduced-motion: kill all bg animations, keep static palette ─ */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
#ls-bg-fx,
|
||||
|
||||
@@ -1084,7 +1084,12 @@ async function applyCosmetics() {
|
||||
// ON TOP of it (#ls-bg-veil, z-index:-1 vs bg-fx -2) acting as a
|
||||
// translucent veil so vibrant animations don't drown out the UI.
|
||||
// The tone attr on <body> lets the veil darken for dark presets.
|
||||
const DARK_BG_SLUGS = new Set(['dark', 'stars', 'aurora', 'nebula', 'grid']);
|
||||
const DARK_BG_SLUGS = new Set([
|
||||
'dark', 'stars', 'aurora', 'nebula', 'grid',
|
||||
// Phase migration-036 additions — all dark except 'clouds'.
|
||||
'sunset', 'rain', 'snow', 'fireflies', 'cyber-grid',
|
||||
'kaleidoscope', 'ocean', 'aurora-dance',
|
||||
]);
|
||||
if (c.background && c.background.slug && c.background.slug !== 'none') {
|
||||
const slug = String(c.background.slug).replace(/[^a-z0-9_-]/gi, '');
|
||||
let bgEl = document.getElementById('ls-bg-fx');
|
||||
|
||||
Reference in New Issue
Block a user