Upload files to "/"
This commit is contained in:
+552
@@ -0,0 +1,552 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="nl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>CAM-01 // Surveillance Feed</title>
|
||||||
|
<style>
|
||||||
|
/* ============================================================
|
||||||
|
CONFIG (makkelijk aanpasbaar)
|
||||||
|
============================================================ */
|
||||||
|
:root{
|
||||||
|
--rec-color:#8a2f28;
|
||||||
|
--hud-color:#c7cac7;
|
||||||
|
--hud-dim:#6b6f6b;
|
||||||
|
--bg-color:#050505;
|
||||||
|
--frame-color:#83887f;
|
||||||
|
--panel-bg: rgba(12,12,12,0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin:0; padding:0; width:100%; height:100%;
|
||||||
|
background:var(--bg-color);
|
||||||
|
overflow:hidden;
|
||||||
|
font-family: "Courier New", ui-monospace, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
#stage {
|
||||||
|
position:relative;
|
||||||
|
width:100vw; height:100vh;
|
||||||
|
background:#000;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
LETTERBOX (bioscoop-achtige balken boven/onder)
|
||||||
|
============================================================ */
|
||||||
|
.letterbox {
|
||||||
|
position:absolute;
|
||||||
|
left:0; width:100%;
|
||||||
|
background:#000;
|
||||||
|
z-index:15;
|
||||||
|
pointer-events:none;
|
||||||
|
}
|
||||||
|
#letterbox-top { top:0; height:3.6vh; }
|
||||||
|
#letterbox-bottom { bottom:0; height:3.6vh; }
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
VIDEO / MJPEG STREAM
|
||||||
|
============================================================ */
|
||||||
|
#video-wrapper {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
overflow:hidden;
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
justify-content:center;
|
||||||
|
background:#000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#camera-stream {
|
||||||
|
/* ROTATIE: --rotate wordt via JS gezet (0/90/180/270) */
|
||||||
|
width:100%;
|
||||||
|
height:100%;
|
||||||
|
object-fit:cover;
|
||||||
|
transform: rotate(var(--rotate, 0deg));
|
||||||
|
filter: grayscale(0.55) contrast(1.12) brightness(0.82) saturate(0.75);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
background:#000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#camera-stream.no-signal { display:none; }
|
||||||
|
#no-signal-msg {
|
||||||
|
display:none;
|
||||||
|
color:var(--hud-dim);
|
||||||
|
font-size:1.1em;
|
||||||
|
letter-spacing:3px;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
|
#no-signal-msg.active { display:block; }
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
OVERLAY EFFECTEN
|
||||||
|
============================================================ */
|
||||||
|
#effects-layer {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
pointer-events:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scanlines {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
background: repeating-linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(255,255,255,0.03) 0px,
|
||||||
|
rgba(255,255,255,0.03) 1px,
|
||||||
|
rgba(0,0,0,0.045) 2px,
|
||||||
|
rgba(0,0,0,0.045) 3px
|
||||||
|
);
|
||||||
|
mix-blend-mode:overlay;
|
||||||
|
pointer-events:none;
|
||||||
|
z-index:5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Zwaardere, filmischere vignette */
|
||||||
|
#vignette {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
background: radial-gradient(ellipse at center, rgba(0,0,0,0) 42%, rgba(0,0,0,0.75) 100%);
|
||||||
|
pointer-events:none;
|
||||||
|
z-index:6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grijze filmkorrel-tint over het geheel */
|
||||||
|
#grade-overlay {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
background: linear-gradient(180deg, rgba(120,125,120,0.05), rgba(0,0,0,0.12));
|
||||||
|
mix-blend-mode:multiply;
|
||||||
|
pointer-events:none;
|
||||||
|
z-index:3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noise-canvas {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
opacity:0.06;
|
||||||
|
pointer-events:none;
|
||||||
|
z-index:4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#glitch-flash {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
background:rgba(255,255,255,0.035);
|
||||||
|
opacity:0;
|
||||||
|
pointer-events:none;
|
||||||
|
z-index:7;
|
||||||
|
}
|
||||||
|
#glitch-flash.flash { opacity:1; transition:none; }
|
||||||
|
#glitch-flash.fade { opacity:0; transition:opacity 0.15s linear; }
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
HUD
|
||||||
|
============================================================ */
|
||||||
|
#hud {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%; height:100%;
|
||||||
|
z-index:10;
|
||||||
|
pointer-events:none;
|
||||||
|
color:var(--hud-color);
|
||||||
|
text-shadow: 0 0 3px rgba(0,0,0,0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hoekmarkeringen — puur decoratief, ruim buiten de tekstzones gehouden */
|
||||||
|
.corner {
|
||||||
|
position:absolute;
|
||||||
|
width:26px; height:26px;
|
||||||
|
opacity:0.55;
|
||||||
|
}
|
||||||
|
.corner span { position:absolute; background:var(--frame-color); }
|
||||||
|
.corner .h { width:100%; height:1px; }
|
||||||
|
.corner .v { width:1px; height:100%; }
|
||||||
|
|
||||||
|
.corner.tl { top:14px; left:14px; }
|
||||||
|
.corner.tl .h { top:0; left:0; }
|
||||||
|
.corner.tl .v { top:0; left:0; }
|
||||||
|
|
||||||
|
.corner.tr { top:14px; right:14px; }
|
||||||
|
.corner.tr .h { top:0; right:0; }
|
||||||
|
.corner.tr .v { top:0; right:0; }
|
||||||
|
|
||||||
|
.corner.bl { bottom:14px; left:14px; }
|
||||||
|
.corner.bl .h { bottom:0; left:0; }
|
||||||
|
.corner.bl .v { bottom:0; left:0; }
|
||||||
|
|
||||||
|
.corner.br { bottom:14px; right:14px; }
|
||||||
|
.corner.br .h { bottom:0; right:0; }
|
||||||
|
.corner.br .v { bottom:0; right:0; }
|
||||||
|
|
||||||
|
/* Center crosshair */
|
||||||
|
#crosshair {
|
||||||
|
position:absolute;
|
||||||
|
top:50%; left:50%;
|
||||||
|
width:20px; height:20px;
|
||||||
|
transform:translate(-50%,-50%);
|
||||||
|
opacity:0.45;
|
||||||
|
}
|
||||||
|
#crosshair::before, #crosshair::after {
|
||||||
|
content:"";
|
||||||
|
position:absolute;
|
||||||
|
background:var(--frame-color);
|
||||||
|
}
|
||||||
|
#crosshair::before { top:50%; left:0; width:100%; height:1px; transform:translateY(-50%); }
|
||||||
|
#crosshair::after { left:50%; top:0; height:100%; width:1px; transform:translateX(-50%); }
|
||||||
|
#crosshair .dot {
|
||||||
|
position:absolute; top:50%; left:50%;
|
||||||
|
width:2px; height:2px;
|
||||||
|
background:var(--frame-color);
|
||||||
|
transform:translate(-50%,-50%);
|
||||||
|
border-radius:50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
HEADER — alle topinfo in één rustige rij, ruim uit de hoeken
|
||||||
|
============================================================ */
|
||||||
|
#top-bar {
|
||||||
|
position:absolute;
|
||||||
|
top:0; left:0; width:100%;
|
||||||
|
padding:22px 56px;
|
||||||
|
display:flex;
|
||||||
|
align-items:flex-start;
|
||||||
|
justify-content:space-between;
|
||||||
|
font-size:12px;
|
||||||
|
letter-spacing:1px;
|
||||||
|
background:linear-gradient(to bottom, rgba(0,0,0,0.6), rgba(0,0,0,0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#rec-block {
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:4px;
|
||||||
|
}
|
||||||
|
#rec-indicator { display:flex; align-items:center; gap:7px; }
|
||||||
|
#rec-dot {
|
||||||
|
width:8px; height:8px;
|
||||||
|
border-radius:50%;
|
||||||
|
background:var(--rec-color);
|
||||||
|
box-shadow:0 0 5px var(--rec-color);
|
||||||
|
animation: blink 1s steps(1) infinite;
|
||||||
|
}
|
||||||
|
@keyframes blink {
|
||||||
|
0%, 49% { opacity:1; }
|
||||||
|
50%, 100% { opacity:0.12; }
|
||||||
|
}
|
||||||
|
#rec-label { color:var(--rec-color); font-weight:bold; letter-spacing:2px; }
|
||||||
|
#rec-timer { color:var(--hud-dim); font-size:11px; letter-spacing:2px; }
|
||||||
|
|
||||||
|
#cam-name {
|
||||||
|
font-size:13px;
|
||||||
|
letter-spacing:3px;
|
||||||
|
text-align:center;
|
||||||
|
align-self:center;
|
||||||
|
color:var(--hud-color);
|
||||||
|
opacity:0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-status {
|
||||||
|
font-size:11px;
|
||||||
|
color:var(--hud-dim);
|
||||||
|
text-align:right;
|
||||||
|
line-height:1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
BOTTOM BAR
|
||||||
|
============================================================ */
|
||||||
|
#bottom-bar {
|
||||||
|
position:absolute;
|
||||||
|
bottom:0; left:0; width:100%;
|
||||||
|
padding:18px 56px;
|
||||||
|
display:flex;
|
||||||
|
align-items:flex-end;
|
||||||
|
justify-content:space-between;
|
||||||
|
font-size:11px;
|
||||||
|
letter-spacing:1px;
|
||||||
|
background:linear-gradient(to top, rgba(0,0,0,0.65), rgba(0,0,0,0));
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-block { display:flex; flex-direction:column; gap:3px; line-height:1.4; }
|
||||||
|
.status-block .label { color:var(--hud-dim); font-size:9px; letter-spacing:2px; }
|
||||||
|
.status-block .value { color:var(--hud-color); }
|
||||||
|
.status-ok { color:#7c8f7c; }
|
||||||
|
.status-warn { color:#a8875a; }
|
||||||
|
|
||||||
|
#datetime-block { text-align:right; }
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
CONTROLE PANEEL — eigen rij, los van hoeken en van bottom-bar
|
||||||
|
============================================================ */
|
||||||
|
#controls {
|
||||||
|
position:absolute;
|
||||||
|
bottom:78px;
|
||||||
|
right:56px;
|
||||||
|
z-index:20;
|
||||||
|
display:flex;
|
||||||
|
gap:1px;
|
||||||
|
pointer-events:auto;
|
||||||
|
background:var(--panel-bg);
|
||||||
|
border:1px solid rgba(131,136,127,0.4);
|
||||||
|
}
|
||||||
|
#controls .ctrl-label {
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
padding:0 8px;
|
||||||
|
font-size:9px;
|
||||||
|
letter-spacing:2px;
|
||||||
|
color:var(--hud-dim);
|
||||||
|
border-right:1px solid rgba(131,136,127,0.3);
|
||||||
|
}
|
||||||
|
#controls button {
|
||||||
|
background:transparent;
|
||||||
|
border:none;
|
||||||
|
border-right:1px solid rgba(131,136,127,0.25);
|
||||||
|
color:var(--hud-color);
|
||||||
|
font-family:inherit;
|
||||||
|
font-size:11px;
|
||||||
|
letter-spacing:1px;
|
||||||
|
padding:7px 11px;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
#controls button:last-child { border-right:none; }
|
||||||
|
#controls button:hover { background:rgba(255,255,255,0.06); }
|
||||||
|
#controls button.active { background:rgba(255,255,255,0.1); color:#fff; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div id="stage">
|
||||||
|
|
||||||
|
<div class="letterbox" id="letterbox-top"></div>
|
||||||
|
<div class="letterbox" id="letterbox-bottom"></div>
|
||||||
|
|
||||||
|
<!-- VIDEO -->
|
||||||
|
<div id="video-wrapper">
|
||||||
|
<img id="camera-stream"
|
||||||
|
src="http://192.168.1.50:8080/video"
|
||||||
|
alt="Camera stream"
|
||||||
|
onerror="handleStreamError()"
|
||||||
|
onload="handleStreamOk()">
|
||||||
|
<div id="no-signal-msg">// GEEN SIGNAAL //<br>STREAM ONBEREIKBAAR</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- EFFECTEN -->
|
||||||
|
<div id="effects-layer">
|
||||||
|
<canvas id="noise-canvas"></canvas>
|
||||||
|
<div id="grade-overlay"></div>
|
||||||
|
<div id="scanlines"></div>
|
||||||
|
<div id="vignette"></div>
|
||||||
|
<div id="glitch-flash"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- HUD -->
|
||||||
|
<div id="hud">
|
||||||
|
<div class="corner tl"><span class="h"></span><span class="v"></span></div>
|
||||||
|
<div class="corner tr"><span class="h"></span><span class="v"></span></div>
|
||||||
|
<div class="corner bl"><span class="h"></span><span class="v"></span></div>
|
||||||
|
<div class="corner br"><span class="h"></span><span class="v"></span></div>
|
||||||
|
|
||||||
|
<div id="crosshair"><div class="dot"></div></div>
|
||||||
|
|
||||||
|
<div id="top-bar">
|
||||||
|
<div id="rec-block">
|
||||||
|
<div id="rec-indicator">
|
||||||
|
<div id="rec-dot"></div>
|
||||||
|
<span id="rec-label">REC</span>
|
||||||
|
</div>
|
||||||
|
<span id="rec-timer">00:00:00</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="cam-name">CAM-01 // VOORINGANG</div>
|
||||||
|
|
||||||
|
<div id="top-status">
|
||||||
|
RES 1920×1080<br>
|
||||||
|
FPS <span id="fps-value">--</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="bottom-bar">
|
||||||
|
<div class="status-block">
|
||||||
|
<span class="label">CAMERA STATUS</span>
|
||||||
|
<span class="value status-ok" id="camera-status">ONLINE</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-block">
|
||||||
|
<span class="label">NETWERK</span>
|
||||||
|
<span class="value status-ok" id="network-status">VERBONDEN</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-block">
|
||||||
|
<span class="label">MODUS</span>
|
||||||
|
<span class="value" id="camera-mode">AUTO / KLEUR</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-block" id="datetime-block">
|
||||||
|
<span class="label">DATUM / TIJD</span>
|
||||||
|
<span class="value" id="datetime-value">--:--:--</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ROTATIE CONTROLS — eigen paneel, ruim boven de bottom-bar -->
|
||||||
|
<div id="controls">
|
||||||
|
<span class="ctrl-label">ROTATIE</span>
|
||||||
|
<button data-deg="0" onclick="setRotation(0, this)">0°</button>
|
||||||
|
<button data-deg="90" onclick="setRotation(90, this)">90°</button>
|
||||||
|
<button data-deg="180" onclick="setRotation(180, this)">180°</button>
|
||||||
|
<button data-deg="270" onclick="setRotation(270, this)">270°</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/* ============================================================
|
||||||
|
CONFIGURATIE
|
||||||
|
============================================================ */
|
||||||
|
const STREAM_URL = "http://192.168.1.50:8080/video"; // <-- pas hier de MJPEG-stream URL aan
|
||||||
|
const CAMERA_NAME = "CAM-01 // VOORINGANG"; // <-- pas hier de cameranaam aan
|
||||||
|
const DEFAULT_ROTATION = 0; // 0, 90, 180 of 270
|
||||||
|
|
||||||
|
document.getElementById("camera-stream").src = STREAM_URL;
|
||||||
|
document.getElementById("cam-name").textContent = CAMERA_NAME;
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
ROTATIE (puur CSS transform, stream blijft ongewijzigd)
|
||||||
|
============================================================ */
|
||||||
|
function setRotation(deg, btn) {
|
||||||
|
document.documentElement.style.setProperty("--rotate", deg + "deg");
|
||||||
|
document.querySelectorAll("#controls button").forEach(b => b.classList.remove("active"));
|
||||||
|
if (btn) btn.classList.add("active");
|
||||||
|
}
|
||||||
|
(function initRotation(){
|
||||||
|
const btn = document.querySelector(`#controls button[data-deg="${DEFAULT_ROTATION}"]`);
|
||||||
|
setRotation(DEFAULT_ROTATION, btn);
|
||||||
|
})();
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
STREAM STATUS HANDLING
|
||||||
|
============================================================ */
|
||||||
|
let streamOk = true;
|
||||||
|
|
||||||
|
function handleStreamError() {
|
||||||
|
streamOk = false;
|
||||||
|
document.getElementById("camera-stream").classList.add("no-signal");
|
||||||
|
document.getElementById("no-signal-msg").classList.add("active");
|
||||||
|
setStatus("camera-status", "OFFLINE", "status-warn");
|
||||||
|
setStatus("network-status", "GEEN VERBINDING", "status-warn");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleStreamOk() {
|
||||||
|
streamOk = true;
|
||||||
|
document.getElementById("camera-stream").classList.remove("no-signal");
|
||||||
|
document.getElementById("no-signal-msg").classList.remove("active");
|
||||||
|
setStatus("camera-status", "ONLINE", "status-ok");
|
||||||
|
setStatus("network-status", "VERBONDEN", "status-ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatus(id, text, cls) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
el.textContent = text;
|
||||||
|
el.classList.remove("status-ok", "status-warn");
|
||||||
|
el.classList.add(cls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
KLOK
|
||||||
|
============================================================ */
|
||||||
|
function updateClock() {
|
||||||
|
const now = new Date();
|
||||||
|
const pad = n => String(n).padStart(2, "0");
|
||||||
|
const dateStr = `${pad(now.getDate())}-${pad(now.getMonth()+1)}-${now.getFullYear()}`;
|
||||||
|
const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
|
||||||
|
document.getElementById("datetime-value").textContent = `${dateStr} ${timeStr}`;
|
||||||
|
}
|
||||||
|
updateClock();
|
||||||
|
setInterval(updateClock, 1000);
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
REC-TIMER (loopt vanaf het laden van de pagina)
|
||||||
|
============================================================ */
|
||||||
|
const recStart = performance.now();
|
||||||
|
function updateRecTimer() {
|
||||||
|
const elapsed = Math.floor((performance.now() - recStart) / 1000);
|
||||||
|
const h = String(Math.floor(elapsed / 3600)).padStart(2, "0");
|
||||||
|
const m = String(Math.floor((elapsed % 3600) / 60)).padStart(2, "0");
|
||||||
|
const s = String(elapsed % 60).padStart(2, "0");
|
||||||
|
document.getElementById("rec-timer").textContent = `${h}:${m}:${s}`;
|
||||||
|
}
|
||||||
|
setInterval(updateRecTimer, 1000);
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
FPS INDICATOR (indicatief, gebaseerd op MJPEG frame-updates)
|
||||||
|
============================================================ */
|
||||||
|
let frameCount = 0;
|
||||||
|
let lastFpsCheck = performance.now();
|
||||||
|
|
||||||
|
document.getElementById("camera-stream").addEventListener("load", () => {
|
||||||
|
frameCount++;
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
const now = performance.now();
|
||||||
|
const elapsed = (now - lastFpsCheck) / 1000;
|
||||||
|
const fps = streamOk ? Math.round(frameCount / elapsed) : 0;
|
||||||
|
document.getElementById("fps-value").textContent = streamOk ? fps : "--";
|
||||||
|
frameCount = 0;
|
||||||
|
lastFpsCheck = now;
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
NOISE EFFECT (lichtgewicht, lage update-frequentie i.v.m. Raspberry Pi)
|
||||||
|
============================================================ */
|
||||||
|
const noiseCanvas = document.getElementById("noise-canvas");
|
||||||
|
const noiseCtx = noiseCanvas.getContext("2d");
|
||||||
|
|
||||||
|
function resizeNoiseCanvas() {
|
||||||
|
noiseCanvas.width = 160;
|
||||||
|
noiseCanvas.height = 90;
|
||||||
|
}
|
||||||
|
resizeNoiseCanvas();
|
||||||
|
window.addEventListener("resize", resizeNoiseCanvas);
|
||||||
|
|
||||||
|
function renderNoise() {
|
||||||
|
const w = noiseCanvas.width, h = noiseCanvas.height;
|
||||||
|
const imgData = noiseCtx.createImageData(w, h);
|
||||||
|
const buffer = imgData.data;
|
||||||
|
for (let i = 0; i < buffer.length; i += 4) {
|
||||||
|
const shade = Math.random() * 255;
|
||||||
|
buffer[i] = shade;
|
||||||
|
buffer[i+1] = shade;
|
||||||
|
buffer[i+2] = shade;
|
||||||
|
buffer[i+3] = 40;
|
||||||
|
}
|
||||||
|
noiseCtx.putImageData(imgData, 0, 0);
|
||||||
|
}
|
||||||
|
setInterval(renderNoise, 150);
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
OCCASIONELE SUBTIELE GLITCH FLASH
|
||||||
|
============================================================ */
|
||||||
|
function triggerGlitch() {
|
||||||
|
const flash = document.getElementById("glitch-flash");
|
||||||
|
flash.classList.remove("fade");
|
||||||
|
flash.classList.add("flash");
|
||||||
|
setTimeout(() => {
|
||||||
|
flash.classList.remove("flash");
|
||||||
|
flash.classList.add("fade");
|
||||||
|
}, 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleGlitch() {
|
||||||
|
const delay = 5000 + Math.random() * 9000;
|
||||||
|
setTimeout(() => {
|
||||||
|
triggerGlitch();
|
||||||
|
scheduleGlitch();
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
scheduleGlitch();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user