helva-robot/face/var/www/html/app.js

82 lines
2.2 KiB
JavaScript
Executable File

const label = document.getElementById("label");
const eyes = Array.from(document.querySelectorAll(".eye"));
const mouthShape = document.querySelector(".mouth-shape");
let current = "neutral";
function setEmotion(name) {
const safe = String(name || "neutral")
.toLowerCase()
.replace(/[^a-z0-9_-]/g, "");
current = safe;
document.body.className = `emotion-${safe}`;
label.textContent = safe;
// Sonderfall: surprised -> O-Mund aktivieren
if (safe === "surprised") document.body.classList.add("has-omouth");
else document.body.classList.remove("has-omouth");
// sad -> frown pseudo aktivieren (eigene Klasse)
if (safe === "sad" || safe === "angry") mouthShape.classList.add("frown");
else mouthShape.classList.remove("frown");
}
/** Pupillen bewegen sich sanft, damit es "lebendig" wirkt */
function startPupilWander() {
const tick = () => {
// kleine zufällige Offsets in Pixel (auf großen Displays reicht das)
const x = Math.round((Math.random() * 2 - 1) * 10);
const y = Math.round((Math.random() * 2 - 1) * 8);
document.documentElement.style.setProperty("--pupil-x", `${x}px`);
document.documentElement.style.setProperty("--pupil-y", `${y}px`);
// alle 600-1400ms neu
const next = 600 + Math.random() * 800;
setTimeout(tick, next);
};
tick();
}
/** Blinzeln: in zufälligen Abständen, manchmal doppelt */
function blinkOnce() {
eyes.forEach(e => e.classList.add("blink"));
setTimeout(() => eyes.forEach(e => e.classList.remove("blink")), 120);
}
function startBlinking() {
const loop = () => {
// sleepy blinzelt öfter/langsamer, angry etwas "härter"
let base = 3500;
if (current === "sleepy") base = 2200;
if (current === "surprised") base = 4200;
const next = base + Math.random() * 2200;
setTimeout(() => {
blinkOnce();
// 15% chance auf Doppelt-Blink
if (Math.random() < 0.15) setTimeout(blinkOnce, 220);
loop();
}, next);
};
loop();
}
function connect() {
const es = new EventSource("/events");
es.addEventListener("emotion", (e) => setEmotion(e.data));
es.onmessage = (e) => setEmotion(e.data); // fallback
es.onerror = () => {
es.close();
setTimeout(connect, 1000);
};
}
connect();
startPupilWander();
startBlinking();