Skip to content

Commit

Permalink
feat(player): new hideControlsOnMouseLeave player prop
Browse files Browse the repository at this point in the history
  • Loading branch information
mihar-22 committed Dec 1, 2023
1 parent 1c82248 commit a481351
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 8 deletions.
6 changes: 6 additions & 0 deletions packages/vidstack/src/core/api/player-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const mediaPlayerProps: MediaPlayerProps = {
src: '',
title: '',
controlsDelay: 2000,
hideControlsOnMouseLeave: false,
viewType: 'unknown',
streamType: 'unknown',
volume: 1,
Expand Down Expand Up @@ -100,6 +101,11 @@ export interface MediaPlayerProps
* activity to indicate an idle state and hide controls.
*/
controlsDelay: number;
/**
* Whether controls visibility should be toggled when the mouse enters and leaves the player
* container.
*/
hideControlsOnMouseLeave: boolean;
/**
* This method will indicate the orientation to lock the screen to when in fullscreen mode and
* the Screen Orientation API is available.
Expand Down
59 changes: 51 additions & 8 deletions packages/vidstack/src/core/controls.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { effect } from 'maverick.js';
import { isKeyboardEvent, listenEvent } from 'maverick.js/std';
import { effect, signal } from 'maverick.js';
import { isKeyboardEvent } from 'maverick.js/std';

import { isTouchPinchEvent } from '../utils/dom';
import { MediaPlayerController } from './api/player-controller';

export class MediaControls extends MediaPlayerController {
private _idleTimer = -2;
private _pausedTracking = false;
private _hideOnMouseLeave = signal(false);
private _isMouseOutside = signal(false);
private _focusedItem: HTMLElement | null = null;

/**
Expand All @@ -17,6 +19,21 @@ export class MediaControls extends MediaPlayerController {
*/
defaultDelay = 2000;

/**
* Whether controls visibility should be toggled when the mouse enters and leaves the player
* container.
*
* @defaultValue false
*/
get hideOnMouseLeave() {
const { hideControlsOnMouseLeave } = this.$props;
return this._hideOnMouseLeave() || hideControlsOnMouseLeave();
}

set hideOnMouseLeave(hide) {
this._hideOnMouseLeave.set(hide);
}

/**
* Whether media controls are currently visible.
*/
Expand Down Expand Up @@ -60,6 +77,7 @@ export class MediaControls extends MediaPlayerController {
}

protected override onConnect() {
effect(this._watchMouse.bind(this));
effect(this._watchPaused.bind(this));

const onPlay = this._onPlay.bind(this),
Expand All @@ -72,6 +90,24 @@ export class MediaControls extends MediaPlayerController {
this.listen('autoplay-fail', onPause);
}

private _watchMouse() {
const { started, pointer, paused } = this.$state;
if (!started() || pointer() !== 'fine') return;

const shouldHideOnMouseLeave = this.hideOnMouseLeave;

if (!shouldHideOnMouseLeave || !this._isMouseOutside()) {
effect(() => {
if (!paused()) this.listen('pointermove', this._onStopIdle.bind(this));
});
}

if (shouldHideOnMouseLeave) {
this.listen('mouseenter', this._onMouseEnter.bind(this));
this.listen('mouseleave', this._onMouseLeave.bind(this));
}
}

private _watchPaused() {
const { paused, started, autoplayError } = this.$state;
if (paused() || (autoplayError() && !started())) return;
Expand All @@ -84,24 +120,31 @@ export class MediaControls extends MediaPlayerController {
events = [isTouch ? 'touchend' : 'pointerup', 'keydown'] as const;

for (const eventType of events) {
listenEvent(this.el!, eventType, onStopIdle, { passive: false });
}

if (!isTouch) {
listenEvent(this.el!, 'pointermove', onStopIdle);
this.listen(eventType, onStopIdle, { passive: false });
}
});
}

private _onPlay(event: Event) {
this.show(0, event);
this.hide(this.defaultDelay, event);
this.hide(undefined, event);
}

private _onPause(event: Event) {
this.show(0, event);
}

private _onMouseEnter(event: Event) {
this._isMouseOutside.set(false);
this.show(0, event);
this.hide(undefined, event);
}

private _onMouseLeave(event: Event) {
this._isMouseOutside.set(true);
this.hide(0, event);
}

private _clearIdleTimer() {
window.clearTimeout(this._idleTimer);
this._idleTimer = -1;
Expand Down

0 comments on commit a481351

Please sign in to comment.