mirror of
https://github.com/movie-web/movie-web.git
synced 2024-11-14 08:25:07 +01:00
feat(video): add "volume adjusted" bar on top for keyboard events
This commit is contained in:
parent
0c2df2cd3c
commit
2424cdfc9e
@ -31,6 +31,7 @@ import { PictureInPictureAction } from "@/video/components/actions/PictureInPict
|
|||||||
import { CaptionRendererAction } from "./actions/CaptionRendererAction";
|
import { CaptionRendererAction } from "./actions/CaptionRendererAction";
|
||||||
import { SettingsAction } from "./actions/SettingsAction";
|
import { SettingsAction } from "./actions/SettingsAction";
|
||||||
import { DividerAction } from "./actions/DividerAction";
|
import { DividerAction } from "./actions/DividerAction";
|
||||||
|
import { VolumeAdjustedAction } from "./actions/VolumeAdjustedAction";
|
||||||
|
|
||||||
type Props = VideoPlayerBaseProps;
|
type Props = VideoPlayerBaseProps;
|
||||||
|
|
||||||
@ -91,6 +92,7 @@ export function VideoPlayer(props: Props) {
|
|||||||
<>
|
<>
|
||||||
<KeyboardShortcutsAction />
|
<KeyboardShortcutsAction />
|
||||||
<PageTitleAction />
|
<PageTitleAction />
|
||||||
|
<VolumeAdjustedAction />
|
||||||
<VideoPlayerError onGoBack={props.onGoBack}>
|
<VideoPlayerError onGoBack={props.onGoBack}>
|
||||||
<BackdropAction onBackdropChange={onBackdropChange}>
|
<BackdropAction onBackdropChange={onBackdropChange}>
|
||||||
<CenterPosition>
|
<CenterPosition>
|
||||||
|
@ -65,12 +65,12 @@ export function KeyboardShortcutsAction() {
|
|||||||
|
|
||||||
// Decrease volume
|
// Decrease volume
|
||||||
case "arrowdown":
|
case "arrowdown":
|
||||||
controls.setVolume(Math.max(mediaPlaying.volume - 0.1, 0));
|
controls.setVolume(Math.max(mediaPlaying.volume - 0.1, 0), true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Increase volume
|
// Increase volume
|
||||||
case "arrowup":
|
case "arrowup":
|
||||||
controls.setVolume(Math.min(mediaPlaying.volume + 0.1, 1));
|
controls.setVolume(Math.min(mediaPlaying.volume + 0.1, 1), true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Do a barrel Roll!
|
// Do a barrel Roll!
|
||||||
|
33
src/video/components/actions/VolumeAdjustedAction.tsx
Normal file
33
src/video/components/actions/VolumeAdjustedAction.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
|
import { useControls } from "@/video/state/logic/controls";
|
||||||
|
import { useInterface } from "@/video/state/logic/interface";
|
||||||
|
import { useMediaPlaying } from "@/video/state/logic/mediaplaying";
|
||||||
|
|
||||||
|
export function VolumeAdjustedAction() {
|
||||||
|
const descriptor = useVideoPlayerDescriptor();
|
||||||
|
const videoInterface = useInterface(descriptor);
|
||||||
|
const mediaPlaying = useMediaPlaying(descriptor);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
videoInterface.volumeChangedWithKeybind
|
||||||
|
? "mt-10 scale-100 opacity-100"
|
||||||
|
: "mt-5 scale-75 opacity-0",
|
||||||
|
"absolute left-1/2 z-[100] flex -translate-x-1/2 items-center space-x-4 rounded-full bg-bink-300 bg-opacity-50 py-2 px-5 transition-all duration-100",
|
||||||
|
].join(" ")}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon={mediaPlaying.volume > 0 ? Icons.VOLUME : Icons.VOLUME_X}
|
||||||
|
className="text-xl text-white"
|
||||||
|
/>
|
||||||
|
<div className="h-2 w-44 overflow-hidden rounded-full bg-denim-100">
|
||||||
|
<div
|
||||||
|
className="h-full rounded-r-full bg-bink-500 transition-[width] duration-100"
|
||||||
|
style={{ width: `${mediaPlaying.volume * 100}%` }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -32,6 +32,7 @@ function initPlayer(): VideoPlayerState {
|
|||||||
isFocused: false,
|
isFocused: false,
|
||||||
leftControlHovering: false,
|
leftControlHovering: false,
|
||||||
popoutBounds: null,
|
popoutBounds: null,
|
||||||
|
volumeChangedWithKeybind: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
mediaPlaying: {
|
mediaPlaying: {
|
||||||
|
@ -5,6 +5,8 @@ import { VideoPlayerMeta } from "@/video/state/types";
|
|||||||
import { getPlayerState } from "../cache";
|
import { getPlayerState } from "../cache";
|
||||||
import { VideoPlayerStateController } from "../providers/providerTypes";
|
import { VideoPlayerStateController } from "../providers/providerTypes";
|
||||||
|
|
||||||
|
let volumeChangedWithKeybindDebounce: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
export type ControlMethods = {
|
export type ControlMethods = {
|
||||||
openPopout(id: string): void;
|
openPopout(id: string): void;
|
||||||
closePopout(): void;
|
closePopout(): void;
|
||||||
@ -48,8 +50,20 @@ export function useControls(
|
|||||||
enterFullscreen() {
|
enterFullscreen() {
|
||||||
state.stateProvider?.enterFullscreen();
|
state.stateProvider?.enterFullscreen();
|
||||||
},
|
},
|
||||||
setVolume(volume) {
|
setVolume(volume, isKeyboardEvent = false) {
|
||||||
state.stateProvider?.setVolume(volume);
|
if (isKeyboardEvent) {
|
||||||
|
if (volumeChangedWithKeybindDebounce)
|
||||||
|
clearTimeout(volumeChangedWithKeybindDebounce);
|
||||||
|
|
||||||
|
state.interface.volumeChangedWithKeybind = true;
|
||||||
|
updateInterface(descriptor, state);
|
||||||
|
|
||||||
|
volumeChangedWithKeybindDebounce = setTimeout(() => {
|
||||||
|
state.interface.volumeChangedWithKeybind = false;
|
||||||
|
updateInterface(descriptor, state);
|
||||||
|
}, 3e3);
|
||||||
|
}
|
||||||
|
state.stateProvider?.setVolume(volume, isKeyboardEvent);
|
||||||
},
|
},
|
||||||
startAirplay() {
|
startAirplay() {
|
||||||
state.stateProvider?.startAirplay();
|
state.stateProvider?.startAirplay();
|
||||||
|
@ -9,6 +9,7 @@ export type VideoInterfaceEvent = {
|
|||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
isFullscreen: boolean;
|
isFullscreen: boolean;
|
||||||
popoutBounds: null | DOMRect;
|
popoutBounds: null | DOMRect;
|
||||||
|
volumeChangedWithKeybind: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
function getInterfaceFromState(state: VideoPlayerState): VideoInterfaceEvent {
|
function getInterfaceFromState(state: VideoPlayerState): VideoInterfaceEvent {
|
||||||
@ -18,6 +19,7 @@ function getInterfaceFromState(state: VideoPlayerState): VideoInterfaceEvent {
|
|||||||
isFocused: state.interface.isFocused,
|
isFocused: state.interface.isFocused,
|
||||||
isFullscreen: state.interface.isFullscreen,
|
isFullscreen: state.interface.isFullscreen,
|
||||||
popoutBounds: state.interface.popoutBounds,
|
popoutBounds: state.interface.popoutBounds,
|
||||||
|
volumeChangedWithKeybind: state.interface.volumeChangedWithKeybind,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ export type VideoPlayerStateController = {
|
|||||||
setSeeking(active: boolean): void;
|
setSeeking(active: boolean): void;
|
||||||
exitFullscreen(): void;
|
exitFullscreen(): void;
|
||||||
enterFullscreen(): void;
|
enterFullscreen(): void;
|
||||||
setVolume(volume: number): void;
|
setVolume(volume: number, isKeyboardEvent?: boolean): void;
|
||||||
startAirplay(): void;
|
startAirplay(): void;
|
||||||
setCaption(id: string, url: string): void;
|
setCaption(id: string, url: string): void;
|
||||||
clearCaption(): void;
|
clearCaption(): void;
|
||||||
|
@ -28,6 +28,7 @@ export type VideoPlayerState = {
|
|||||||
isFullscreen: boolean;
|
isFullscreen: boolean;
|
||||||
popout: string | null; // id of current popout (eg source select, episode select)
|
popout: string | null; // id of current popout (eg source select, episode select)
|
||||||
isFocused: boolean; // is the video player the users focus? (shortcuts only works when its focused)
|
isFocused: boolean; // is the video player the users focus? (shortcuts only works when its focused)
|
||||||
|
volumeChangedWithKeybind: boolean; // has the volume recently been adjusted with the up/down arrows recently?
|
||||||
leftControlHovering: boolean; // is the cursor hovered over the left side of player controls
|
leftControlHovering: boolean; // is the cursor hovered over the left side of player controls
|
||||||
popoutBounds: null | DOMRect; // bounding box of current popout
|
popoutBounds: null | DOMRect; // bounding box of current popout
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user