регулировка ресайза
This commit is contained in:
@@ -166,7 +166,7 @@ h1 {
|
|||||||
|
|
||||||
.fill {
|
.fill {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 0%;
|
width: 0;
|
||||||
background: #e6852d;
|
background: #e6852d;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
inset 0 0 4px rgba(255,255,255,0.2),
|
inset 0 0 4px rgba(255,255,255,0.2),
|
||||||
@@ -177,7 +177,7 @@ h1 {
|
|||||||
.fill.no-connection {
|
.fill.no-connection {
|
||||||
background: #555555 !important;
|
background: #555555 !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
width: 0% !important;
|
width: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== SIGNAL ICON ===== */
|
/* ===== SIGNAL ICON ===== */
|
||||||
@@ -194,7 +194,7 @@ h1 {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
min-width: 5px;
|
min-width: 5px;
|
||||||
border: 1.5px solid #e6852d;
|
border: 2px solid #e6852d;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
transition: background-color 0.3s, border-color 0.3s;
|
transition: background-color 0.3s, border-color 0.3s;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
// camera.ts
|
// camera.ts
|
||||||
import { updateResizerVisibility } from './layout.js';
|
import { updateResizerVisibility, openRightPanel, closeRightPanel } from './layout.js';
|
||||||
|
|
||||||
let rightPanel: HTMLElement | null = null;
|
let rightPanel: HTMLElement | null = null;
|
||||||
let camImage: HTMLImageElement | null = null;
|
let camImage: HTMLImageElement | null = null;
|
||||||
let crosshair: HTMLElement | null = null;
|
let crosshair: HTMLElement | null = null;
|
||||||
let isOpen: boolean = false;
|
let isOpen: boolean = false;
|
||||||
let streamCheckInterval: number | null = null;
|
let streamCheckInterval: number | null = null;
|
||||||
|
let resizer: HTMLElement | null = null;
|
||||||
interface StreamResponse {
|
|
||||||
cam: string;
|
|
||||||
available: boolean;
|
|
||||||
source: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initCamera(): void {
|
export function initCamera(): void {
|
||||||
rightPanel = document.getElementById("rightPanel");
|
rightPanel = document.getElementById("rightPanel");
|
||||||
camImage = document.getElementById("camImage") as HTMLImageElement;
|
camImage = document.getElementById("camImage") as HTMLImageElement;
|
||||||
crosshair = document.getElementById("crosshair");
|
crosshair = document.getElementById("crosshair");
|
||||||
|
resizer = document.getElementById("dragBar");
|
||||||
|
|
||||||
const btn = document.getElementById("camToggle");
|
const btn = document.getElementById("camToggle");
|
||||||
|
|
||||||
@@ -28,9 +23,8 @@ export function initCamera(): void {
|
|||||||
|
|
||||||
isOpen = true;
|
isOpen = true;
|
||||||
|
|
||||||
if (rightPanel) {
|
// Открываем правую панель через layout
|
||||||
rightPanel.classList.remove("hidden");
|
openRightPanel();
|
||||||
}
|
|
||||||
|
|
||||||
// Показываем перекрестие
|
// Показываем перекрестие
|
||||||
if (crosshair) {
|
if (crosshair) {
|
||||||
@@ -44,13 +38,15 @@ export function initCamera(): void {
|
|||||||
|
|
||||||
if (btn) {
|
if (btn) {
|
||||||
const textSpan = btn.querySelector('.cam-btn-text');
|
const textSpan = btn.querySelector('.cam-btn-text');
|
||||||
if (textSpan) textSpan.textContent = 'Скрыть';
|
if (textSpan) {
|
||||||
|
textSpan.textContent = 'Скрыть';
|
||||||
|
} else {
|
||||||
|
btn.innerHTML = '📷 Скрыть';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[camera] opened");
|
console.log("[camera] opened");
|
||||||
|
|
||||||
updateResizerVisibility();
|
|
||||||
|
|
||||||
// Запускаем мониторинг потока
|
// Запускаем мониторинг потока
|
||||||
startStreamMonitoring();
|
startStreamMonitoring();
|
||||||
}
|
}
|
||||||
@@ -65,24 +61,25 @@ export function initCamera(): void {
|
|||||||
crosshair.style.display = "none";
|
crosshair.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
// остановка stream
|
// Останавливаем поток
|
||||||
if (camImage) {
|
if (camImage) {
|
||||||
camImage.src = "";
|
camImage.src = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rightPanel) {
|
// Закрываем правую панель через layout
|
||||||
rightPanel.classList.add("hidden");
|
closeRightPanel();
|
||||||
}
|
|
||||||
|
|
||||||
if (btn) {
|
if (btn) {
|
||||||
const textSpan = btn.querySelector('.cam-btn-text');
|
const textSpan = btn.querySelector('.cam-btn-text');
|
||||||
if (textSpan) textSpan.textContent = 'Камера';
|
if (textSpan) {
|
||||||
|
textSpan.textContent = 'Камера';
|
||||||
|
} else {
|
||||||
|
btn.innerHTML = '📷 Камера';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("[camera] closed");
|
console.log("[camera] closed");
|
||||||
|
|
||||||
updateResizerVisibility();
|
|
||||||
|
|
||||||
// Останавливаем мониторинг
|
// Останавливаем мониторинг
|
||||||
stopStreamMonitoring();
|
stopStreamMonitoring();
|
||||||
}
|
}
|
||||||
@@ -139,7 +136,15 @@ export function initCamera(): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Экспортируем для возможности ручного управления
|
// ============================================
|
||||||
|
// ПУБЛИЧНЫЕ ФУНКЦИИ (используются в других модулях)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Показать/скрыть перекрестие на видео
|
||||||
|
* @param show - показывать или скрыть
|
||||||
|
*/
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
export function showCrosshair(show: boolean = true): void {
|
export function showCrosshair(show: boolean = true): void {
|
||||||
const crosshairElem = document.getElementById("crosshair");
|
const crosshairElem = document.getElementById("crosshair");
|
||||||
if (crosshairElem) {
|
if (crosshairElem) {
|
||||||
@@ -147,9 +152,14 @@ export function showCrosshair(show: boolean = true): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Установить прозрачность перекрестия
|
||||||
|
* @param opacity - значение прозрачности (0-1)
|
||||||
|
*/
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
export function setCrosshairOpacity(opacity: string | number): void {
|
export function setCrosshairOpacity(opacity: string | number): void {
|
||||||
const crosshairElem = document.getElementById("crosshair");
|
const crosshairElem = document.getElementById("crosshair");
|
||||||
if (crosshairElem) {
|
if (crosshairElem) {
|
||||||
crosshairElem.style.opacity = String(opacity);
|
crosshairElem.style.opacity = String(opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,88 +1,236 @@
|
|||||||
// layout.ts
|
// layout.ts
|
||||||
|
let resizer: HTMLElement | null = null;
|
||||||
let leftPanel: HTMLElement | null = null;
|
let leftPanel: HTMLElement | null = null;
|
||||||
let rightPanel: HTMLElement | null = null;
|
let rightPanel: HTMLElement | null = null;
|
||||||
let dragBar: HTMLElement | null = null;
|
let isOpen: boolean = false;
|
||||||
let isDragging: boolean = false;
|
let lastLeftWidth: number = 0;
|
||||||
let pendingX: number | null = null;
|
let lastRightWidth: number = 0;
|
||||||
|
|
||||||
export function initResize(): void {
|
export function initResize(): void {
|
||||||
|
resizer = document.getElementById("dragBar");
|
||||||
leftPanel = document.getElementById("leftPanel");
|
leftPanel = document.getElementById("leftPanel");
|
||||||
rightPanel = document.getElementById("rightPanel");
|
rightPanel = document.getElementById("rightPanel");
|
||||||
dragBar = document.getElementById("dragBar");
|
|
||||||
|
|
||||||
if (!leftPanel || !rightPanel || !dragBar) return;
|
if (!resizer || !leftPanel || !rightPanel) {
|
||||||
|
console.warn("[layout] Элементы не найдены");
|
||||||
const savedRatio = localStorage.getItem('layoutRatio');
|
return;
|
||||||
if (savedRatio) {
|
|
||||||
applyRatio(parseFloat(savedRatio));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dragBar.addEventListener("mousedown", (e) => {
|
// Проверяем видимость при инициализации
|
||||||
if (rightPanel!.classList.contains("hidden")) return;
|
updateResizerVisibility();
|
||||||
|
|
||||||
|
let isDragging = false;
|
||||||
|
|
||||||
|
// Обработчик начала перетаскивания
|
||||||
|
resizer.addEventListener("mousedown", (e: MouseEvent) => {
|
||||||
|
// Сохраняем ссылки на элементы в локальные переменные
|
||||||
|
const left = leftPanel;
|
||||||
|
const right = rightPanel;
|
||||||
|
|
||||||
|
// Проверяем, что элементы существуют
|
||||||
|
if (!left || !right) {
|
||||||
|
console.warn("[layout] Панели не найдены");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что правая панель видима
|
||||||
|
if (right.classList.contains("hidden")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
document.body.style.cursor = "col-resize";
|
document.body.style.cursor = "col-resize";
|
||||||
e.preventDefault();
|
document.body.style.userSelect = "none";
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener("mouseup", () => {
|
// Сохраняем начальные размеры
|
||||||
if (!isDragging) return;
|
const startX = e.clientX;
|
||||||
|
const leftWidth = left.getBoundingClientRect().width;
|
||||||
|
const rightWidth = right.getBoundingClientRect().width;
|
||||||
|
const totalWidth = leftWidth + rightWidth;
|
||||||
|
|
||||||
isDragging = false;
|
function onMouseMove(moveEvent: MouseEvent): void {
|
||||||
document.body.style.cursor = "default";
|
if (!isDragging) return;
|
||||||
|
|
||||||
if (rightPanel && !rightPanel.classList.contains("hidden")) {
|
// Проверяем, что элементы всё ещё существуют
|
||||||
const total = window.innerWidth;
|
if (!left || !right) return;
|
||||||
const leftWidth = leftPanel!.getBoundingClientRect().width;
|
|
||||||
localStorage.setItem('layoutRatio', String(leftWidth / total));
|
const deltaX = moveEvent.clientX - startX;
|
||||||
|
let newLeftWidth = leftWidth + deltaX;
|
||||||
|
|
||||||
|
// Ограничиваем минимальные размеры
|
||||||
|
const minWidth = 200;
|
||||||
|
newLeftWidth = Math.max(minWidth, Math.min(totalWidth - minWidth, newLeftWidth));
|
||||||
|
|
||||||
|
const newRightWidth = totalWidth - newLeftWidth;
|
||||||
|
|
||||||
|
left.style.flex = `0 0 ${newLeftWidth}px`;
|
||||||
|
right.style.flex = `0 0 ${newRightWidth}px`;
|
||||||
|
|
||||||
|
// Сохраняем последние размеры
|
||||||
|
lastLeftWidth = newLeftWidth;
|
||||||
|
lastRightWidth = newRightWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onMouseUp(): void {
|
||||||
|
isDragging = false;
|
||||||
|
document.body.style.cursor = "";
|
||||||
|
document.body.style.userSelect = "";
|
||||||
|
|
||||||
|
document.removeEventListener("mousemove", onMouseMove);
|
||||||
|
document.removeEventListener("mouseup", onMouseUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("mousemove", onMouseMove);
|
||||||
|
document.addEventListener("mouseup", onMouseUp);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("mousemove", (e) => {
|
// Обработчик изменения размера окна
|
||||||
if (!isDragging) return;
|
window.addEventListener("resize", () => {
|
||||||
if (rightPanel && rightPanel.classList.contains("hidden")) return;
|
handleWindowResize();
|
||||||
|
|
||||||
pendingX = e.clientX;
|
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
if (pendingX === null) return;
|
|
||||||
if (!leftPanel || !rightPanel || !dragBar) return;
|
|
||||||
|
|
||||||
const total = window.innerWidth;
|
|
||||||
let leftWidth = pendingX;
|
|
||||||
|
|
||||||
const min = 300;
|
|
||||||
if (leftWidth < min) leftWidth = min;
|
|
||||||
if (leftWidth > total - min) leftWidth = total - min;
|
|
||||||
|
|
||||||
const rightWidth = total - leftWidth - dragBar.offsetWidth;
|
|
||||||
|
|
||||||
leftPanel.style.flex = `0 0 ${leftWidth}px`;
|
|
||||||
rightPanel.style.flex = `0 0 ${rightWidth}px`;
|
|
||||||
|
|
||||||
pendingX = null;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyRatio(ratio: number): void {
|
/**
|
||||||
const total = window.innerWidth;
|
* Обработка изменения размера окна
|
||||||
const leftWidth = Math.floor(total * ratio);
|
*/
|
||||||
const rightWidth = total - leftWidth;
|
function handleWindowResize(): void {
|
||||||
|
if (!leftPanel || !rightPanel) return;
|
||||||
|
|
||||||
if (leftPanel && rightPanel) {
|
// Если правая панель видима, пересчитываем размеры
|
||||||
leftPanel.style.flex = `0 0 ${leftWidth}px`;
|
if (!rightPanel.classList.contains("hidden")) {
|
||||||
rightPanel.style.flex = `0 0 ${rightWidth}px`;
|
// Если есть сохраненные размеры, используем их пропорционально
|
||||||
|
if (lastLeftWidth > 0 && lastRightWidth > 0) {
|
||||||
|
const totalWidth = window.innerWidth;
|
||||||
|
const ratio = lastLeftWidth / (lastLeftWidth + lastRightWidth);
|
||||||
|
const newLeftWidth = totalWidth * ratio;
|
||||||
|
const newRightWidth = totalWidth - newLeftWidth;
|
||||||
|
|
||||||
|
// Ограничиваем минимальные размеры
|
||||||
|
const minWidth = 200;
|
||||||
|
const finalLeftWidth = Math.max(minWidth, Math.min(totalWidth - minWidth, newLeftWidth));
|
||||||
|
const finalRightWidth = totalWidth - finalLeftWidth;
|
||||||
|
|
||||||
|
leftPanel.style.flex = `0 0 ${finalLeftWidth}px`;
|
||||||
|
rightPanel.style.flex = `0 0 ${finalRightWidth}px`;
|
||||||
|
|
||||||
|
lastLeftWidth = finalLeftWidth;
|
||||||
|
lastRightWidth = finalRightWidth;
|
||||||
|
} else {
|
||||||
|
// Если нет сохраненных размеров, делим пополам
|
||||||
|
const totalWidth = window.innerWidth;
|
||||||
|
const halfWidth = totalWidth / 2;
|
||||||
|
leftPanel.style.flex = `0 0 ${halfWidth}px`;
|
||||||
|
rightPanel.style.flex = `0 0 ${halfWidth}px`;
|
||||||
|
|
||||||
|
lastLeftWidth = halfWidth;
|
||||||
|
lastRightWidth = halfWidth;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Обновление видимости разделителя в зависимости от состояния правой панели
|
||||||
|
*/
|
||||||
export function updateResizerVisibility(): void {
|
export function updateResizerVisibility(): void {
|
||||||
if (!dragBar || !rightPanel || !leftPanel) return;
|
if (!resizer || !rightPanel) return;
|
||||||
|
|
||||||
|
// Если правая панель скрыта - скрываем разделитель
|
||||||
|
if (rightPanel.classList.contains("hidden")) {
|
||||||
|
resizer.classList.add("hidden");
|
||||||
|
} else {
|
||||||
|
resizer.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Открыть правую панель
|
||||||
|
*/
|
||||||
|
export function openRightPanel(): void {
|
||||||
|
if (!leftPanel || !rightPanel) return;
|
||||||
|
|
||||||
|
// Убираем класс hidden
|
||||||
|
rightPanel.classList.remove("hidden");
|
||||||
|
|
||||||
|
// Обновляем разделитель
|
||||||
|
updateResizerVisibility();
|
||||||
|
|
||||||
|
// Устанавливаем размеры
|
||||||
|
const totalWidth = window.innerWidth;
|
||||||
|
const halfWidth = totalWidth / 2;
|
||||||
|
|
||||||
|
// Проверяем, есть ли сохраненные размеры
|
||||||
|
if (lastLeftWidth > 0 && lastRightWidth > 0) {
|
||||||
|
// Используем сохраненные пропорции
|
||||||
|
const ratio = lastLeftWidth / (lastLeftWidth + lastRightWidth);
|
||||||
|
const newLeftWidth = totalWidth * ratio;
|
||||||
|
const newRightWidth = totalWidth - newLeftWidth;
|
||||||
|
|
||||||
|
const minWidth = 200;
|
||||||
|
const finalLeftWidth = Math.max(minWidth, Math.min(totalWidth - minWidth, newLeftWidth));
|
||||||
|
const finalRightWidth = totalWidth - finalLeftWidth;
|
||||||
|
|
||||||
|
leftPanel.style.flex = `0 0 ${finalLeftWidth}px`;
|
||||||
|
rightPanel.style.flex = `0 0 ${finalRightWidth}px`;
|
||||||
|
|
||||||
|
lastLeftWidth = finalLeftWidth;
|
||||||
|
lastRightWidth = finalRightWidth;
|
||||||
|
} else {
|
||||||
|
// Делим пополам
|
||||||
|
leftPanel.style.flex = `0 0 ${halfWidth}px`;
|
||||||
|
rightPanel.style.flex = `0 0 ${halfWidth}px`;
|
||||||
|
|
||||||
|
lastLeftWidth = halfWidth;
|
||||||
|
lastRightWidth = halfWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Закрыть правую панель
|
||||||
|
*/
|
||||||
|
export function closeRightPanel(): void {
|
||||||
|
if (!rightPanel) return;
|
||||||
|
|
||||||
|
// Добавляем класс hidden
|
||||||
|
rightPanel.classList.add("hidden");
|
||||||
|
|
||||||
|
// Обновляем разделитель
|
||||||
|
updateResizerVisibility();
|
||||||
|
|
||||||
|
// Сбрасываем flex для левой панели
|
||||||
|
if (leftPanel) {
|
||||||
|
leftPanel.style.flex = "1 1 auto";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// ПУБЛИЧНЫЕ ФУНКЦИИ (используются в других модулях)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Установить видимость разделителя вручную
|
||||||
|
* @param visible - показывать или скрыть
|
||||||
|
*/
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
export function setResizerVisible(visible: boolean): void {
|
||||||
|
if (resizer) {
|
||||||
|
if (visible) {
|
||||||
|
resizer.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
resizer.classList.add("hidden");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переключить состояние правой панели
|
||||||
|
*/
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
export function toggleRightPanel(): void {
|
||||||
|
if (!rightPanel) return;
|
||||||
|
|
||||||
if (rightPanel.classList.contains("hidden")) {
|
if (rightPanel.classList.contains("hidden")) {
|
||||||
dragBar.classList.add("hidden");
|
openRightPanel();
|
||||||
leftPanel.style.flex = "1";
|
|
||||||
} else {
|
} else {
|
||||||
dragBar.classList.remove("hidden");
|
closeRightPanel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user