11import { AnimatePresence , motion , Variant } from "framer-motion" ;
22import { useCallback , useEffect , useState } from "react" ;
33
4- import AnimatedGradientBackground from "@/shared/Utils/Animations/AnimatedGradientBackground" ;
5-
64import styles from "./CurrentTrack.module.scss" ;
75
86interface Props {
@@ -12,11 +10,12 @@ interface Props {
1210
1311type 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
2221export 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}
0 commit comments