summaryrefslogtreecommitdiff
path: root/web/js/player.js
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-05 23:07:35 +0300
committerPaul Buetow <paul@buetow.org>2026-05-05 23:07:35 +0300
commit69a3a88b85ed0a56bf442af4fec420c871d337fa (patch)
treeb1c01a820454cb43fe5f9ecf8c3af1f250a1b534 /web/js/player.js
parentf96c13de4828a3d531f4dc62f63ba571542c3ece (diff)
Add fullscreen crop mode toggle (hotkey c)
- In fullscreen, press 'c' to toggle object-fit between contain (default) and fill (stretches to fill, no aspect ratio preservation). - Adds .player.crop-mode video { object-fit: fill; } in player.css. - Adds toggleCrop() in player.js that toggles a crop-mode class and a small UI indicator (✂️) in the controls, visible only in fullscreen. - Resets crop mode back to contain when exiting fullscreen (via Escape, minimize, or fullscreenchange event). - Wires the 'c' hotkey in keyboard.js and registers toggleCrop in app.js. - Adds crop-indicator to index.html and detach.html controls, and documents the shortcut in the help modal.
Diffstat (limited to 'web/js/player.js')
-rw-r--r--web/js/player.js27
1 files changed, 26 insertions, 1 deletions
diff --git a/web/js/player.js b/web/js/player.js
index 6da8954..fea4a19 100644
--- a/web/js/player.js
+++ b/web/js/player.js
@@ -21,6 +21,7 @@ let isPanning = false;
let panStart = { x: 0, y: 0 };
let slideshowTimer = null;
let slideshowPausedUntil = 0;
+let cropMode = false;
const els = () => ({
video: document.getElementById('media-video'),
@@ -47,6 +48,7 @@ const els = () => ({
volume: document.getElementById('volume-slider'),
timeElapsed: document.getElementById('time-elapsed'),
timeTotal: document.getElementById('time-total'),
+ cropIndicator: document.getElementById('crop-indicator'),
});
export function initPlayer(options = {}) {
@@ -134,6 +136,16 @@ export function initPlayer(options = {}) {
e.video.addEventListener('loadedmetadata', updateFloatingSize);
+ document.addEventListener('fullscreenchange', () => {
+ const p = e.player;
+ if (!p) return;
+ if (!document.fullscreenElement) {
+ p.classList.remove('crop-mode');
+ cropMode = false;
+ if (e.cropIndicator) e.cropIndicator.classList.add('hidden');
+ }
+ });
+
function setupVideoDebug(m) {
const events = ['loadstart','loadeddata','loadedmetadata','canplay','canplaythrough','playing','waiting','stalled','suspend','error','abort','emptied','ended'];
events.forEach(event => {
@@ -521,10 +533,23 @@ function updateMinimizedTitle() {
export function exitFullscreenIfNeeded() {
if (document.fullscreenElement) {
document.exitFullscreen().catch(() => {});
- els().player?.classList.remove('is-fullscreen');
+ const p = els().player;
+ if (p) p.classList.remove('is-fullscreen', 'crop-mode');
+ cropMode = false;
+ const ind = els().cropIndicator;
+ if (ind) ind.classList.add('hidden');
}
}
+export function toggleCrop() {
+ const e = els();
+ if (!e.player) return;
+ if (!document.fullscreenElement) return;
+ cropMode = !cropMode;
+ e.player.classList.toggle('crop-mode', cropMode);
+ if (e.cropIndicator) e.cropIndicator.classList.toggle('hidden', !cropMode);
+}
+
function startProgressTimer() {
stopProgressTimer();
if (!reportProgress) return;