Skip to content

Commit

Permalink
Hide video overlay when mouse stops moving
Browse files Browse the repository at this point in the history
  • Loading branch information
undyingwraith committed Jan 26, 2025
1 parent 1df40c8 commit 5d4d2f6
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@
z-index: 1;
width: 100%;
height: 100%;
visibility: hidden;
opacity: 0;
color: #fff;
background-color: #3232324d;
transition: all 0.3s;
}

.innerContainer:hover .videoOverlay {
visibility: visible;
.videoOverlay.visible {
opacity: 1;
}

.video {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ declare const styles: {
readonly toolbar: string;
readonly progress: string;
readonly progressFilled: string;
readonly visible: string;
};
export default styles;
139 changes: 84 additions & 55 deletions packages/ui/src/components/organisms/VideoPlayer/VideoPlayer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Fullscreen, FullscreenExit, Pause, PlayArrow, VolumeDown, VolumeUp } from '@mui/icons-material';
import { IconButton, Slider, Stack } from '@mui/material';
import { useComputed, useSignal, useSignalEffect } from '@preact/signals-react';
import { computed, useComputed, useSignal, useSignalEffect } from '@preact/signals-react';
import { IIpfsService, IIpfsServiceSymbol, IVideoFile } from 'ipmc-interfaces';
import React from 'react';
//@ts-ignore
Expand Down Expand Up @@ -42,16 +42,39 @@ export function VideoPlayer(props: { file: IVideoFile; autoPlay?: boolean; }) {
const playing = useSignal<boolean>(props.autoPlay ?? false);
const fullScreen = useSignal<boolean>(false);
const volume = useSignal<number>(1);
const overlayVisible = useSignal<boolean>(false);

useSignalEffect(() => {
if (containerRef.value != null) {
let timeout: NodeJS.Timeout;
const abortController = new AbortController();
containerRef.value.addEventListener('mousemove', () => {
overlayVisible.value = true;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
overlayVisible.value = false;
}, 3000);
}, { signal: abortController.signal });

return () => {
abortController.abort();
};
}

return () => { };
});

useSignalEffect(() => {
if (videoRef.value != null && progressRef.value != null) {
//Event handlers
videoRef.value.addEventListener("timeupdate", handleProgress);
progressRef.value.addEventListener("click", scrub);
videoRef.value.addEventListener('timeupdate', handleProgress);
progressRef.value.addEventListener('click', scrub);
let mousedown = false;
progressRef.value.addEventListener("mousedown", () => (mousedown = true));
progressRef.value.addEventListener("mousemove", (e) => mousedown && scrub(e));
progressRef.value.addEventListener("mouseup", () => (mousedown = false));
progressRef.value.addEventListener('mousedown', () => (mousedown = true));
progressRef.value.addEventListener('mousemove', (e) => mousedown && scrub(e));
progressRef.value.addEventListener('mouseup', () => (mousedown = false));

// Shaka player init
shaka.net.NetworkingEngine.registerScheme('ipfs', createShakaIpfsPlugin(ipfs), 1, false);
Expand Down Expand Up @@ -135,61 +158,67 @@ export function VideoPlayer(props: { file: IVideoFile; autoPlay?: boolean; }) {
useHotkey({ key: 'F' }, () => toggleFullScreen());
useHotkey({ key: 'Space' }, () => togglePlay());

const progress = useComputed(() => (
<div className={styles.progress} ref={(ref) => progressRef.value = ref}>
<div className={styles.progressFilled} ref={(ref) => progressBarRef.value = ref} />
</div>
));

return (
<div className={styles.outerContainer}>
<div className={styles.innerContainer} ref={(ref) => containerRef.value = ref}>
<div className={styles.videoOverlay}>
<div>
<FileInfoDisplay file={props.file} />
</div>
<div className={styles.spacer} />
<div className={styles.toolbar}>
<IconButton onClick={() => togglePlay()}>
{useComputed(() => playing.value ? <Pause /> : <PlayArrow />)}
</IconButton>
<div className={styles.spacer} />
{useComputed(() => (
<div className={`${styles.videoOverlay} ${overlayVisible.value ? styles.visible : ''}`}>
<div>
Language
<select>
{useComputed(() => languages.value.map(l => (
<option>{l}</option>
)))}
</select>
Subtitle
<select onChange={(ev) => {
if (ev.currentTarget.value !== 'null') {
playerRef.value.selectTextTrack(ev.currentTarget.value);
playerRef.value.setTextTrackVisibility(true);
} else {
playerRef.value.setTextTrackVisibility(false);
}
}}>
<option value="null">None</option>
{useComputed(() => subtitles.value.map(l => (
<option>{l.language}</option>
)))}
</select>
<FileInfoDisplay file={props.file} />
</div>
<Stack spacing={2} direction="row" sx={{ alignItems: 'center', width: 250 }}>
<VolumeDown />
{useComputed(() => (
<Slider
value={volume.value}
onChange={(_, value) => volume.value = value as number}
min={0}
max={1}
step={0.05} />
))}
<VolumeUp />
</Stack>
<IconButton onClick={() => toggleFullScreen()}>
{useComputed(() => playing.value ? <FullscreenExit /> : <Fullscreen />)}
</IconButton>
</div>
<div className={styles.progress} ref={(ref) => progressRef.value = ref}>
<div className={styles.progressFilled} ref={(ref) => progressBarRef.value = ref} />
<div className={styles.spacer} />
<div className={styles.toolbar}>
<IconButton onClick={() => togglePlay()}>
{computed(() => playing.value ? <Pause /> : <PlayArrow />)}
</IconButton>
<div className={styles.spacer} />
<div>
Language
<select>
{computed(() => languages.value.map(l => (
<option>{l}</option>
)))}
</select>
Subtitle
<select onChange={(ev) => {
if (ev.currentTarget.value !== 'null') {
playerRef.value.selectTextTrack(ev.currentTarget.value);
playerRef.value.setTextTrackVisibility(true);
} else {
playerRef.value.setTextTrackVisibility(false);
}
}}>
<option value="null">None</option>
{computed(() => subtitles.value.map(l => (
<option>{l.language}</option>
)))}
</select>
</div>
<Stack spacing={2} direction="row" sx={{ alignItems: 'center', width: 250 }}>
<VolumeDown />
{computed(() => (
<Slider
value={volume.value}
onChange={(_, value) => volume.value = value as number}
min={0}
max={1}
step={0.05} />
))}
<VolumeUp />
</Stack>
<IconButton onClick={() => toggleFullScreen()}>
{computed(() => playing.value ? <FullscreenExit /> : <Fullscreen />)}
</IconButton>
</div>
{progress}
</div>
</div>
))}
<video
controls={false}
ref={(ref) => {
Expand Down
1 change: 1 addition & 0 deletions packages/ui/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default defineConfig(({ mode }) => ({
globals: {
'@emotion/react': 'react',
'@emotion/styled': 'emStyled',
'@mui/icons-material': 'iconsMaterial',
'@mui/material': 'material',
'@preact/signals-react': 'signalsReact',
'file-type': 'fileType',
Expand Down

0 comments on commit 5d4d2f6

Please sign in to comment.