Files
go-service/cmd/server/web/js/camera.ts
2026-06-17 15:59:53 +03:00

165 lines
4.7 KiB
TypeScript

// camera.ts
import { updateResizerVisibility, openRightPanel, closeRightPanel } from './layout.js';
let rightPanel: HTMLElement | null = null;
let camImage: HTMLImageElement | null = null;
let crosshair: HTMLElement | null = null;
let isOpen: boolean = false;
let streamCheckInterval: number | null = null;
let resizer: HTMLElement | null = null;
export function initCamera(): void {
rightPanel = document.getElementById("rightPanel");
camImage = document.getElementById("camImage") as HTMLImageElement;
crosshair = document.getElementById("crosshair");
resizer = document.getElementById("dragBar");
const btn = document.getElementById("camToggle");
const streamUrl = "/api/cam";
function open(): void {
if (isOpen) return;
isOpen = true;
// Открываем правую панель через layout
openRightPanel();
// Показываем перекрестие
if (crosshair) {
crosshair.style.display = "block";
crosshair.style.opacity = "0.5";
}
if (camImage) {
camImage.src = streamUrl;
}
if (btn) {
const textSpan = btn.querySelector('.cam-btn-text');
if (textSpan) {
textSpan.textContent = 'Скрыть';
} else {
btn.innerHTML = '📷 Скрыть';
}
}
console.log("[camera] opened");
// Запускаем мониторинг потока
startStreamMonitoring();
}
function close(): void {
if (!isOpen) return;
isOpen = false;
// Скрываем перекрестие
if (crosshair) {
crosshair.style.display = "none";
}
// Останавливаем поток
if (camImage) {
camImage.src = "";
}
// Закрываем правую панель через layout
closeRightPanel();
if (btn) {
const textSpan = btn.querySelector('.cam-btn-text');
if (textSpan) {
textSpan.textContent = 'Камера';
} else {
btn.innerHTML = '📷 Камера';
}
}
console.log("[camera] closed");
// Останавливаем мониторинг
stopStreamMonitoring();
}
function toggle(): void {
if (isOpen) {
close();
} else {
open();
}
}
if (camImage) {
camImage.onload = () => {
console.log("[camera] stream started");
if (crosshair) {
crosshair.style.opacity = '1';
}
};
camImage.onerror = (e) => {
console.error("[camera] stream error", e);
if (crosshair) {
crosshair.style.opacity = '0.3';
}
};
}
btn?.addEventListener("click", toggle);
// Мониторинг состояния потока
function startStreamMonitoring(): void {
stopStreamMonitoring();
streamCheckInterval = window.setInterval(() => {
if (isOpen && camImage) {
if (!camImage.complete || camImage.naturalWidth === 0) {
if (crosshair) {
crosshair.style.opacity = '0.3';
}
} else {
if (crosshair) {
crosshair.style.opacity = '1';
}
}
}
}, 2000);
}
function stopStreamMonitoring(): void {
if (streamCheckInterval) {
clearInterval(streamCheckInterval);
streamCheckInterval = null;
}
}
}
// ============================================
// ПУБЛИЧНЫЕ ФУНКЦИИ (используются в других модулях)
// ============================================
/**
* Показать/скрыть перекрестие на видео
* @param show - показывать или скрыть
*/
// noinspection JSUnusedGlobalSymbols
export function showCrosshair(show: boolean = true): void {
const crosshairElem = document.getElementById("crosshair");
if (crosshairElem) {
crosshairElem.style.display = show ? "block" : "none";
}
}
/**
* Установить прозрачность перекрестия
* @param opacity - значение прозрачности (0-1)
*/
// noinspection JSUnusedGlobalSymbols
export function setCrosshairOpacity(opacity: string | number): void {
const crosshairElem = document.getElementById("crosshair");
if (crosshairElem) {
crosshairElem.style.opacity = String(opacity);
}
}