diff --git a/VERSION b/VERSION index 2f29dcd..674d109 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.120 +0.0.121 diff --git a/frontend/static/app.js b/frontend/static/app.js index c7e9b51..842a3c6 100644 --- a/frontend/static/app.js +++ b/frontend/static/app.js @@ -540,14 +540,9 @@ const Live = { if (this._fsBound) return; this._fsBound = true; const onFs = () => { - // дать браузеру применить размеры, затем пересчитать canvas плееров - setTimeout(() => { - document.querySelectorAll(".hevc-player").forEach((b) => { - if (b._wc && typeof b._wc.resize === "function") { try { b._wc.resize(); } catch (e) { /**/ } } - if (b._hevc && typeof b._hevc.resize === "function") { try { b._hevc.resize(); } catch (e) { /**/ } } - }); - window.dispatchEvent(new Event("resize")); - }, 120); + // выход из фуллскрина (в т.ч. по ESC) — снять «соло», вернуться к сетке + if (!(document.fullscreenElement || document.webkitFullscreenElement)) this.clearSolo(); + this.resizePlayers(); }; document.addEventListener("fullscreenchange", onFs); document.addEventListener("webkitfullscreenchange", onFs); @@ -662,10 +657,10 @@ const Live = { grid.className = "grid"; grid.style.gridTemplateColumns = `repeat(${this.dim}, 1fr)`; grid.style.gridTemplateRows = `repeat(${this.dim}, 1fr)`; - grid.ondblclick = (e) => { // двойной клик по ячейке → fullscreen + grid.ondblclick = (e) => { // двойной клик по ячейке → «соло» (камера на весь экран) if (e.target.closest(".tile-btn")) return; const tile = e.target.closest(".tile"); - if (tile) toggleFullscreen(tile); + if (tile) this.toggleSolo(tile); }; grid.onclick = (e) => { // кнопка переключения на основной поток const btn = e.target.closest(".tile-btn"); @@ -719,6 +714,36 @@ const Live = { this.setTileMedia(tile, cam, useMain); }, + resizePlayers() { // пересчитать canvas плееров после смены размера ячейки + setTimeout(() => { + document.querySelectorAll(".hevc-player").forEach((b) => { + if (b._wc && typeof b._wc.resize === "function") { try { b._wc.resize(); } catch (e) { /**/ } } + if (b._hevc && typeof b._hevc.resize === "function") { try { b._hevc.resize(); } catch (e) { /**/ } } + }); + window.dispatchEvent(new Event("resize")); + }, 120); + }, + + clearSolo() { + const grid = document.getElementById("grid"); + if (!grid) return; + grid.classList.remove("solo"); + grid.querySelectorAll(".tile.solo").forEach((t) => t.classList.remove("solo")); + }, + + toggleSolo(tile) { // одна камера на весь экран (внутри полноэкранного режима) + const grid = document.getElementById("grid"); + const wasSolo = grid.classList.contains("solo") && tile.classList.contains("solo"); + this.clearSolo(); + if (!wasSolo) { // включить «соло» этой ячейки + grid.classList.add("solo"); + tile.classList.add("solo"); + // ещё не в полноэкранном — войти, чтобы камера заняла весь экран (выход — ESC) + if (!(document.fullscreenElement || document.webkitFullscreenElement)) toggleFullscreen(grid); + } // повторный двойной клик — назад к сетке (фуллскрин сохраняется) + this.resizePlayers(); + }, + updateDots() { document.querySelectorAll(".tile .label[data-cam]").forEach((lbl) => { const cam = this.cameras.find((c) => c.id === lbl.dataset.cam); diff --git a/frontend/static/style.css b/frontend/static/style.css index 520ef85..25cf225 100644 --- a/frontend/static/style.css +++ b/frontend/static/style.css @@ -158,6 +158,9 @@ input[type="date"], input[type="number"], select { background: #000; overflow: hidden; grid-auto-rows: 1fr; grid-auto-columns: 1fr; /* все дорожки строго равны */ } +/* «Соло»: одна камера на весь экран (двойной клик по ячейке в полноэкранном режиме) */ +.grid.solo { grid-template-columns: 1fr !important; grid-template-rows: 1fr !important; } +.grid.solo > .tile:not(.solo) { display: none; } .tile { position: relative; background: #05070a; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; overflow: hidden;