Neue Seite "Einfuehrung" im Menue: 31 Folien in zwei Teilen - Bedienen im Alltag (Grundriss, Raum, Rollos, Heizung, Licht, Zeitleiste, Solar, Auto, Heizstab und Wasser, Statistik, Historie, Heizung, Wetter, Fahrzeug, Hilfe) und Einrichten und Verwalten (Preise, Grundriss, Kacheln, Geraete, Automatik anlegen in Wenn/Wann/Dann, Ketten, Fahrzeugschluessel, Protokolle). - Bilder sind echte Aufnahmen der Oberflaeche mit Livewerten, Handy- und Desktopgroesse. tools/einfuehrung/aufnehmen.ps1 steuert Edge ohne Fenster ueber das DevTools-Protokoll nach plan.json und schreibt zu jedem Bild die Lage der markierten Elemente; daraus sitzen die nummerierten Punkte. Wetterort und Schluesselendungen werden vor der Aufnahme abgedeckt. - Blaettern per Knopf, Pfeiltaste oder Wischen; Kapitelsprung; die Folie steht in der Adresse (#folie-7). Erklaerung und Punkt heben sich gegenseitig hervor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
134 lines
6.0 KiB
JavaScript
134 lines
6.0 KiB
JavaScript
/* ==========================================================================
|
|
Einführung als Folien (restricted/einfuehrung.php).
|
|
|
|
Blättern mit den Knöpfen, den Pfeiltasten oder durch Wischen. Die Folie
|
|
steht in der Adresse (#folie-7), damit man sie verlinken und nach einem
|
|
Neuladen dort weitermachen kann. Eine Erklärung antippen lässt ihren Punkt
|
|
im Bild aufleuchten - und umgekehrt.
|
|
========================================================================== */
|
|
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
const ef = document.getElementById("einfuehrung");
|
|
if (!ef) return;
|
|
|
|
const folien = [...ef.querySelectorAll(".ef-folie")];
|
|
const zurueck = ef.querySelector(".ef-zurueck");
|
|
const weiter = ef.querySelector(".ef-weiter");
|
|
const zaehler = ef.querySelector(".ef-zaehler");
|
|
const balken = ef.querySelector(".ef-fortschritt span");
|
|
const kapitelWahl = ef.querySelector(".ef-kapitel");
|
|
const punkteNav = ef.querySelector(".ef-punkte-nav");
|
|
let aktuell = 0;
|
|
|
|
// Die Punkte unten: einer je Folie, Teile durch eine Lücke getrennt.
|
|
folien.forEach((f, i) => {
|
|
const p = document.createElement("span");
|
|
p.className = "ef-punkt" + (i > 0 && folien[i - 1].dataset.teil !== f.dataset.teil ? " neuer-teil" : "");
|
|
p.dataset.ziel = i;
|
|
punkteNav.appendChild(p);
|
|
});
|
|
punkteNav.removeAttribute("aria-hidden");
|
|
|
|
function zeige(nr, ohneAdresse) {
|
|
nr = Math.max(0, Math.min(folien.length - 1, nr));
|
|
const richtung = nr >= aktuell ? "vor" : "rueck";
|
|
folien.forEach((f, i) => {
|
|
f.hidden = i !== nr;
|
|
f.classList.remove("rein-vor", "rein-rueck");
|
|
});
|
|
const f = folien[nr];
|
|
void f.offsetWidth; // Animation neu starten
|
|
f.classList.add("rein-" + richtung);
|
|
aktuell = nr;
|
|
|
|
zaehler.textContent = (nr + 1) + " / " + folien.length;
|
|
balken.style.width = ((nr + 1) / folien.length * 100).toFixed(1) + "%";
|
|
zurueck.disabled = nr === 0;
|
|
weiter.innerHTML = nr === folien.length - 1
|
|
? 'Von vorn<i class="bi bi-arrow-counterclockwise ms-1"></i>'
|
|
: 'Weiter<i class="bi bi-arrow-right ms-1"></i>';
|
|
ef.querySelectorAll(".ef-teil").forEach(t => t.classList.toggle("aktiv", t.dataset.teil === f.dataset.teil));
|
|
punkteNav.querySelectorAll(".ef-punkt").forEach((p, i) => p.classList.toggle("aktiv", i === nr));
|
|
|
|
// Das Kapitel dieser Folie in der Auswahl: die letzte Kapitel-Folie <= nr.
|
|
let kap = null;
|
|
[...kapitelWahl.options].forEach(o => { if (Number(o.value) <= nr) kap = o.value; });
|
|
if (kap !== null) kapitelWahl.value = kap;
|
|
|
|
if (!ohneAdresse) history.replaceState(null, "", "#folie-" + (nr + 1));
|
|
// Auf dem Handy nach oben, damit die neue Folie mit dem Titel beginnt.
|
|
const oben = ef.getBoundingClientRect().top;
|
|
if (oben < 0) ef.scrollIntoView({ block: "start", behavior: "smooth" });
|
|
}
|
|
|
|
zurueck.addEventListener("click", () => zeige(aktuell - 1));
|
|
weiter.addEventListener("click", () => zeige(aktuell === folien.length - 1 ? 0 : aktuell + 1));
|
|
kapitelWahl.addEventListener("change", () => zeige(Number(kapitelWahl.value)));
|
|
ef.querySelectorAll(".ef-teil").forEach(t => t.addEventListener("click", () => {
|
|
zeige(folien.findIndex(f => f.dataset.teil === t.dataset.teil));
|
|
}));
|
|
punkteNav.addEventListener("click", e => {
|
|
const p = e.target.closest(".ef-punkt");
|
|
if (p) zeige(Number(p.dataset.ziel));
|
|
});
|
|
|
|
document.addEventListener("keydown", e => {
|
|
if (e.target.closest("input, select, textarea")) return;
|
|
if (e.key === "ArrowRight" || e.key === "PageDown") { zeige(aktuell + 1); e.preventDefault(); }
|
|
if (e.key === "ArrowLeft" || e.key === "PageUp") { zeige(aktuell - 1); e.preventDefault(); }
|
|
if (e.key === "Home") zeige(0);
|
|
if (e.key === "End") zeige(folien.length - 1);
|
|
});
|
|
|
|
// Wischen: waagerecht deutlich, senkrecht wenig - sonst ist es Scrollen.
|
|
let start = null;
|
|
ef.querySelector(".ef-buehne").addEventListener("touchstart", e => {
|
|
start = { x: e.touches[0].clientX, y: e.touches[0].clientY };
|
|
}, { passive: true });
|
|
ef.querySelector(".ef-buehne").addEventListener("touchend", e => {
|
|
if (!start) return;
|
|
const dx = e.changedTouches[0].clientX - start.x;
|
|
const dy = e.changedTouches[0].clientY - start.y;
|
|
start = null;
|
|
if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5) zeige(aktuell + (dx < 0 ? 1 : -1));
|
|
}, { passive: true });
|
|
|
|
// Erklärung und Punkt im Bild gehören zusammen: das eine hebt das andere hervor.
|
|
function hervorheben(folie, nr, an) {
|
|
folie.querySelectorAll('[data-marke="' + nr + '"]').forEach(el => el.classList.toggle("hell", an));
|
|
}
|
|
folien.forEach(folie => {
|
|
folie.addEventListener("mouseover", e => {
|
|
const el = e.target.closest("[data-marke]");
|
|
if (el) hervorheben(folie, el.dataset.marke, true);
|
|
});
|
|
folie.addEventListener("mouseout", e => {
|
|
const el = e.target.closest("[data-marke]");
|
|
if (el) hervorheben(folie, el.dataset.marke, false);
|
|
});
|
|
folie.addEventListener("click", e => {
|
|
const el = e.target.closest("[data-marke]");
|
|
if (!el) return;
|
|
const nr = el.dataset.marke;
|
|
folie.querySelectorAll("[data-marke]").forEach(x => x.classList.remove("hell"));
|
|
hervorheben(folie, nr, true);
|
|
// Auf dem Handy liegt das Bild über dem Text: zum Punkt scrollen.
|
|
const punkt = folie.querySelector('.ef-marke[data-marke="' + nr + '"]');
|
|
if (punkt && el.tagName === "LI") {
|
|
const r = punkt.getBoundingClientRect();
|
|
if (r.top < 60 || r.bottom > window.innerHeight) punkt.scrollIntoView({ block: "center", behavior: "smooth" });
|
|
}
|
|
});
|
|
});
|
|
|
|
// Ein Link auf eine Folie in einem schon offenen Tab ändert nur den Anker -
|
|
// die Seite lädt dann nicht neu, also hier nachziehen.
|
|
window.addEventListener("hashchange", () => {
|
|
const t = /^#folie-(\d+)$/.exec(location.hash);
|
|
if (t && Number(t[1]) - 1 !== aktuell) zeige(Number(t[1]) - 1, true);
|
|
});
|
|
|
|
const treffer = /^#folie-(\d+)$/.exec(location.hash);
|
|
zeige(treffer ? Number(treffer[1]) - 1 : 0, !treffer);
|
|
});
|