@@ -6,125 +6,118 @@ import staticLogoSrc from "@/../public/logo/buggie_bugchan_logo.png";
66import spriteSrc from "@/../public/logo/jsconf_bugchan_v5_minified.png" ;
77
88type Props = {
9- width : number ;
10- height : number ;
11- alt : string ;
12- fetchPriority ?: "high" | "low" | "auto" ;
9+ width : number ;
10+ height : number ;
11+ alt : string ;
12+ fetchPriority ?: "high" | "low" | "auto" ;
1313} ;
1414
1515const FRAME_COUNT = 29 ;
1616const SPRITE_WIDTH = 400 ;
1717const SPRITE_HEIGHT = 11600 ;
1818const ANIMATION_DURATION = 1500 ;
1919
20- export function AnimatedLogo ( {
21- width,
22- height,
23- alt,
24- fetchPriority,
25- } : Props ) {
26- const [ isAnimating , setIsAnimating ] = useState ( false ) ;
27- const [ isSpriteLoaded , setIsSpriteLoaded ] = useState ( false ) ;
28- const [ clickCount , setClickCount ] = useState ( 0 ) ;
20+ export function AnimatedLogo ( { width, height, alt, fetchPriority } : Props ) {
21+ const [ isAnimating , setIsAnimating ] = useState ( false ) ;
22+ const [ isSpriteLoaded , setIsSpriteLoaded ] = useState ( false ) ;
23+ const [ clickCount , setClickCount ] = useState ( 0 ) ;
2924
30- // detect when sprite sheet has fully loaded
31- useEffect ( ( ) => {
32- const img = new window . Image ( ) ;
33- img . onload = ( ) => {
34- setIsSpriteLoaded ( true ) ;
35- } ;
36- img . src = spriteSrc . src ;
37- } , [ ] ) ;
25+ // detect when sprite sheet has fully loaded
26+ useEffect ( ( ) => {
27+ const img = new window . Image ( ) ;
28+ img . onload = ( ) => {
29+ setIsSpriteLoaded ( true ) ;
30+ } ;
31+ img . src = spriteSrc . src ;
32+ } , [ ] ) ;
3833
39- const handleClick = ( ) => {
40- if ( isAnimating || ! isSpriteLoaded ) return ;
34+ const handleClick = ( ) => {
35+ if ( isAnimating || ! isSpriteLoaded ) return ;
4136
42- setClickCount ( clickCount + 1 ) ;
43- setIsAnimating ( true ) ;
44- setTimeout ( ( ) => {
45- setIsAnimating ( false ) ;
46- } , ANIMATION_DURATION ) ;
47- } ;
37+ setClickCount ( clickCount + 1 ) ;
38+ setIsAnimating ( true ) ;
39+ setTimeout ( ( ) => {
40+ setIsAnimating ( false ) ;
41+ } , ANIMATION_DURATION ) ;
42+ } ;
4843
49- const handleKeyDown = ( e : React . KeyboardEvent ) => {
50- // when focused, allow to animate the logo by pressing Enter or Space key
51- if ( e . key === "Enter" || e . key === " " ) {
52- e . preventDefault ( ) ;
53- handleClick ( ) ;
54- }
55- } ;
44+ const handleKeyDown = ( e : React . KeyboardEvent ) => {
45+ // when focused, allow to animate the logo by pressing Enter or Space key
46+ if ( e . key === "Enter" || e . key === " " ) {
47+ e . preventDefault ( ) ;
48+ handleClick ( ) ;
49+ }
50+ } ;
5651
57- const isReverse = clickCount % 3 === 0 ;
52+ const isReverse = clickCount % 3 === 0 ;
5853
54+ return (
55+ < div
56+ className = "logo-container"
57+ onClick = { handleClick }
58+ onKeyDown = { handleKeyDown }
59+ role = "button"
60+ tabIndex = { 0 }
61+ aria-label = { `${ alt } . Click to animate.` }
62+ aria-busy = { ! isSpriteLoaded }
63+ style = { {
64+ width : `${ width } px` ,
65+ height : `${ height } px` ,
66+ flexShrink : 0 ,
67+ cursor : isSpriteLoaded ? "pointer" : "default" ,
68+ } }
69+ >
70+ { /* show static logo only if sprite hasn't loaded yet */ }
71+ { ! isSpriteLoaded && (
72+ < Image
73+ src = { staticLogoSrc }
74+ fetchPriority = { fetchPriority }
75+ alt = { alt }
76+ width = { width }
77+ height = { height }
78+ style = { {
79+ width : `${ width } px` ,
80+ height : `${ height } px` ,
81+ objectFit : "contain" ,
82+ } }
83+ />
84+ ) }
5985
60- return (
86+ { /* after sprite is loaded, show sprite at frame 0 (idle) / animating */ }
87+ { isSpriteLoaded && (
6188 < div
62- className = "logo-container"
63- onClick = { handleClick }
64- onKeyDown = { handleKeyDown }
65- role = "button"
66- tabIndex = { 0 }
67- aria-label = { `${ alt } . Click to animate.` }
68- aria-busy = { ! isSpriteLoaded }
69- style = { {
70- width : `${ width } px` ,
71- height : `${ height } px` ,
72- flexShrink : 0 ,
73- cursor : isSpriteLoaded ? "pointer" : "default" ,
74- } }
75- >
76- { /* show static logo only if sprite hasn't loaded yet */ }
77- { ! isSpriteLoaded && (
78- < Image
79- src = { staticLogoSrc }
80- fetchPriority = { fetchPriority }
81- alt = { alt }
82- width = { width }
83- height = { height }
84- style = { {
85- width : `${ width } px` ,
86- height : `${ height } px` ,
87- objectFit : "contain" ,
88- } }
89- />
90- ) }
89+ role = "img"
90+ aria-label = { alt }
91+ style = { {
92+ width : `${ width } px` ,
93+ height : `${ height } px` ,
94+ backgroundImage : `url(${ spriteSrc . src } )` ,
95+ backgroundSize : `${ width } px ${ ( SPRITE_HEIGHT / SPRITE_WIDTH ) * width } px` ,
96+ animation : isAnimating
97+ ? `sprite-animate ${ ANIMATION_DURATION } ms steps(${ FRAME_COUNT - 1 } ) 1 ${ isReverse ? "reverse" : "normal" } `
98+ : "none" ,
99+ } }
100+ />
101+ ) }
91102
92- { /* after sprite is loaded, show sprite at frame 0 (idle) / animating */ }
93- { isSpriteLoaded && (
94- < div
95- role = "img"
96- aria-label = { alt }
97- style = { {
98- width : `${ width } px` ,
99- height : `${ height } px` ,
100- backgroundImage : `url(${ spriteSrc . src } )` ,
101- backgroundSize : `${ width } px ${ ( SPRITE_HEIGHT / SPRITE_WIDTH ) * width } px` ,
102- animation : isAnimating
103- ? `sprite-animate ${ ANIMATION_DURATION } ms steps(${ FRAME_COUNT - 1 } ) 1 ${ isReverse ? "reverse" : "normal" } `
104- : "none" ,
105- } }
106- />
107- ) }
108-
109- < style jsx > { `
103+ < style jsx > { `
110104 .logo-container {
111105 transition: transform 0.2s ease-out;
112106 }
113-
107+
114108 .logo-container:hover {
115109 transform: scale(1.05);
116110 }
117-
111+
118112 @keyframes sprite-animate {
119113 from {
120114 background-position: 0 0;
121115 }
122116 to {
123- background-position: 0 -${ ( ( FRAME_COUNT - 1 ) * height ) } px;
117+ background-position: 0 -${ ( FRAME_COUNT - 1 ) * height } px;
124118 }
125119 }
126120 ` } </ style >
127- </ div >
128- ) ;
121+ </ div >
122+ ) ;
129123}
130-
0 commit comments