1
1
'use client' ;
2
- import { useCallback , useState } from 'react' ;
3
-
2
+ import { useCallback , useEffect , useRef , useState } from 'react' ;
3
+ import './MusicalNote.css' ;
4
4
const notesSpritesSheet = '/notes_sprites.png' ;
5
5
6
6
const spritesSheetSize = {
@@ -24,44 +24,68 @@ interface MusicalNoteProps {
24
24
size ?: number ;
25
25
}
26
26
27
- export const MusicalNote = ( { size = 8 } : MusicalNoteProps ) => {
28
- const [ currentNote , setCurrentNote ] = useState ( 0 ) ;
27
+ export const MusicalNote = ( { size = 4 } : MusicalNoteProps ) => {
28
+ const [ currentNote , setCurrentNote ] = useState (
29
+ Math . floor ( Math . random ( ) * totalNotes ) ,
30
+ ) ;
29
31
30
32
const noteToCell = useCallback ( ( ) => {
33
+ const index = Math . abs ( currentNote ) % totalNotes ;
31
34
return {
32
- x : currentNote % gridDimensions . x ,
33
- y : Math . floor ( currentNote / gridDimensions . x ) ,
35
+ x : index % gridDimensions . x ,
36
+ y : Math . floor ( index / gridDimensions . x ) ,
34
37
} ;
35
38
} , [ currentNote ] ) ;
36
39
37
- const nextNote = ( ) => {
38
- setCurrentNote ( ( currentNote + 1 ) % totalNotes ) ;
39
- } ;
40
+ const cell = noteToCell ( ) ;
41
+ const ref = useRef < HTMLDivElement | null > ( null ) ;
42
+
43
+ useEffect ( ( ) => {
44
+ const handleClick = ( ) => {
45
+ if ( ref . current ) {
46
+ ref . current . classList . remove ( 'animate-note' ) ;
47
+ ref . current . classList . add ( 'animate-note-active' ) ;
48
+ // Trigger reflow to restart the animation
49
+ void ref . current . offsetWidth ;
50
+ ref . current . classList . add ( 'animate-note' ) ;
40
51
41
- const onNoteClick = ( ) => {
42
- nextNote ( ) ;
43
- } ;
52
+ setTimeout ( ( ) => {
53
+ ref . current ?. classList . remove ( 'animate-note-active' ) ;
54
+ } , 500 ) ; // Match the duration of the animation
44
55
45
- const cell = noteToCell ( ) ;
56
+ setCurrentNote ( ( prev ) => ( prev === totalNotes - 1 ? 0 : prev + 1 ) ) ;
57
+ }
58
+ } ;
59
+
60
+ ref . current ?. parentElement ?. addEventListener ( 'click' , handleClick ) ;
61
+
62
+ return ( ) => {
63
+ ref . current ?. parentElement ?. removeEventListener ( 'click' , handleClick ) ;
64
+ } ;
65
+ } , [ ] ) ;
46
66
47
67
return (
48
68
< div
69
+ className = 'musical-note animate-note-active'
70
+ ref = { ref }
49
71
style = { {
50
72
width : singleNoteSize . width * size , // Scale display size
51
73
height : singleNoteSize . height * size , // Scale display size
52
-
53
74
backgroundImage : `url(${ notesSpritesSheet } )` ,
54
75
backgroundPosition : `-${ cell . x * singleNoteSize . width * size } px -${
55
76
cell . y * singleNoteSize . height * size
56
77
} px`,
57
78
backgroundSize : `${ spritesSheetSize . width * size } px ${
58
79
spritesSheetSize . height * size
59
80
} px`, // Scale background
60
-
81
+ zIndex : 999 ,
61
82
imageRendering : 'pixelated' ,
62
83
cursor : 'pointer' ,
84
+ position : 'absolute' , // Ensure the parent element is positioned relatively
85
+ opacity : 0 ,
86
+ // disable pointer events to allow the parent element to handle the click event
87
+ pointerEvents : 'none' ,
63
88
} }
64
- onClick = { onNoteClick }
65
89
/>
66
90
) ;
67
91
} ;
0 commit comments