Skip to content

Commit 7896e66

Browse files
committed
Refactor AnimationControl component and update styles for enhanced animation flow and layout
- Replaced slide animation stages with compress animations for improved visual transitions. - Updated styles in CurrentTrack.module.scss to enhance layout and responsiveness of animated elements. - Adjusted animation handling logic in AnimationControl for better performance and clarity. - Improved structure of CurrentTrack component for better readability and maintainability.
1 parent 9f05daf commit 7896e66

File tree

4 files changed

+130
-105
lines changed

4 files changed

+130
-105
lines changed
Lines changed: 79 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { AnimatePresence, motion, Variant } from "framer-motion";
22
import { useCallback, useEffect, useState } from "react";
33

4-
import AnimatedGradientBackground from "@/shared/Utils/Animations/AnimatedGradientBackground";
5-
64
import styles from "./CurrentTrack.module.scss";
75

86
interface Props {
@@ -12,11 +10,12 @@ interface Props {
1210

1311
type AnimationStage =
1412
| "idle"
15-
| "slidesCover"
16-
| "slidesReveal"
13+
| "compressIn"
14+
| "compressOut"
1715
| "nowPlaying"
18-
| "slidesCoverFinal"
19-
| "slidesRevealFinal"
16+
| "nowPlayingExit"
17+
| "compressInFinal"
18+
| "compressOutFinal"
2019
| "showChildren";
2120

2221
export default function AnimationControl({ children, AnimationStart }: Props) {
@@ -26,7 +25,7 @@ export default function AnimationControl({ children, AnimationStart }: Props) {
2625
const startAnimation = useCallback(() => {
2726
if (!AnimationStart || animationStage !== "idle") return;
2827

29-
setAnimationStage("slidesCover");
28+
setAnimationStage("compressIn");
3029
setNowPlayingCount(0);
3130
}, [AnimationStart, animationStage]);
3231

@@ -38,45 +37,39 @@ export default function AnimationControl({ children, AnimationStart }: Props) {
3837

3938
const handleAnimationComplete = (stage: AnimationStage) => {
4039
switch (stage) {
41-
case "slidesCover":
42-
setTimeout(() => setAnimationStage("slidesReveal"), 300);
43-
break;
44-
case "slidesReveal":
45-
setTimeout(() => setAnimationStage("nowPlaying"), 200);
40+
case "compressIn":
41+
setAnimationStage("compressOut");
4642
break;
4743
case "nowPlaying":
48-
if (nowPlayingCount < 2) {
49-
setTimeout(() => {
50-
setNowPlayingCount(prev => prev + 1);
51-
}, 800);
52-
} else {
53-
setTimeout(() => setAnimationStage("slidesCoverFinal"), 200);
54-
}
44+
// цикл NOW PLAYING управляется самим блоком через AnimatePresence
45+
break;
46+
case "compressOut":
47+
setAnimationStage("nowPlaying");
48+
break;
49+
case "compressInFinal":
50+
setAnimationStage("compressOutFinal");
5551
break;
56-
case "slidesCoverFinal":
57-
setTimeout(() => setAnimationStage("slidesRevealFinal"), 300);
52+
case "compressOutFinal":
53+
setAnimationStage("showChildren");
5854
break;
59-
case "slidesRevealFinal":
60-
setTimeout(() => setAnimationStage("showChildren"), 200);
55+
case "nowPlayingExit":
56+
setAnimationStage("compressInFinal");
6157
break;
6258
case "showChildren":
6359
setTimeout(() => setAnimationStage("idle"), 500);
6460
break;
6561
}
6662
};
6763

68-
// Варианты анимации для слайдов
69-
const slideVariants: { [key: string]: { [key: string]: Variant } } = {
70-
top: {
71-
cover: { y: "0%" },
72-
reveal: { y: "-100%" },
73-
idle: { y: "-100%" },
74-
},
75-
bottom: {
76-
cover: { y: "0%" },
77-
reveal: { y: "100%" },
78-
idle: { y: "100%" },
79-
},
64+
// Варианты анимации для красных полос: каждая полоса двигается к центру
65+
const topBarVariants: { [key: string]: Variant } = {
66+
idle: { top: "0%", y: "0%" },
67+
compress: { top: "50%", y: "-50%" },
68+
};
69+
70+
const bottomBarVariants: { [key: string]: Variant } = {
71+
idle: { top: "100%", y: "-100%" },
72+
compress: { top: "50%", y: "-50%" },
8073
};
8174

8275
// Варианты для NOW PLAYING текста
@@ -109,87 +102,77 @@ export default function AnimationControl({ children, AnimationStart }: Props) {
109102
hidden: {
110103
scale: 0,
111104
opacity: 0,
105+
transition: { duration: 0.45, ease: [0.25, 0.46, 0.45, 0.94] },
112106
},
113107
visible: {
114108
scale: 1,
115109
opacity: 1,
116-
transition: {
117-
type: "spring" as const,
118-
damping: 15,
119-
stiffness: 200,
120-
delay: 0.2,
121-
},
110+
transition: { duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] },
122111
},
123112
};
124113

125114
return (
126-
<AnimatedGradientBackground>
127-
{/* Верхний слайд */}
115+
<div className={styles.stageContainer}>
116+
{/* Верхняя красная полоса */}
128117
<motion.div
129118
className={styles.slide}
130119
style={{
131120
position: "absolute",
132-
top: 0,
133-
left: 0,
134-
right: 0,
135-
height: "50%",
121+
left: "2vw",
122+
right: "2vw",
136123
zIndex: 10,
137124
}}
138-
variants={slideVariants.top}
125+
data-position="top"
126+
variants={topBarVariants}
139127
animate={
140-
animationStage === "slidesCover" ||
141-
animationStage === "slidesCoverFinal"
142-
? "cover"
143-
: animationStage === "slidesReveal" ||
144-
animationStage === "slidesRevealFinal"
145-
? "reveal"
146-
: "idle"
128+
animationStage === "compressIn" ||
129+
animationStage === "compressInFinal"
130+
? "compress"
131+
: "idle"
147132
}
148-
transition={{
149-
duration: 0.6,
150-
ease: [0.25, 0.46, 0.45, 0.94],
151-
}}
133+
transition={{ duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] }}
152134
onAnimationComplete={() => {
153-
if (animationStage === "slidesCover")
154-
handleAnimationComplete("slidesCover");
155-
if (animationStage === "slidesReveal")
156-
handleAnimationComplete("slidesReveal");
157-
if (animationStage === "slidesCoverFinal")
158-
handleAnimationComplete("slidesCoverFinal");
159-
if (animationStage === "slidesRevealFinal")
160-
handleAnimationComplete("slidesRevealFinal");
135+
if (animationStage === "compressIn")
136+
handleAnimationComplete("compressIn");
137+
if (animationStage === "compressOut")
138+
handleAnimationComplete("compressOut");
139+
if (animationStage === "compressInFinal")
140+
handleAnimationComplete("compressInFinal");
141+
if (animationStage === "compressOutFinal")
142+
handleAnimationComplete("compressOutFinal");
161143
}}
162144
/>
163145

164-
{/* Нижний слайд */}
146+
{/* Нижняя красная полоса */}
165147
<motion.div
166148
className={styles.slide}
167149
style={{
168150
position: "absolute",
169-
bottom: 0,
170-
left: 0,
171-
right: 0,
172-
height: "50%",
151+
top: 0,
152+
left: "2vw",
153+
right: "2vw",
173154
zIndex: 10,
174155
}}
175-
variants={slideVariants.bottom}
156+
data-position="bottom"
157+
variants={bottomBarVariants}
176158
animate={
177-
animationStage === "slidesCover" ||
178-
animationStage === "slidesCoverFinal"
179-
? "cover"
180-
: animationStage === "slidesReveal" ||
181-
animationStage === "slidesRevealFinal"
182-
? "reveal"
183-
: "idle"
159+
animationStage === "compressIn" ||
160+
animationStage === "compressInFinal"
161+
? "compress"
162+
: "idle"
184163
}
185-
transition={{
186-
duration: 0.6,
187-
ease: [0.25, 0.46, 0.45, 0.94],
188-
}}
164+
transition={{ duration: 0.6, ease: [0.25, 0.46, 0.45, 0.94] }}
189165
/>
190166

191167
{/* NOW PLAYING текст */}
192-
<AnimatePresence mode="wait">
168+
<AnimatePresence
169+
mode="wait"
170+
onExitComplete={() => {
171+
if (animationStage === "nowPlayingExit") {
172+
handleAnimationComplete("nowPlayingExit");
173+
}
174+
}}
175+
>
193176
{animationStage === "nowPlaying" && (
194177
<motion.div
195178
key={`now-playing-${nowPlayingCount}`}
@@ -211,7 +194,12 @@ export default function AnimationControl({ children, AnimationStart }: Props) {
211194
exit="exit"
212195
onAnimationComplete={definition => {
213196
if (definition === "visible") {
214-
handleAnimationComplete("nowPlaying");
197+
if (nowPlayingCount < 1) {
198+
setTimeout(() => setNowPlayingCount(prev => prev + 1), 200);
199+
} else {
200+
// Запускаем выход NOW PLAYING, после onExitComplete начнётся второй цикл полос
201+
setAnimationStage("nowPlayingExit");
202+
}
215203
}
216204
}}
217205
>
@@ -232,7 +220,7 @@ export default function AnimationControl({ children, AnimationStart }: Props) {
232220
)}
233221
</AnimatePresence>
234222

235-
{/* Children контент */}
223+
{/* Контент между полосами */}
236224
<motion.div
237225
style={{
238226
position: "relative",
@@ -245,7 +233,9 @@ export default function AnimationControl({ children, AnimationStart }: Props) {
245233
}}
246234
variants={childrenVariants}
247235
animate={
248-
animationStage === "showChildren" || animationStage === "idle"
236+
animationStage === "showChildren" ||
237+
animationStage === "idle" ||
238+
animationStage === "compressOutFinal"
249239
? "visible"
250240
: "hidden"
251241
}
@@ -257,6 +247,6 @@ export default function AnimationControl({ children, AnimationStart }: Props) {
257247
>
258248
{children}
259249
</motion.div>
260-
</AnimatedGradientBackground>
250+
</div>
261251
);
262252
}

src/components/OBS_Components/SoundRequest/CurrentTrack/CurrentTrack.module.scss

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
/* Animation.module.scss */
2+
.stageContainer {
3+
position: relative;
4+
width: 100%;
5+
display: flex;
6+
align-items: center;
7+
justify-content: center;
8+
}
9+
210
.slide {
3-
height: 100%;
4-
background: linear-gradient(80deg, red 0%, red 100%);
11+
height: 3%;
12+
background: red;
513
border-radius: 0;
6-
transition: height 0.5s ease;
14+
transition: height 0.6s ease;
15+
}
16+
17+
/* Скругления только внешних углов */
18+
.slide[data-position="top"] {
19+
border-radius: 3vw;
20+
}
21+
22+
.slide[data-position="bottom"] {
23+
border-radius: 3vw;
724
}
825

926
/* CurrentTrack.module.scss */
@@ -60,7 +77,7 @@
6077
height: 100%;
6178
display: flex;
6279
flex-direction: column;
63-
justify-content: center;
80+
justify-content: space-between;
6481
align-items: center;
6582
}
6683

@@ -81,22 +98,25 @@
8198

8299
&:first-child {
83100
width: 100%;
84-
height: 100%;
101+
height: auto;
85102
align-items: center;
86-
> div {
103+
104+
>div {
87105
color: yellow;
88106
}
89107
}
108+
90109
&:last-child {
91110
width: 100%;
92-
height: 100%;
111+
height: auto;
93112
align-items: center;
94-
> div {
113+
114+
>div {
95115
color: white;
96116
}
97117
}
98118

99-
> div {
119+
>div {
100120
text-align: center;
101121
}
102122
}
@@ -128,11 +148,13 @@
128148

129149
/* Анимации */
130150
@keyframes pulse {
151+
131152
0%,
132153
100% {
133154
transform: scale(1);
134155
}
156+
135157
50% {
136158
transform: scale(1.05);
137159
}
138-
}
160+
}

src/components/OBS_Components/SoundRequest/CurrentTrack/CurrentTrack.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ export default function CurrentTrack({ track, shouldAnimate = true }: Props) {
2727
min={20}
2828
style={{
2929
width: "100%",
30-
textAlign: "end",
31-
color: "var(--site-text-warning) !important",
30+
textAlign: "center",
3231
}}
3332
>
3433
{track.artists.join(", ")}
@@ -40,8 +39,7 @@ export default function CurrentTrack({ track, shouldAnimate = true }: Props) {
4039
min={30}
4140
style={{
4241
width: "100%",
43-
textAlign: "end",
44-
color: "var(--site-text-light)",
42+
textAlign: "center",
4543
}}
4644
>
4745
{track.title}

0 commit comments

Comments
 (0)