Raum-Modal: Fensterbild zeigt Hoehe und Neigung, Regler nach rechts

Die Regler standen neben den Fahrknoepfen - beim Antippen von "Auf" fand
der Daumen leicht den Schieber daneben. Jetzt stehen sie rechts aussen,
zwischen ihnen und den Knoepfen liegt das Fensterbild.

Das Bild zeigt jetzt beides. Die Hoehe schneidet ab: gezeichnet wird nur
der Teil des Fensters, den die Jalousie bedeckt, mit der Endschiene an
seiner Unterkante; darunter sieht man hinaus. Die Neigung bestimmt wie
bisher, wie dick eine Lamelle von vorn erscheint. Ein Rollladen ohne
Neigung bekommt volle Lamellenhoehe - er ist ja ein geschlossener Panzer.

Die Prozentzahl unter den Reglern ist weg; die Bahn ist dafuer von 5 auf
6,5 rem gewachsen. Was der Regler bewirkt, zeigt das Bild ohnehin besser
als eine Zahl.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-31 21:59:52 +02:00
co-authored by Claude Opus 5
parent e7fd7715ef
commit 18557021b0
3 changed files with 87 additions and 48 deletions
+10 -12
View File
@@ -277,14 +277,10 @@ input[type='range']::-webkit-slider-runnable-track {
align-items: stretch; align-items: stretch;
gap: 0.6rem; gap: 0.6rem;
} }
/* Neigungsregler und Lamellenbild gehoeren zusammen: das Bild sagt, was der /* Das Fensterbild nimmt den Platz zwischen Knoepfen und Reglern. */
Regler daneben bedeutet, und laeuft beim Ziehen mit. */ .raum-jalousie .raum-lamellen {
.raum-jalousie .raum-neigung-block {
display: flex;
align-items: stretch;
gap: 0.4rem;
flex: 1 1 auto; flex: 1 1 auto;
min-width: 0; min-width: 2.5rem;
} }
.raum-jalousie .dropdown { .raum-jalousie .dropdown {
align-self: flex-start; align-self: flex-start;
@@ -325,14 +321,14 @@ input[type='range']::-webkit-slider-runnable-track {
.raum-senkrecht { .raum-senkrecht {
position: relative; position: relative;
width: 1.6rem; width: 1.6rem;
height: 5rem; height: 6.5rem;
margin: 0.1rem 0; margin: 0.1rem 0;
} }
#roomView .raum-senkrecht input[type='range'] { #roomView .raum-senkrecht input[type='range'] {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
width: 5rem; /* = Hoehe des Kastens, die Laenge der Bahn */ width: 6.5rem; /* = Hoehe des Kastens, die Laenge der Bahn */
height: 1.6rem; /* = Breite des Kastens, die Flaeche zum Treffen */ height: 1.6rem; /* = Breite des Kastens, die Flaeche zum Treffen */
transform: translate(-50%, -50%) rotate(90deg); transform: translate(-50%, -50%) rotate(90deg);
touch-action: none; touch-action: none;
@@ -350,10 +346,8 @@ input[type='range']::-webkit-slider-runnable-track {
aneinanderliegend heisst geschlossen. */ aneinanderliegend heisst geschlossen. */
.raum-lamellen { .raum-lamellen {
display: block; display: block;
flex: 1 1 auto;
min-width: 2.5rem;
align-self: center; align-self: center;
height: 5rem; height: 6.9rem;
/* Der Rahmen macht aus dem Streifen ein Fenster - ohne ihn liest sich das /* Der Rahmen macht aus dem Streifen ein Fenster - ohne ihn liest sich das
geschlossene Bild wie eine zweite Reglerbahn. */ geschlossene Bild wie eine zweite Reglerbahn. */
background: rgba(0, 120, 143, 0.14); background: rgba(0, 120, 143, 0.14);
@@ -367,3 +361,7 @@ input[type='range']::-webkit-slider-runnable-track {
stroke-width: 0.6; stroke-width: 0.6;
vector-effect: non-scaling-stroke; vector-effect: non-scaling-stroke;
} }
.raum-endschiene {
fill: currentColor;
opacity: 0.85;
}
+42 -16
View File
@@ -229,6 +229,7 @@ function switchFloor(floor){
// Schieberegler: waehrend des Ziehens nur die Anzeige, geschickt wird // Schieberegler: waehrend des Ziehens nur die Anzeige, geschickt wird
// erst beim Loslassen. Sonst bekaeme eine Jalousie bei jedem Pixel ein // erst beim Loslassen. Sonst bekaeme eine Jalousie bei jedem Pixel ein
// neues Ziel und kaeme aus dem Anfahren nicht mehr heraus. // neues Ziel und kaeme aus dem Anfahren nicht mehr heraus.
koerper.querySelectorAll(".raum-jalousie").forEach(lamellenZeichnen);
koerper.querySelectorAll(".raum-regler").forEach(regler => { koerper.querySelectorAll(".raum-regler").forEach(regler => {
const schieber = regler.querySelector("input[type=range]"); const schieber = regler.querySelector("input[type=range]");
reglerAnzeigen(regler); reglerAnzeigen(regler);
@@ -283,8 +284,8 @@ function switchFloor(floor){
* Anzeige, gefuellte Bahn und Lamellenbild eines Reglers nachziehen. * Anzeige, gefuellte Bahn und Lamellenbild eines Reglers nachziehen.
* *
* Was 0 % und 100 % bedeuten, steht bei den senkrechten Reglern als Wort * Was 0 % und 100 % bedeuten, steht bei den senkrechten Reglern als Wort
* an ihren Enden ("oben"/"unten", "auf"/"zu"); die Anzeige braucht deshalb * an ihren Enden ("oben"/"unten", "auf"/"zu"); eine Zahl steht dort nicht
* nur noch die Zahl, und die ist beim Ziehen das Interessante. * mehr - das Bild daneben zeigt beim Ziehen mehr als sie.
*/ */
function reglerAnzeigen(regler) { function reglerAnzeigen(regler) {
const schieber = regler.querySelector("input[type=range]"); const schieber = regler.querySelector("input[type=range]");
@@ -302,28 +303,53 @@ function switchFloor(floor){
schieber.style.setProperty("--background-size", schieber.style.setProperty("--background-size",
((wert - min) * 100 / (max - min)) + "%"); ((wert - min) * 100 / (max - min)) + "%");
lamellenZeichnen(regler.closest(".raum-neigung-block"), wert, min, max); lamellenZeichnen(regler.closest(".raum-jalousie"));
}
/** Der Stand eines der beiden Regler einer Jalousie, als Anteil 0..1. */
function reglerAnteil(jalousie, rolle, ersatz) {
const schieber = jalousie.querySelector("[data-rolle=" + rolle + "] input[type=range]");
if (!schieber) return ersatz;
const min = Number(schieber.min);
return (Number(schieber.value) - min) / (Number(schieber.max) - min);
} }
/** /**
* Die Lamellen zur Neigung zeichnen. * Das Fenster zeichnen, wie die Jalousie davor gerade steht.
* *
* Gezeichnet wird die Hoehe, die eine gekippte Lamelle von vorn zeigt: * Zwei Zahlen in einem Bild. Die Hoehe schneidet ab - gezeichnet wird nur
* w * sin(Winkel). Bei 0 % steht sie waagerecht und man sieht nur ihre * der Teil des Fensters, den die Jalousie bedeckt, mit der Endschiene an
* Kante, bei 100 % steht sie quer und deckt den Abstand zur naechsten * seiner Unterkante. Die Neigung bestimmt, wie dick eine Lamelle von vorn
* vollstaendig ab - genau das, was das Licht abhaelt. Der Abstand von * erscheint: w * sin(Winkel). Bei "auf" sieht man nur ihre Kante und viel
* Mitte zu Mitte ist 10, die Lamelle 11 tief: ab etwa 65 % schliesst * Luft dazwischen, bei "zu" deckt sie den Abstand zur naechsten
* vollstaendig ab - genau das, was das Licht abhaelt. Von Mitte zu Mitte
* sind es 10 Einheiten, die Lamelle ist 11 tief; ab etwa 65 % schliesst
* das Bild, so wie es die Jalousie auch tut. * das Bild, so wie es die Jalousie auch tut.
*
* Ein Rollladen ohne Neigung bekommt volle Lamellenhoehe - er ist ja ein
* geschlossener Panzer.
*/ */
function lamellenZeichnen(block, wert, min, max) { function lamellenZeichnen(jalousie) {
if (!block) return; if (!jalousie) return;
const anteil = (wert - min) / (max - min); const bild = jalousie.querySelector(".raum-lamellen");
const hoehe = Math.max(1.6, 11 * Math.sin(anteil * Math.PI / 2)); if (!bild) return;
block.querySelectorAll(".raum-lamelle").forEach(lamelle => {
const hoch = bild.viewBox.baseVal.height;
const abdeckung = hoch * reglerAnteil(jalousie, "position", 1);
const kippung = reglerAnteil(jalousie, "neigung", 1);
const dicke = Math.max(1.6, 11 * Math.sin(kippung * Math.PI / 2));
bild.querySelector(".raum-abdeckung").setAttribute("height", abdeckung);
bild.querySelectorAll(".raum-lamelle").forEach(lamelle => {
const mitte = Number(lamelle.dataset.mitte); const mitte = Number(lamelle.dataset.mitte);
lamelle.setAttribute("height", hoehe); lamelle.setAttribute("height", dicke);
lamelle.setAttribute("y", mitte - hoehe / 2); lamelle.setAttribute("y", mitte - dicke / 2);
}); });
// Die Endschiene sitzt auf der Unterkante und bleibt im Bild, auch wenn
// die Jalousie ganz oben steht.
const schiene = bild.querySelector(".raum-endschiene");
const dick = schiene.height.baseVal.value;
schiene.setAttribute("y", Math.max(0, Math.min(hoch - dick, abdeckung - dick)));
} }
/** /**
+35 -20
View File
@@ -283,38 +283,53 @@ function reglerFeld($min, $max, $wert, $einheit, $beschriftung = "")
* bewegt sich in seinen eigenen Koordinaten waagerecht, wird von jedem * bewegt sich in seinen eigenen Koordinaten waagerecht, wird von jedem
* Browser unterstuetzt und traegt dieselbe Gestaltung wie die liegenden. * Browser unterstuetzt und traegt dieselbe Gestaltung wie die liegenden.
*/ */
function senkrechterRegler($cmd, $param, $wert, $titel, $oben, $unten) function senkrechterRegler($cmd, $param, $wert, $titel, $oben, $unten, $rolle)
{ {
$min = reglerMin($param); $min = reglerMin($param);
$max = reglerMax($param); $max = reglerMax($param);
$wert = reglerWert($wert, $min, $max); $wert = reglerWert($wert, $min, $max);
// Keine Prozentzahl darunter: was der Regler bewirkt, zeigt das
// Lamellenbild daneben, und die gesparte Zeile macht die Bahn laenger.
return "<div class='raum-regler raum-spalte' data-cmd='" . intval($cmd["id"]) . "'" return "<div class='raum-regler raum-spalte' data-cmd='" . intval($cmd["id"]) . "'"
. " data-param='" . intval($param["id"]) . "'>" . " data-param='" . intval($param["id"]) . "' data-rolle='" . $rolle . "'>"
. "<span class='raum-name'>" . htmlspecialchars($titel) . "</span>" . "<span class='raum-name'>" . htmlspecialchars($titel) . "</span>"
. "<span class='raum-ende'>" . htmlspecialchars($oben) . "</span>" . "<span class='raum-ende'>" . htmlspecialchars($oben) . "</span>"
. "<div class='raum-senkrecht'>" . reglerFeld($min, $max, $wert, "%", $titel) . "</div>" . "<div class='raum-senkrecht'>" . reglerFeld($min, $max, $wert, "%", $titel) . "</div>"
. "<span class='raum-ende'>" . htmlspecialchars($unten) . "</span>" . "<span class='raum-ende'>" . htmlspecialchars($unten) . "</span>"
. "<span class='raum-regler-wert small fw-semibold'>" . $wert . "%</span>"
. "</div>"; . "</div>";
} }
/** /**
* Die sechs Lamellen neben dem Neigungsregler. * Das Fenster, wie die Jalousie davor gerade steht.
* *
* Bei 30 % oder 70 % sagt die Zahl nicht, ob man noch hinaussieht. Die * Zwei Zahlen in einem Bild: wie weit sie heruntergefahren ist und wie die
* Streifen zeigen es: schmal mit Luft dazwischen heisst offen, geschlossen * Lamellen stehen. Weder "60 %" noch "30 %" sagt, ob man noch hinaussieht -
* liegen sie aneinander. Gezeichnet wird die Hoehe, die eine gekippte * das Bild sagt es.
* Lamelle von vorn zeigt - genau das, was das Licht abhaelt. Die Hoehe setzt *
* lamellenZeichnen() in js/solar/homeMQTT.js. * Die Hoehe schneidet ab: gezeichnet wird nur der Teil des Fensters, den die
* Jalousie bedeckt, mit der Endschiene an seiner Unterkante. Die Neigung
* bestimmt, wie dick eine Lamelle von vorn erscheint - w * sin(Winkel),
* genau das, was das Licht abhaelt. Beides setzt lamellenZeichnen() in
* js/solar/homeMQTT.js.
*
* Die Kennung des Beschnitts traegt die Geraetenummer: in einem Raum haengen
* mehrere dieser Bilder, und url(#...) findet sonst immer dasselbe.
*/ */
function lamellenBild() function lamellenBild($actorId)
{ {
$html = "<svg class='raum-lamellen' viewBox='0 0 100 60' preserveAspectRatio='none' aria-hidden='true'>"; $schnitt = "lamellen" . intval($actorId);
$html = "<svg class='raum-lamellen' viewBox='0 0 100 60' preserveAspectRatio='none' aria-hidden='true'>"
. "<defs><clipPath id='" . $schnitt . "'>"
. "<rect class='raum-abdeckung' x='0' y='0' width='100' height='60'></rect>"
. "</clipPath></defs>"
. "<g clip-path='url(#" . $schnitt . ")'>";
foreach ([5, 15, 25, 35, 45, 55] as $mitte) { foreach ([5, 15, 25, 35, 45, 55] as $mitte) {
$html .= "<rect class='raum-lamelle' x='0' width='100' data-mitte='" . $mitte . "'" $html .= "<rect class='raum-lamelle' x='0' width='100' data-mitte='" . $mitte . "'"
. " y='" . ($mitte - 1) . "' height='2'></rect>"; . " y='" . ($mitte - 1) . "' height='2'></rect>";
} }
return $html . "</svg>"; return $html . "</g>"
. "<rect class='raum-endschiene' x='0' y='57' width='100' height='3'></rect>"
. "</svg>";
} }
/** Kippschalter mit dem Stand, den das Geraet zuletzt gemeldet hat. */ /** Kippschalter mit dem Stand, den das Geraet zuletzt gemeldet hat. */
@@ -434,18 +449,18 @@ function zeichneBeschattung($d)
} }
$html .= "</div>"; $html .= "</div>";
} }
// Erst das Bild, dann die Regler: neben den Fahrknoepfen faende der
// Daumen sonst beim Antippen gleich einen Schieber.
if ($position || $neigung) {
$html .= lamellenBild($d["id"]);
}
if ($position) { if ($position) {
$html .= senkrechterRegler($position, $position["params"][0], $standPos, $html .= senkrechterRegler($position, $position["params"][0], $standPos,
"Position", "oben", "unten"); "Position", "oben", "unten", "position");
} }
if ($neigung) { if ($neigung) {
// Regler und Lamellenbild bilden ein Paar: das Bild sagt, was der $html .= senkrechterRegler($neigung, $neigung["params"][0], $standNeigung,
// Regler daneben bedeutet, und laeuft beim Ziehen mit. "Neigung", "auf", "zu", "neigung");
$html .= "<div class='raum-neigung-block'>"
. senkrechterRegler($neigung, $neigung["params"][0], $standNeigung,
"Neigung", "auf", "zu")
. lamellenBild()
. "</div>";
} }
$html .= mehrMenue($rest); $html .= mehrMenue($rest);
return $html . "</div>" . karteZu(); return $html . "</div>" . karteZu();