live: double-click solos a cell to full screen (fullscreen mode)
Double-clicking a grid cell now maximizes that camera ("solo") via a CSS
class on the grid instead of putting the tile into the browser Fullscreen
API. In fullscreen the soloed camera fills the screen; double-clicking
again returns to the grid (staying fullscreen). From windowed mode the
gesture also enters fullscreen so the camera fills the screen. ESC exits
fullscreen natively; on exit the solo state is cleared.
VERSION -> 0.0.121.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+35
-10
@@ -540,14 +540,9 @@ const Live = {
|
|||||||
if (this._fsBound) return;
|
if (this._fsBound) return;
|
||||||
this._fsBound = true;
|
this._fsBound = true;
|
||||||
const onFs = () => {
|
const onFs = () => {
|
||||||
// дать браузеру применить размеры, затем пересчитать canvas плееров
|
// выход из фуллскрина (в т.ч. по ESC) — снять «соло», вернуться к сетке
|
||||||
setTimeout(() => {
|
if (!(document.fullscreenElement || document.webkitFullscreenElement)) this.clearSolo();
|
||||||
document.querySelectorAll(".hevc-player").forEach((b) => {
|
this.resizePlayers();
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
document.addEventListener("fullscreenchange", onFs);
|
document.addEventListener("fullscreenchange", onFs);
|
||||||
document.addEventListener("webkitfullscreenchange", onFs);
|
document.addEventListener("webkitfullscreenchange", onFs);
|
||||||
@@ -662,10 +657,10 @@ const Live = {
|
|||||||
grid.className = "grid";
|
grid.className = "grid";
|
||||||
grid.style.gridTemplateColumns = `repeat(${this.dim}, 1fr)`;
|
grid.style.gridTemplateColumns = `repeat(${this.dim}, 1fr)`;
|
||||||
grid.style.gridTemplateRows = `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;
|
if (e.target.closest(".tile-btn")) return;
|
||||||
const tile = e.target.closest(".tile");
|
const tile = e.target.closest(".tile");
|
||||||
if (tile) toggleFullscreen(tile);
|
if (tile) this.toggleSolo(tile);
|
||||||
};
|
};
|
||||||
grid.onclick = (e) => { // кнопка переключения на основной поток
|
grid.onclick = (e) => { // кнопка переключения на основной поток
|
||||||
const btn = e.target.closest(".tile-btn");
|
const btn = e.target.closest(".tile-btn");
|
||||||
@@ -719,6 +714,36 @@ const Live = {
|
|||||||
this.setTileMedia(tile, cam, useMain);
|
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() {
|
updateDots() {
|
||||||
document.querySelectorAll(".tile .label[data-cam]").forEach((lbl) => {
|
document.querySelectorAll(".tile .label[data-cam]").forEach((lbl) => {
|
||||||
const cam = this.cameras.find((c) => c.id === lbl.dataset.cam);
|
const cam = this.cameras.find((c) => c.id === lbl.dataset.cam);
|
||||||
|
|||||||
@@ -158,6 +158,9 @@ input[type="date"], input[type="number"], select {
|
|||||||
background: #000; overflow: hidden;
|
background: #000; overflow: hidden;
|
||||||
grid-auto-rows: 1fr; grid-auto-columns: 1fr; /* все дорожки строго равны */
|
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 {
|
.tile {
|
||||||
position: relative; background: #05070a; border: 1px solid var(--border);
|
position: relative; background: #05070a; border: 1px solid var(--border);
|
||||||
display: flex; align-items: center; justify-content: center; overflow: hidden;
|
display: flex; align-items: center; justify-content: center; overflow: hidden;
|
||||||
|
|||||||
Reference in New Issue
Block a user