diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-05 23:07:35 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-05 23:07:35 +0300 |
| commit | 69a3a88b85ed0a56bf442af4fec420c871d337fa (patch) | |
| tree | b1c01a820454cb43fe5f9ecf8c3af1f250a1b534 /web/js/player.js | |
| parent | f96c13de4828a3d531f4dc62f63ba571542c3ece (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.js | 27 |
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; |
