1- "use client" ;
2-
31import React , { useState , useEffect } from 'react' ;
42
53type FFTChannel = number [ ] ;
64type FFTData = FFTChannel [ ] ;
75
86interface BrightCandleViewProps {
9- fftData ?: FFTData ;
10- betaPower : number ;
11- fullscreen ?: boolean ;
7+ fftData ?: FFTData ;
8+ betaPower : number ;
129}
1310
1411interface CandleLitProps {
15- betaPower : number ;
12+ betaPower : number ;
1613}
1714
18- const BrightCandleView : React . FC < BrightCandleViewProps > = ( { fftData = [ ] , betaPower, fullscreen } ) => {
19- const brightness = Math . max ( 0 , betaPower / 100 ) ; // Normalize brightness
20- // Add smoothing and minimum brightness
21- const [ displayBrightness , setDisplayBrightness ] = useState ( 0.1 ) ; // Start with small flame
22- const [ screenSize , setScreenSize ] = useState ( 'normal' ) ; // 'normal', 'large' (150%), 'xlarge' (175%)
23-
24- useEffect ( ( ) => {
25- // Detect screen size
26- const detectScreenSize = ( ) => {
27- const width = window . innerWidth ;
28- if ( width >= 1920 ) { // Assuming 1920px is 175% of standard size
29- setScreenSize ( 'xlarge' ) ;
30- } else if ( width >= 1680 ) { // Assuming 1680px is 150% of standard size
31- setScreenSize ( 'large' ) ;
32- } else {
33- setScreenSize ( 'normal' ) ;
34- }
35- } ;
36-
37- // Set initial size
38- detectScreenSize ( ) ;
39-
40- // Update on resize
41- window . addEventListener ( 'resize' , detectScreenSize ) ;
42- return ( ) => window . removeEventListener ( 'resize' , detectScreenSize ) ;
43- } , [ ] ) ;
44-
45- useEffect ( ( ) => {
46- // Always show at least a small flame (0.1) and cap at 1.0
47- const target = Math . max ( 0.1 , Math . min ( 1 , betaPower / 100 ) ) ;
48-
49- // Smooth transition
50- const timer = setInterval ( ( ) => {
51- setDisplayBrightness ( prev => {
52- const diff = target - prev ;
53- return Math . abs ( diff ) < 0.01 ? target : prev + diff * 0.1 ;
54- } ) ;
55- } , 16 ) ; // ~60fps
56-
57- return ( ) => clearInterval ( timer ) ;
58- } , [ betaPower ] ) ;
59- // console.log("beta",betaPower);
60-
61- // Use displayBrightness instead of raw betaPower for all visual elements
62- const flameColor = `rgba(255, 165, 0, ${ displayBrightness } )` ;
63- // Calculate brightness from FFT data
64- const calculateBrightness = ( ) : number => {
65- if ( ! Array . isArray ( fftData ) || fftData . length === 0 ) {
66- return 0.5 ;
67- }
68- const channelData = fftData . find (
69- ( channel ) : channel is FFTChannel =>
70- Array . isArray ( channel ) && channel . length > 0
71- ) ;
72- if ( ! channelData ) return 0.5 ;
73- const totalEnergy = channelData . reduce ( ( sum : number , value : number ) => sum + ( value || 0 ) , 0 ) ;
74- return Math . min ( Math . max ( totalEnergy / 100 , 0.3 ) , 1 ) ;
75- } ;
76-
77-
78- // Generate a slightly randomized organic flame path
79- const generateFlamePath = ( ) => {
80- const basePoints = [
81- { x : 100 , y : 30 } ,
82- { x : 60 , y : 80 } ,
83- { x : 100 , y : 200 } ,
84- { x : 140 , y : 80 }
85- ] ;
86- const controlPoints = basePoints . map ( point => ( {
87- x : point . x + ( Math . random ( ) - 0.5 ) * ( 10 * brightness ) ,
88- y : point . y + ( Math . random ( ) - 0.5 ) * ( 10 * brightness )
89- } ) ) ;
90- return `
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 ) ) ;
23+
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
31+
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 `
9148 M${ controlPoints [ 0 ] . x } ${ controlPoints [ 0 ] . y }
9249 Q${ controlPoints [ 1 ] . x } ${ controlPoints [ 1 ] . y } , ${ controlPoints [ 2 ] . x } ${ controlPoints [ 2 ] . y }
9350 Q${ controlPoints [ 3 ] . x } ${ controlPoints [ 3 ] . y } , ${ controlPoints [ 0 ] . x } ${ controlPoints [ 0 ] . y }
9451 ` ;
95- } ;
96-
97- // Calculate candle size based on screen size and fullscreen state
98- const getCandleWidthClass = ( ) => {
99- if ( fullscreen ) {
100- return screenSize === 'xlarge' ? 'w-40' : screenSize === 'large' ? 'w-36' : 'w-32' ;
101- } else {
102- return screenSize === 'xlarge' ? 'w-28' : screenSize === 'large' ? 'w-24' : 'w-20' ;
103- }
104- } ;
105-
106- const getCandleHeightClass = ( ) => {
107- if ( fullscreen ) {
108- return 'h-96' ;
109- } else {
110- return screenSize === 'xlarge' ? 'h-56' : screenSize === 'large' ? 'h-48' : 'h-40' ;
111- }
112- } ;
113-
114- const getCandleHolderHeightClass = ( ) => {
115- if ( fullscreen ) {
116- return 'h-48' ;
117- } else {
118- return screenSize === 'xlarge' ? 'h-28' : screenSize === 'large' ? 'h-24' : 'h-20' ;
119- }
120- } ;
121-
122- const getFlameHeightClass = ( ) => {
123- if ( fullscreen ) {
124- return 'h-64' ; // Increased from h-48
125- } else {
126- return screenSize === 'xlarge' ? 'h-56' :
127- screenSize === 'large' ? 'h-48' :
128- 'h-40' ;
129- }
130- } ;
131- return (
132- < div className = { `w-full h-full flex items-end justify-center min-h-0 min-w-0 ${ fullscreen ? 'pb-1' : '' } ` } >
133- { /* Candle Container with dynamic width based on screen size and fullscreen mode */ }
134- < div className = { `relative ${ getCandleWidthClass ( ) } ${ getCandleHeightClass ( ) } group` } >
135- { /* Candle Holder with a glassy, frosted look */ }
136- < div
137- className = { `absolute bottom-0 w-full ${ getCandleHolderHeightClass ( ) } bg-gradient-to-b from-gray-100 to-gray-200 dark:from-stone-600 dark:to-stone-700
138- rounded-t-md border border-gray-900 dark:border-gray-800 border-b-0 backdrop-blur-md transition-transform duration-300 before:absolute before:inset-0 before:bg-white/10 before:opacity-40 before:rounded-b-xl before:rounded-t-md ` }
139- >
140- < div className = "absolute inset-0 overflow-hidden rounded-t-md bg-gradient-to-b from-cyan-300 via-blue-400 to-gray-900" >
141- < div className = "absolute top-2 left-1/2 transform -translate-x-1/2 text-center" >
142- < div
143- className = { ` ${ fullscreen ? 'text-3xl'
144- : screenSize === 'xlarge' ? 'text-2xl' : 'text-xl' } font-semibold text-[#030c21] px-2 py-1 rounded-md opacity-70 `}
145- >
146- { String (
147- Math . floor ( Number . isNaN ( betaPower ) ? 0 : betaPower )
148- ) . padStart ( 2 , '0' ) }
149- </ div >
150- </ div >
151- </ div >
152- </ div >
153-
154-
155- { /* Yellow-Themed Animated Flame */ }
156- < svg
157- xmlns = "http://www.w3.org/2000/svg"
158- viewBox = { `0 0 200 ${ fullscreen ? 200 : 400 } ` } // Increased viewBox height
159- className = { `absolute top-0 left-1/2 transform -translate-x-1/2 w-full ${ getFlameHeightClass ( ) } z-10 drop-shadow-xl` }
160- >
161- < defs >
162- { /* Outer Flame Gradient: Rich, Realistic Candle Flame Colors */ }
163- < linearGradient id = "outerFlameGradient" x1 = "0%" y1 = "0%" x2 = "0%" y2 = "100%" >
164- < stop offset = "0%" stopColor = { `rgba(255,140,0, ${ brightness * 1 } )` } />
165- < stop offset = "100%" stopColor = { `rgba(255,69,0, ${ brightness * 0.6 } )` } />
166- </ linearGradient >
167-
168- { /* Inner Flame Gradient: Warm, Luminous Colors */ }
169- < linearGradient id = "innerFlameGradient" x1 = "0%" y1 = "0%" x2 = "0%" y2 = "100%" >
170- < stop offset = "0%" stopColor = { `rgba(255,165,0, ${ brightness * 1.2 } )` } />
171- < stop offset = "100%" stopColor = { `rgba(255,99,71, ${ brightness * 0.8 } )` } />
172- </ linearGradient >
173-
174- { /* Filters for Enhanced Realism */ }
175- < filter id = "flameBlur" >
176- < feGaussianBlur stdDeviation = "7" />
177- </ filter >
178- < filter id = "innerGlow" >
179- < feGaussianBlur stdDeviation = "4" result = "coloredBlur" />
180- < feMerge >
181- < feMergeNode in = "coloredBlur" />
182- < feMergeNode in = "SourceGraphic" />
183- </ feMerge >
184- </ filter >
185- </ defs >
186-
187- { /* Outer Flame Layer */ }
188- < path
189- d = { generateFlamePath ( ) }
190- fill = "url(#outerFlameGradient)"
191- filter = "url(#flameBlur)"
192- className = "transition-all duration-300 animate-flicker opacity-70"
193- />
194-
195- { /* Inner Flame Layer */ }
196- < path
197- d = { generateFlamePath ( ) }
198- fill = "url(#innerFlameGradient)"
199- filter = "url(#innerGlow)"
200- className = "transition-all duration-300 animate-flicker"
201- />
202- </ svg >
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 >
20372 </ div >
73+ </ div >
20474 </ div >
205- ) ;
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 >
94+
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 >
107+
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+ />
115+
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+ ) ;
206127} ;
207128
208129export default BrightCandleView ;
0 commit comments