1
- import React , {
2
- useMemo ,
3
- useCallback ,
4
- useState ,
5
- useLayoutEffect ,
6
- useEffect ,
7
- } from "react" ;
1
+ import React , { useMemo , useState , useLayoutEffect , useEffect } from "react" ;
8
2
9
3
import * as Actions from "../actionCreators" ;
10
4
import * as Selectors from "../selectors" ;
@@ -23,25 +17,20 @@ type Props = {
23
17
analyser : AnalyserNode ;
24
18
} ;
25
19
26
- export let PIXEL_DENSITY = 1 ;
27
- export let renderHeight : number ;
28
- export let doubled : boolean | undefined ;
29
- export let smallVis : boolean | undefined ;
30
- export let isMWOpen : boolean | undefined ;
31
-
32
20
// Pre-render the background grid
33
21
function preRenderBg (
34
22
width : number ,
35
23
height : number ,
36
24
bgColor : string ,
37
25
fgColor : string ,
38
- windowShade : boolean
26
+ windowShade : boolean ,
27
+ pixelDensity : number
39
28
) : HTMLCanvasElement {
40
29
// Off-screen canvas for pre-rendering the background
41
30
const bgCanvas = document . createElement ( "canvas" ) ;
42
31
bgCanvas . width = width ;
43
32
bgCanvas . height = height ;
44
- const distance = 2 * PIXEL_DENSITY ;
33
+ const distance = 2 * pixelDensity ;
45
34
46
35
const bgCanvasCtx = bgCanvas . getContext ( "2d" ) ;
47
36
if ( bgCanvasCtx == null ) {
@@ -52,8 +41,8 @@ function preRenderBg(
52
41
if ( ! windowShade ) {
53
42
bgCanvasCtx . fillStyle = fgColor ;
54
43
for ( let x = 0 ; x < width ; x += distance ) {
55
- for ( let y = PIXEL_DENSITY ; y < height ; y += distance ) {
56
- bgCanvasCtx . fillRect ( x , y , PIXEL_DENSITY , PIXEL_DENSITY ) ;
44
+ for ( let y = pixelDensity ; y < height ; y += distance ) {
45
+ bgCanvasCtx . fillRect ( x , y , pixelDensity , pixelDensity ) ;
57
46
}
58
47
}
59
48
}
@@ -64,78 +53,100 @@ export default function Vis({ analyser }: Props) {
64
53
useLayoutEffect ( ( ) => {
65
54
analyser . fftSize = 1024 ;
66
55
} , [ analyser , analyser . fftSize ] ) ;
56
+
67
57
const colors = useTypedSelector ( Selectors . getSkinColors ) ;
68
58
const mode = useTypedSelector ( Selectors . getVisualizerStyle ) ;
69
59
const audioStatus = useTypedSelector ( Selectors . getMediaStatus ) ;
70
60
const getWindowShade = useTypedSelector ( Selectors . getWindowShade ) ;
71
61
const getWindowOpen = useTypedSelector ( Selectors . getWindowOpen ) ;
72
- isMWOpen = getWindowOpen ( "main" ) ;
73
- doubled = useTypedSelector ( Selectors . getDoubled ) ;
74
- const dummyVizData = useTypedSelector ( Selectors . getDummyVizData ) ;
62
+ const isMWOpen = getWindowOpen ( "main" ) ;
63
+ const doubled = useTypedSelector ( Selectors . getDoubled ) ;
75
64
const toggleVisualizerStyle = useActionCreator ( Actions . toggleVisualizerStyle ) ;
76
65
const windowShade = getWindowShade ( "main" ) ;
77
66
78
- smallVis = windowShade && isMWOpen ;
67
+ const smallVis = windowShade && isMWOpen ;
68
+ const renderHeight = smallVis ? 5 : 16 ;
79
69
const renderWidth = 76 ;
80
- const renderWidthBG = ! isMWOpen
81
- ? renderWidth
82
- : windowShade
83
- ? ( doubled ? renderWidth : 38 )
84
- : renderWidth * PIXEL_DENSITY ;
85
-
86
- renderHeight = smallVis ? 5 : 16 ;
87
- PIXEL_DENSITY = doubled && smallVis ? 2 : 1 ;
88
-
89
- const width = renderWidth * PIXEL_DENSITY ;
90
- const height = renderHeight * PIXEL_DENSITY ;
70
+ const pixelDensity = doubled && smallVis ? 2 : 1 ;
71
+ const renderWidthBG = ! isMWOpen
72
+ ? renderWidth
73
+ : windowShade
74
+ ? doubled
75
+ ? renderWidth
76
+ : 38
77
+ : renderWidth * pixelDensity ;
78
+
79
+ const width = renderWidth * pixelDensity ;
80
+ const height = renderHeight * pixelDensity ;
91
81
92
82
const bgCanvas = useMemo ( ( ) => {
93
83
return preRenderBg (
94
84
renderWidthBG ,
95
85
height ,
96
86
colors [ 0 ] ,
97
87
colors [ 1 ] ,
98
- Boolean ( windowShade )
88
+ Boolean ( windowShade ) ,
89
+ pixelDensity
99
90
) ;
100
- } , [ colors , height , width , windowShade ] ) ;
91
+ } , [ colors , height , renderWidthBG , windowShade , pixelDensity ] ) ;
101
92
102
93
const [ canvas , setCanvas ] = useState < HTMLCanvasElement | null > ( null ) ;
103
94
104
95
//? painter administration
105
- const [ painter , setPainter ] = useState < VisPaintHandler | null > ( null ) ;
106
-
107
- useEffect ( ( ) => {
108
- if ( ! canvas ) return ;
109
- const _setPainter = ( PainterType : typeof VisPaintHandler ) => {
110
- const _vis : IVis = {
111
- canvas ,
112
- colors ,
113
- analyser ,
114
- oscStyle : "lines " ,
115
- bandwidth : "wide" ,
116
- coloring : "normal " ,
117
- peaks : true ,
118
- saFalloff : "moderate " ,
119
- saPeakFalloff : "slow" ,
120
- sa : "analyzer" ,
121
- } ;
122
- const newPainter = new PainterType ( _vis ) ;
123
- setPainter ( newPainter ) ;
96
+ const painter = useMemo ( ( ) => {
97
+ if ( ! canvas ) return null ;
98
+
99
+ const vis : IVis = {
100
+ canvas ,
101
+ colors ,
102
+ analyser ,
103
+ oscStyle : "lines" ,
104
+ bandwidth : "wide" ,
105
+ coloring : "normal " ,
106
+ peaks : true ,
107
+ saFalloff : "moderate " ,
108
+ saPeakFalloff : "slow" ,
109
+ sa : "analyzer " ,
110
+ renderHeight ,
111
+ smallVis ,
112
+ pixelDensity ,
113
+ doubled ,
114
+ isMWOpen ,
124
115
} ;
116
+
125
117
switch ( mode ) {
126
118
case VISUALIZERS . OSCILLOSCOPE :
127
- _setPainter ( WavePaintHandler ) ;
128
- break ;
119
+ return new WavePaintHandler ( vis ) ;
129
120
case VISUALIZERS . BAR :
130
- _setPainter ( BarPaintHandler ) ;
131
- break ;
121
+ return new BarPaintHandler ( vis ) ;
132
122
case VISUALIZERS . NONE :
133
- _setPainter ( NoVisualizerHandler ) ;
134
- break ;
123
+ return new NoVisualizerHandler ( vis ) ;
135
124
default :
136
- _setPainter ( NoVisualizerHandler ) ;
125
+ return new NoVisualizerHandler ( vis ) ;
137
126
}
138
- } , [ analyser , canvas , mode , colors ] ) ;
127
+ } , [
128
+ analyser ,
129
+ canvas ,
130
+ mode ,
131
+ colors ,
132
+ renderHeight ,
133
+ smallVis ,
134
+ pixelDensity ,
135
+ doubled ,
136
+ isMWOpen ,
137
+ ] ) ;
138
+
139
+ useEffect ( ( ) => {
140
+ if ( canvas && painter ) {
141
+ const canvasCtx = canvas . getContext ( "2d" ) ;
142
+ if ( canvasCtx ) {
143
+ // wipes the canvas clean if playback is paused and doubled is changing
144
+ if ( audioStatus === MEDIA_STATUS . PAUSED ) {
145
+ canvasCtx . clearRect ( 0 , 0 , canvas . width , canvas . height ) ;
146
+ }
147
+ }
148
+ }
149
+ } , [ doubled , canvas , painter ] ) ;
139
150
140
151
useEffect ( ( ) => {
141
152
if ( canvas == null || painter == null ) {
@@ -151,37 +162,31 @@ export default function Vis({ analyser }: Props) {
151
162
let animationRequest : number | null = null ;
152
163
153
164
const loop = ( ) => {
154
- if ( mode === VISUALIZERS . NONE ) {
155
- canvasCtx . clearRect ( 0 , 0 , renderWidthBG , height ) ;
156
- } else {
157
- canvasCtx . drawImage ( bgCanvas , 0 , 0 ) ;
158
- }
165
+ canvasCtx . drawImage ( bgCanvas , 0 , 0 ) ;
159
166
painter . prepare ( ) ;
160
167
painter . paintFrame ( ) ;
161
168
animationRequest = window . requestAnimationFrame ( loop ) ;
162
169
} ;
163
170
164
171
if ( audioStatus === MEDIA_STATUS . PLAYING ) {
165
- loop ( ) ;
166
- } else if ( animationRequest !== null ) {
167
- // Clean up the animation frame request if the status is not PLAYING
168
- window . cancelAnimationFrame ( animationRequest ) ;
169
- animationRequest = null ;
172
+ if ( mode === VISUALIZERS . NONE ) {
173
+ canvasCtx . clearRect ( 0 , 0 , renderWidthBG , height ) ;
174
+ } else {
175
+ loop ( ) ;
176
+ }
170
177
}
171
178
172
179
return ( ) => {
173
180
if ( animationRequest !== null ) {
174
181
window . cancelAnimationFrame ( animationRequest ) ;
175
182
}
176
183
} ;
177
- } , [ audioStatus , canvas , painter , bgCanvas ] ) ;
184
+ } , [ audioStatus , canvas , painter , bgCanvas , renderWidthBG , height , mode ] ) ;
178
185
179
186
if ( audioStatus === MEDIA_STATUS . STOPPED ) {
180
187
return null ;
181
188
}
182
- // @ts -ignore
183
189
184
- // @ts -ignore
185
190
return (
186
191
< canvas
187
192
id = "visualizer"
0 commit comments