Fix zoom in Firefox and Safari.

This commit is contained in:
Dermot Duffy
2023-05-22 20:37:18 -07:00
parent c156f094f5
commit 5a67c8bc2e
10 changed files with 223 additions and 26 deletions
+33 -14
View File
@@ -7,25 +7,42 @@ import { Timer } from './timer';
export const MEDIA_LOAD_CONTROLS_HIDE_SECONDS = 2;
const MEDIA_SEEK_CONTROLS_HIDE_SECONDS = 1;
export type FrigateCardHTMLVideoElement = HTMLVideoElement & {
_controlsHideTimer?: Timer;
};
/**
* Sets the controls on a video and removes a timer that may have been added by
* hideMediaControlsTemporarily.
* @param video
* @param value
*/
export const setControlsOnVideo = (
video: FrigateCardHTMLVideoElement,
value: boolean,
): void => {
if (video._controlsHideTimer) {
video._controlsHideTimer.stop();
delete video._controlsHideTimer;
}
video.controls = value;
};
/**
* Temporarily hide media controls.
* @param element Any HTMLElement that has a controls property (e.g.
* @param elemaent Any HTMLElement that has a controls property (e.g.
* HTMLVideoElement, FrigateCardHaHlsPlayer)
* @param seconds The number of seconds to hide the controls for.
*/
export const hideMediaControlsTemporarily = (
element: HTMLElement & {
controls: boolean;
_controlsHideTimer?: Timer;
},
video: FrigateCardHTMLVideoElement,
seconds = MEDIA_SEEK_CONTROLS_HIDE_SECONDS,
): void => {
element.controls = false;
element._controlsHideTimer ??= new Timer();
element._controlsHideTimer.start(seconds, () => {
element.controls = true;
delete element._controlsHideTimer;
const oldValue = video.controls;
setControlsOnVideo(video, false);
video._controlsHideTimer ??= new Timer();
video._controlsHideTimer.start(seconds, () => {
setControlsOnVideo(video, oldValue);
});
};
@@ -41,8 +58,10 @@ export const playMediaMutingIfNecessary = async (
// and then try again. This works around some browsers that prevent
// auto-play unless the video is muted.
if (video?.play) {
video.play().catch(async (ev) => {
if (ev.name === 'NotAllowedError' && !player.isMuted()) {
try {
await video.play();
} catch (err: unknown) {
if ((err as Error).name === 'NotAllowedError' && !player.isMuted()) {
await player.mute();
try {
await video.play();
@@ -50,6 +69,6 @@ export const playMediaMutingIfNecessary = async (
// Pass.
}
}
});
}
}
};
+20 -3
View File
@@ -1,5 +1,4 @@
import { PanzoomObject, PanzoomEventDetail } from '@dermotduffy/panzoom';
import Panzoom from '@dermotduffy/panzoom';
import Panzoom, { PanzoomEventDetail, PanzoomObject } from '@dermotduffy/panzoom';
import round from 'lodash-es/round';
import { dispatchFrigateCardEvent, isHoverableDevice } from '../basic';
@@ -11,6 +10,7 @@ export class Zoom {
protected _element: HTMLElement;
protected _panzoom?: PanzoomObject;
protected _zoomed = false;
protected _allowClick = true;
protected _events = isHoverableDevice()
? {
@@ -31,9 +31,23 @@ export class Zoom {
// If we do not prevent default here, the media carousels scroll.
ev.preventDefault();
this._allowClick = false;
} else {
this._allowClick = true;
}
};
protected _clickHandler = (ev: Event) => {
// When mouse clicking is used to pan, need to avoid that causing a click
// handler elsewhere in the card being called. Example: Viewing a snapshot,
// and panning within it should not cause a related clip to play (the click
// handler in the viewer).
if (!this._allowClick) {
ev.stopPropagation();
}
this._allowClick = true;
};
protected _moveHandler = (ev: Event) => {
if (this._shouldZoomOrPan(ev)) {
this._panzoom?.handleMove(ev as PointerEvent);
@@ -64,7 +78,9 @@ export class Zoom {
protected _shouldZoomOrPan(ev: Event): boolean {
return (
!this._isScaleNormal(this._panzoom?.getScale()) ||
(ev instanceof TouchEvent && ev.touches.length > 1) ||
// TouchEvent does not exist on Firefox on non-touch events.
// See: https://github.com/dermotduffy/frigate-hass-card/issues/1174
(window.TouchEvent && ev instanceof TouchEvent && ev.touches.length > 1) ||
(ev instanceof WheelEvent && ev.ctrlKey)
);
}
@@ -94,6 +110,7 @@ export class Zoom {
registerListeners(this._events['move'], this._moveHandler, { capture: true });
registerListeners(this._events['up'], this._upHandler, { capture: true });
registerListeners(['wheel'], this._wheelHandler);
registerListeners(['click'], this._clickHandler, { capture: true });
this._element.addEventListener('panzoomzoom', (ev: Event) => {
// Take care here to only dispatch the zoomed/unzoomed events when the