Gameboy: add a fullscreen button

environments/review-wasmboy-2avzcf/deployments/4280
Alex Gleason 10 months ago
parent 48db472af5
commit 4d840e0290
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7

@ -3,9 +3,11 @@ import { WasmBoy } from '@soapbox.pub/wasmboy';
import clsx from 'clsx'; import clsx from 'clsx';
import React, { useCallback, useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import { exitFullscreen, isFullscreen, requestFullscreen } from 'soapbox/features/ui/util/fullscreen';
import { IconButton } from './ui'; import { IconButton } from './ui';
interface IGameboy extends Pick<React.CanvasHTMLAttributes<HTMLCanvasElement>, 'onFocus' | 'onBlur'> { interface IGameboy extends Pick<React.HTMLAttributes<HTMLDivElement>, 'onFocus' | 'onBlur'> {
/** Classname of the outer `<div>`. */ /** Classname of the outer `<div>`. */
className?: string; className?: string;
/** URL to the ROM. */ /** URL to the ROM. */
@ -16,10 +18,12 @@ interface IGameboy extends Pick<React.CanvasHTMLAttributes<HTMLCanvasElement>, '
/** Component to display a playable Gameboy emulator. */ /** Component to display a playable Gameboy emulator. */
const Gameboy: React.FC<IGameboy> = ({ className, src, aspect = 'normal', onFocus, onBlur, ...rest }) => { const Gameboy: React.FC<IGameboy> = ({ className, src, aspect = 'normal', onFocus, onBlur, ...rest }) => {
const node = useRef<HTMLDivElement>(null);
const canvas = useRef<HTMLCanvasElement>(null); const canvas = useRef<HTMLCanvasElement>(null);
const [paused, setPaused] = useState(false); const [paused, setPaused] = useState(false);
const [muted, setMuted] = useState(true); const [muted, setMuted] = useState(true);
const [fullscreen, setFullscreen] = useState(false);
async function init() { async function init() {
await WasmBoy.config(WasmBoyOptions, canvas.current!); await WasmBoy.config(WasmBoyOptions, canvas.current!);
@ -33,14 +37,18 @@ const Gameboy: React.FC<IGameboy> = ({ className, src, aspect = 'normal', onFocu
} }
} }
const handleFocus: React.FocusEventHandler<HTMLCanvasElement> = useCallback(() => { const handleFocus: React.FocusEventHandler<HTMLDivElement> = useCallback(() => {
WasmBoy.enableDefaultJoypad(); WasmBoy.enableDefaultJoypad();
}, []); }, []);
const handleBlur: React.FocusEventHandler<HTMLCanvasElement> = useCallback(() => { const handleBlur: React.FocusEventHandler<HTMLDivElement> = useCallback(() => {
WasmBoy.disableDefaultJoypad(); WasmBoy.disableDefaultJoypad();
}, []); }, []);
const handleFullscreenChange = useCallback(() => {
setFullscreen(isFullscreen());
}, []);
const pause = async () => { const pause = async () => {
await WasmBoy.pause(); await WasmBoy.pause();
setPaused(true); setPaused(true);
@ -58,6 +66,14 @@ const Gameboy: React.FC<IGameboy> = ({ className, src, aspect = 'normal', onFocu
setMuted(false); setMuted(false);
}; };
const toggleFullscreen = () => {
if (isFullscreen()) {
exitFullscreen();
} else if (node.current) {
requestFullscreen(node.current);
}
};
useEffect(() => { useEffect(() => {
init(); init();
@ -67,17 +83,27 @@ const Gameboy: React.FC<IGameboy> = ({ className, src, aspect = 'normal', onFocu
}; };
}, []); }, []);
useEffect(() => {
document.addEventListener('fullscreenchange', handleFullscreenChange, true);
return () => {
document.removeEventListener('fullscreenchange', handleFullscreenChange, true);
};
}, []);
return ( return (
<div className={clsx(className, 'relative')}> <div
ref={node}
tabIndex={0}
className={clsx(className, 'relative')}
onFocus={onFocus ?? handleFocus}
onBlur={onBlur ?? handleBlur}
>
<canvas <canvas
ref={canvas} ref={canvas}
tabIndex={0}
className={clsx('h-full w-full bg-black ', { className={clsx('h-full w-full bg-black ', {
'object-contain': aspect === 'normal', 'object-contain': aspect === 'normal',
'object-cover': aspect === 'stretched', 'object-cover': aspect === 'stretched',
})} })}
onFocus={onFocus ?? handleFocus}
onBlur={onBlur ?? handleBlur}
{...rest} {...rest}
/> />
@ -92,6 +118,11 @@ const Gameboy: React.FC<IGameboy> = ({ className, src, aspect = 'normal', onFocu
onClick={unmute} onClick={unmute}
src={muted ? require('@tabler/icons/volume-3.svg') : require('@tabler/icons/volume.svg')} src={muted ? require('@tabler/icons/volume-3.svg') : require('@tabler/icons/volume.svg')}
/> />
<IconButton
className='ml-auto text-white'
onClick={toggleFullscreen}
src={fullscreen ? require('@tabler/icons/arrows-minimize.svg') : require('@tabler/icons/arrows-maximize.svg')}
/>
</div> </div>
</div> </div>
); );

Loading…
Cancel
Save