@@ -4,126 +4,100 @@ type FFTChannel = number[];
44type FFTData = FFTChannel [ ] ;
55
66interface BrightCandleViewProps {
7- fftData ?: FFTData ;
8- betaPower : number ;
7+ fftData ?: FFTData ;
8+ betaPower : number ;
9+ isFullPage ?: boolean ;
910}
1011
11- interface CandleLitProps {
12- betaPower : number ;
13- }
14-
15- const BrightCandleView : React . FC < BrightCandleViewProps > = ( { fftData = [ ] , betaPower } ) => {
16- const brightness = Math . max ( 0 , betaPower / 100 ) ; // Normalize brightness
17- // Add smoothing and minimum brightness
18- const [ displayBrightness , setDisplayBrightness ] = useState ( 0.1 ) ; // Start with small flame
19-
20- useEffect ( ( ) => {
21- // Always show at least a small flame (0.1) and cap at 1.0
22- const target = Math . max ( 0.1 , Math . min ( 1 , betaPower / 100 ) ) ;
12+ const BrightCandleView : React . FC < BrightCandleViewProps > = ( { fftData = [ ] , betaPower, isFullPage } ) => {
13+ const brightness = Math . max ( 0 , betaPower / 100 ) ;
14+ const [ displayBrightness , setDisplayBrightness ] = useState ( 0.1 ) ;
2315
24- // Smooth transition
25- const timer = setInterval ( ( ) => {
26- setDisplayBrightness ( prev => {
27- const diff = target - prev ;
28- return Math . abs ( diff ) < 0.01 ? target : prev + diff * 0.1 ;
29- } ) ;
30- } , 16 ) ; // ~60fps
16+ useEffect ( ( ) => {
17+ const target = Math . max ( 0.1 , Math . min ( 1 , betaPower / 100 ) ) ;
18+ const timer = setInterval ( ( ) => {
19+ setDisplayBrightness ( prev => {
20+ const diff = target - prev ;
21+ return Math . abs ( diff ) < 0.01 ? target : prev + diff * 0.1 ;
22+ } ) ;
23+ } , 16 ) ;
24+ return ( ) => clearInterval ( timer ) ;
25+ } , [ betaPower ] ) ;
3126
32- return ( ) => clearInterval ( timer ) ;
33- } , [ betaPower ] ) ;
34-
35- // Generate a slightly randomized organic flame path
36- const generateFlamePath = ( ) => {
37- const basePoints = [
38- { x : 100 , y : 50 } ,
39- { x : 70 , y : 100 } ,
40- { x : 100 , y : 180 } ,
41- { x : 130 , y : 100 }
42- ] ;
43- const controlPoints = basePoints . map ( point => ( {
44- x : point . x + ( Math . random ( ) - 0.5 ) * ( 10 * brightness ) ,
45- y : point . y + ( Math . random ( ) - 0.5 ) * ( 10 * brightness )
46- } ) ) ;
47- return `
48- M${ controlPoints [ 0 ] . x } ${ controlPoints [ 0 ] . y }
27+ const generateFlamePath = ( ) => {
28+ const basePoints = [
29+ { x : 100 , y : 50 } ,
30+ { x : 70 , y : 100 } ,
31+ { x : 100 , y : 180 } ,
32+ { x : 130 , y : 100 }
33+ ] ;
34+ const controlPoints = basePoints . map ( point => ( {
35+ x : point . x + ( Math . random ( ) - 0.5 ) * ( 10 * brightness ) ,
36+ y : point . y + ( Math . random ( ) - 0.5 ) * ( 10 * brightness )
37+ } ) ) ;
38+ return `
39+ M${ controlPoints [ 0 ] . x } ${ controlPoints [ 0 ] . y }
4940 Q${ controlPoints [ 1 ] . x } ${ controlPoints [ 1 ] . y } , ${ controlPoints [ 2 ] . x } ${ controlPoints [ 2 ] . y }
5041 Q${ controlPoints [ 3 ] . x } ${ controlPoints [ 3 ] . y } , ${ controlPoints [ 0 ] . x } ${ controlPoints [ 0 ] . y }
5142 ` ;
52- } ;
53-
54- return (
55- < div className = "w-full h-full flex items-center justify-center min-h-0 min-w-0" >
56- { /* Candle Container - responsive width for different screen sizes */ }
57- < div className = "relative w-24 sm:w-28 md:w-32 lg:w-36 h-64 group" >
58- { /* Candle Holder with a glassy, frosted look */ }
59- < div className = "absolute bottom-0 w-full h-24 sm:h-28 md:h-32
60- bg-gradient-to-b from-gray-100 to-gray-200 dark:from-stone-600 dark:to-stone-700
61- rounded-b-xl rounded-t-md
62- border border-gray-300 dark:border-white/20
63- backdrop-blur-md shadow-xl
64- transition-transform duration-300
65- before:absolute before:inset-0 before:bg-white/10 before:opacity-40 before:rounded-b-xl before:rounded-t-md"
66- >
67- < div className = "absolute inset-0 overflow-hidden rounded-b-xl rounded-t-md bg-gradient-to-b from-cyan-300 via-blue-400 to-blue-400" >
68- < div className = "absolute top-2 left-1/2 transform -translate-x-1/2 text-center" >
69- < div className = "text-base sm:text-lg font-semibold text-gray-700 px-1 sm:px-2 py-1 rounded-md" >
70- { String ( Math . floor ( betaPower ) ) . padStart ( 2 , '0' ) }
71- </ div >
72- </ div >
73- </ div >
74- </ div >
75-
76- { /* Yellow-Themed Animated Flame */ }
77- < svg
78- xmlns = "http://www.w3.org/2000/svg"
79- viewBox = "0 0 200 300"
80- className = "absolute top-0 left-1/2 transform -translate-x-1/2 w-full h-36 sm:h-40 md:h-44 lg:h-48 z-10 drop-shadow-xl"
81- >
82- < defs >
83- { /* Outer Flame Gradient: Rich, Realistic Candle Flame Colors */ }
84- < linearGradient id = "outerFlameGradient" x1 = "0%" y1 = "0%" x2 = "0%" y2 = "100%" >
85- < stop offset = "0%" stopColor = { `rgba(255,140,0, ${ brightness * 0.6 } )` } />
86- < stop offset = "100%" stopColor = { `rgba(255,69,0, ${ brightness * 0.3 } )` } />
87- </ linearGradient >
88-
89- { /* Inner Flame Gradient: Warm, Luminous Colors */ }
90- < linearGradient id = "innerFlameGradient" x1 = "0%" y1 = "0%" x2 = "0%" y2 = "100%" >
91- < stop offset = "0%" stopColor = { `rgba(255,165,0, ${ brightness * 0.8 } )` } />
92- < stop offset = "100%" stopColor = { `rgba(255,99,71, ${ brightness * 0.5 } )` } />
93- </ linearGradient >
43+ } ;
9444
95- { /* Filters for Enhanced Realism */ }
96- < filter id = "flameBlur" >
97- < feGaussianBlur stdDeviation = "7" />
98- </ filter >
99- < filter id = "innerGlow" >
100- < feGaussianBlur stdDeviation = "4" result = "coloredBlur" />
101- < feMerge >
102- < feMergeNode in = "coloredBlur" />
103- < feMergeNode in = "SourceGraphic" />
104- </ feMerge >
105- </ filter >
106- </ defs >
45+ return (
46+ < div className = "w-full h-full flex items-end justify-center min-h-0 min-w-0" >
47+ < div className = { `relative ${ isFullPage ? 'w-[20%] sm:w-[18%] md:w-[20%] lg:w-[25%] h-[30%] sm:h-[40%] md:h-[50%] lg:h-[60%]' : 'w-[20%] sm:w-[18%] md:w-[15%] lg:w-[20%] h-[60%] sm:h-[70%] md:h-[80%] lg:h-[95%]' } group overflow-visible` } >
48+ < svg
49+ xmlns = "http://www.w3.org/2000/svg"
50+ viewBox = "0 0 200 300"
51+ className = { `absolute ${ isFullPage ? '-top-[42%] sm:-top-[40%] md:-top-[50%] lg:-top-[40%]' : '-top-[25%] sm:-top-[18%] md:-top-[14%] lg:-top-[24%]' } left-1/2 transform -translate-x-1/2 w-full h-auto z-50 drop-shadow-xl pointer-events-none` }
52+ preserveAspectRatio = "xMidYMid meet"
53+ >
10754
108- { /* Outer Flame Layer */ }
109- < path
110- d = { generateFlamePath ( ) }
111- fill = "url(#outerFlameGradient)"
112- filter = "url(#flameBlur)"
113- className = "transition-all duration-300 animate-flicker opacity-70"
114- />
55+ < defs >
56+ < linearGradient id = "outerFlameGradient" x1 = "0%" y1 = "0%" x2 = "0%" y2 = "100%" >
57+ < stop offset = "0%" stopColor = { `rgba(255,140,0, ${ brightness * 0.6 } )` } />
58+ < stop offset = "100%" stopColor = { `rgba(255,69,0, ${ brightness * 0.3 } )` } />
59+ </ linearGradient >
60+ < linearGradient id = "innerFlameGradient" x1 = "0%" y1 = "0%" x2 = "0%" y2 = "100%" >
61+ < stop offset = "0%" stopColor = { `rgba(255,165,0, ${ brightness * 0.8 } )` } />
62+ < stop offset = "100%" stopColor = { `rgba(255,99,71, ${ brightness * 0.5 } )` } />
63+ </ linearGradient >
64+ < filter id = "flameBlur" >
65+ < feGaussianBlur stdDeviation = "7" />
66+ </ filter >
67+ < filter id = "innerGlow" >
68+ < feGaussianBlur stdDeviation = "4" result = "coloredBlur" />
69+ < feMerge >
70+ < feMergeNode in = "coloredBlur" />
71+ < feMergeNode in = "SourceGraphic" />
72+ </ feMerge >
73+ </ filter >
74+ </ defs >
75+ < path
76+ d = { generateFlamePath ( ) }
77+ fill = "url(#outerFlameGradient)"
78+ filter = "url(#flameBlur)"
79+ className = "transition-all duration-300 animate-flicker opacity-70"
80+ />
81+ < path
82+ d = { generateFlamePath ( ) }
83+ fill = "url(#innerFlameGradient)"
84+ filter = "url(#innerGlow)"
85+ className = "transition-all duration-300 animate-flicker"
86+ />
87+ </ svg >
88+ < div className = { `absolute bottom-0 w-full ${ isFullPage ? 'h-full' : 'h-[30%] sm:h-[32%] md:h-[34%] lg:h-[36%]' } bg-gradient-to-b from-gray-100 to-gray-200 dark:from-stone-600 dark:to-stone-700 rounded-b-xl rounded-t-md border border-gray-300 dark:border-white/20 backdrop-blur-md shadow-xl before:absolute before:inset-0 before:bg-white/10 before:opacity-40 before:rounded-b-xl before:rounded-t-md` } >
89+ < div className = "absolute inset-0 overflow-hidden rounded-b-xl rounded-t-md bg-gradient-to-b from-cyan-300 via-blue-400 to-blue-400" >
90+ < div className = "absolute inset-0 flex items-center justify-center" >
91+ < div className = "text-sm sm:text-xl md:text-xl lg:text-2xl font-semibold text-gray-700 px-2 sm:px-3 py-1 " >
92+ { Number . isFinite ( betaPower ) ? String ( Math . floor ( betaPower ) ) . padStart ( 2 , '0' ) : '00' }
93+ </ div >
94+ </ div >
11595
116- { /* Inner Flame Layer */ }
117- < path
118- d = { generateFlamePath ( ) }
119- fill = "url(#innerFlameGradient)"
120- filter = "url(#innerGlow)"
121- className = "transition-all duration-300 animate-flicker"
122- />
123- </ svg >
124- </ div >
125- </ div >
126- ) ;
96+ </ div >
97+ </ div >
98+ </ div >
99+ </ div >
100+ ) ;
127101} ;
128102
129- export default BrightCandleView ;
103+ export default BrightCandleView ;
0 commit comments