Projektuebersicht: alle Streifen untereinander auf einer Flaeche
Neu unter Projekt -> "Alle Projekte im Streifen". uebersicht.html zeigt die Streifen aller drei Projekte gestapelt, ohne Sidebar, auf derselben Art Flaeche wie der Generator: ziehen schiebt, das Rad zoomt, Fit passt ein. Die Seite baut die Slides nicht nach. Jedes Projekt kommt als iframe mit ?nur=streifen; in diesem Modus blendet die Post-Seite ihre Bedienoberflaeche aus und zeigt den Streifen in natuerlicher Groesse, skaliert wird aussen. Ein Nachbau wuerde irgendwann von dem abweichen, was der Generator zeigt, und waere dann schlimmer als keine Uebersicht. Drei Dinge, die dieser Modus regelt: - Es wird nichts gespeichert (stateLocked). Drei Kacheln, die beim Schliessen ihren Stand zurueckschreiben, waeren genau die Falle aus POST-2.md 6. - Die Ansicht steht fest auf "streifen". Post 2 stand im Stand auf "einzeln" und meldete deshalb 1080 px statt der vollen Breite. - Der Auswahlrahmen faellt weg; in der Uebersicht ist keine Slide "die aktuelle". Die Streifenbreite kommt per postMessage von der Post-Seite: die Zahl der Slides ist je Projekt anders und soll nicht zweimal gepflegt werden. Beim Einpassen zaehlen Beschriftung, Abstaende und Polster nicht mit -- die skalieren nicht, weil sie ausserhalb des transformierten iframes stehen. Ohne diese Rechnung lief die letzte Reihe unten hinaus. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
0c4933d6b5
commit
16b85a285a
6 files changed
+603
-9
No files matched your search
@@ -24,6 +24,14 @@ Downloads landen direkt in `~/Downloads`, ohne Browser-Dialog.
|
||||
| Post 2 · Die Geschichte | `post2.html` | Carousel, 6 Slides, erscheint 12.08. | [`POST-2.md`](./POST-2.md) |
|
||||
| Post 2 · Alternative | `post2-alt.html` | Derselbe Beitrag im Theme `modern` | |
|
||||
|
||||
**Alle Streifen nebeneinander** zeigt `uebersicht.html`, erreichbar über
|
||||
*Projekt → Alle Projekte im Streifen*. Die Seite baut die Slides nicht selbst
|
||||
nach: jedes Projekt kommt als `iframe` mit `?nur=streifen`, und in diesem Modus
|
||||
blendet die Post-Seite ihre Bedienoberfläche aus, zeigt den Streifen in
|
||||
natürlicher Größe und **speichert nichts** — drei Kacheln, die beim Schließen
|
||||
ihren Stand zurückschreiben, wären genau die Falle aus [`POST-2.md`](./POST-2.md) §6.
|
||||
Skaliert wird außen; ziehen schiebt, das Rad zoomt, `Fit` passt ein.
|
||||
|
||||
**Post 2 gibt es zweimal, in zwei Themes.** Projekt und Theme sind zwei
|
||||
verschiedene Dinge: das Projekt sagt, welcher Beitrag, das Theme, wie er
|
||||
aussieht. `notebook` ist die Zeichenblock-Optik mit Raster und Marker,
|
||||
|
||||
+52
-3
@@ -1,6 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<script>
|
||||
/* Uebersichts-Modus: die Seite laeuft dann als Kachel in uebersicht.html.
|
||||
Das Attribut muss vor dem ersten Zeichnen stehen, sonst blitzt die
|
||||
Bedienoberflaeche kurz auf. Deshalb hier oben und nicht im Hauptskript. */
|
||||
if (location.search.indexOf("nur=streifen") >= 0)
|
||||
document.documentElement.dataset.nur = "streifen";
|
||||
</script>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PostGrid Post 1 · Lösung</title>
|
||||
@@ -753,6 +760,26 @@
|
||||
--guide-axis: rgba(48,49,61,.45);
|
||||
--guide-mid: rgba(200,144,26,.75);
|
||||
}
|
||||
|
||||
/* ─── Uebersichts-Modus ──────────────────────────────────────────────────
|
||||
Die Seite zeigt dann nur den Streifen, in natuerlicher Groesse und ohne
|
||||
eigenen Rahmen: die Uebersicht setzt sie als iframe ein und skaliert
|
||||
selbst. Alles Bedienbare faellt weg, auch die Hilfslinien. */
|
||||
html[data-nur="streifen"] { overflow: hidden; }
|
||||
html[data-nur="streifen"] .menubar,
|
||||
html[data-nur="streifen"] .panel,
|
||||
html[data-nur="streifen"] .hud,
|
||||
html[data-nur="streifen"] .guides { display: none !important; }
|
||||
/* Der Auswahlrahmen ist eine Bedienhilfe und hat in der Uebersicht nichts
|
||||
verloren -- dort ist keine Slide "die aktuelle". */
|
||||
html[data-nur="streifen"] .holder.sel::after { display: none; }
|
||||
html[data-nur="streifen"] .stage { background: none; height: auto; }
|
||||
html[data-nur="streifen"] .scroller { position: static; overflow: visible; }
|
||||
html[data-nur="streifen"] .canvas { padding: 0; display: block;
|
||||
width: max-content; min-width: 0; height: max-content; min-height: 0; }
|
||||
html[data-nur="streifen"] body, html[data-nur="streifen"] .app {
|
||||
height: auto; min-height: 0; background: none; overflow: visible;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -767,6 +794,8 @@
|
||||
<button type="button" data-gehe="./post2.html">Post 2</button>
|
||||
<button type="button" data-gehe="./post2-alt.html">Post 2 · Alternative</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
<button type="button" data-klick="snap-save">Stand merken</button>
|
||||
<button type="button" data-klick="snap-load">Zurück zum gemerkten Stand</button>
|
||||
<hr>
|
||||
@@ -1296,7 +1325,12 @@
|
||||
Voreinstellung an und schreibt die beim ersten Klick ueber genau die
|
||||
Datei, die gerade nicht verstanden wurde. Solange die Sperre steht, geht
|
||||
nichts mehr raus, weder in die Datei noch in den localStorage. */
|
||||
let stateLocked = false;
|
||||
/* Im Uebersichts-Modus wird nichts gespeichert: drei Kacheln, die beim
|
||||
Schliessen ihren Stand zurueckschreiben, waeren genau die Falle aus
|
||||
POST-2.md 6. Die Konstante steht hier oben, weil stateLocked sie
|
||||
braucht -- weiter unten waere sie noch nicht initialisiert. */
|
||||
const NUR_STREIFEN = document.documentElement.dataset.nur === "streifen";
|
||||
let stateLocked = NUR_STREIFEN;
|
||||
function lockState(grund) {
|
||||
stateLocked = true;
|
||||
clearTimeout(pushTimer); pushTimer = null;
|
||||
@@ -1487,7 +1521,10 @@
|
||||
variant = d.variant || variant;
|
||||
tilt = d.tilt || tilt;
|
||||
superSample = d.superSample || superSample;
|
||||
view = d.view || view;
|
||||
/* Der Uebersichts-Modus zeigt immer den Streifen. Steht im Stand
|
||||
"einzeln", waere sonst nur die zuletzt gewaehlte Slide zu sehen --
|
||||
und stripWidth() meldete 1080 statt der vollen Breite. */
|
||||
view = NUR_STREIFEN ? "streifen" : (d.view || view);
|
||||
tagOut = d.tagOut || tagOut;
|
||||
frameStyle = d.frameStyle || frameStyle;
|
||||
current = Number.isInteger(d.current) ? Math.min(d.current, SLIDES.length - 1) : 0;
|
||||
@@ -1526,7 +1563,8 @@
|
||||
springt. */
|
||||
/* zoom === null bedeutet: automatisch einpassen. Sobald der Nutzer zoomt,
|
||||
hält der Wert, bis er auf "Fit" zurückgeht. */
|
||||
let zoom = null;
|
||||
// Im Uebersichts-Modus steht der Streifen fest auf 100 % -- skaliert wird aussen.
|
||||
let zoom = NUR_STREIFEN ? 1 : null;
|
||||
|
||||
function stripWidth() { return view === "streifen" ? 1080 * SLIDES.length : 1080; }
|
||||
|
||||
@@ -1537,6 +1575,16 @@
|
||||
return Math.min(availW / stripWidth(), availH / 1350, 0.45);
|
||||
}
|
||||
|
||||
/* Die Uebersicht muss wissen, wie breit dieser Streifen ist -- die Zahl
|
||||
der Slides ist je Projekt anders. Sie kommt per postMessage statt ueber
|
||||
einen Griff ins iframe-Dokument: so bleibt die Uebersicht von den
|
||||
Interna der Post-Seite unabhaengig. */
|
||||
function groesseMelden() {
|
||||
if (!NUR_STREIFEN || parent === window) return;
|
||||
parent.postMessage({ hg: "streifen", w: stripWidth(), h: 1350,
|
||||
titel: document.title }, "*");
|
||||
}
|
||||
|
||||
function fitStrip() {
|
||||
const wrap = document.getElementById("strip-wrap");
|
||||
const k = zoom || autoFit();
|
||||
@@ -1545,6 +1593,7 @@
|
||||
wrap.style.height = Math.round(1350 * k) + "px";
|
||||
const lvl = document.getElementById("zoom-fit");
|
||||
if (lvl) lvl.textContent = zoom ? Math.round(k * 100) + " %" : "Fit";
|
||||
groesseMelden();
|
||||
}
|
||||
|
||||
function setZoom(k, anchor) {
|
||||
|
||||
+52
-3
@@ -1,6 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<script>
|
||||
/* Uebersichts-Modus: die Seite laeuft dann als Kachel in uebersicht.html.
|
||||
Das Attribut muss vor dem ersten Zeichnen stehen, sonst blitzt die
|
||||
Bedienoberflaeche kurz auf. Deshalb hier oben und nicht im Hauptskript. */
|
||||
if (location.search.indexOf("nur=streifen") >= 0)
|
||||
document.documentElement.dataset.nur = "streifen";
|
||||
</script>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PostGrid Post 2 · Alternative</title>
|
||||
@@ -951,6 +958,26 @@
|
||||
--guide-axis: rgba(48,49,61,.45);
|
||||
--guide-mid: rgba(200,144,26,.75);
|
||||
}
|
||||
|
||||
/* ─── Uebersichts-Modus ──────────────────────────────────────────────────
|
||||
Die Seite zeigt dann nur den Streifen, in natuerlicher Groesse und ohne
|
||||
eigenen Rahmen: die Uebersicht setzt sie als iframe ein und skaliert
|
||||
selbst. Alles Bedienbare faellt weg, auch die Hilfslinien. */
|
||||
html[data-nur="streifen"] { overflow: hidden; }
|
||||
html[data-nur="streifen"] .menubar,
|
||||
html[data-nur="streifen"] .panel,
|
||||
html[data-nur="streifen"] .hud,
|
||||
html[data-nur="streifen"] .guides { display: none !important; }
|
||||
/* Der Auswahlrahmen ist eine Bedienhilfe und hat in der Uebersicht nichts
|
||||
verloren -- dort ist keine Slide "die aktuelle". */
|
||||
html[data-nur="streifen"] .holder.sel::after { display: none; }
|
||||
html[data-nur="streifen"] .stage { background: none; height: auto; }
|
||||
html[data-nur="streifen"] .scroller { position: static; overflow: visible; }
|
||||
html[data-nur="streifen"] .canvas { padding: 0; display: block;
|
||||
width: max-content; min-width: 0; height: max-content; min-height: 0; }
|
||||
html[data-nur="streifen"] body, html[data-nur="streifen"] .app {
|
||||
height: auto; min-height: 0; background: none; overflow: visible;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -965,6 +992,8 @@
|
||||
<button type="button" data-gehe="./post2.html">Post 2</button>
|
||||
<button type="button" data-gehe="./post2-alt.html">Post 2 · Alternative</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
<button type="button" data-klick="snap-save">Stand merken</button>
|
||||
<button type="button" data-klick="snap-load">Zurück zum gemerkten Stand</button>
|
||||
<hr>
|
||||
@@ -2699,7 +2728,12 @@
|
||||
Voreinstellung an und schreibt die beim ersten Klick ueber genau die
|
||||
Datei, die gerade nicht verstanden wurde. Solange die Sperre steht, geht
|
||||
nichts mehr raus, weder in die Datei noch in den localStorage. */
|
||||
let stateLocked = false;
|
||||
/* Im Uebersichts-Modus wird nichts gespeichert: drei Kacheln, die beim
|
||||
Schliessen ihren Stand zurueckschreiben, waeren genau die Falle aus
|
||||
POST-2.md 6. Die Konstante steht hier oben, weil stateLocked sie
|
||||
braucht -- weiter unten waere sie noch nicht initialisiert. */
|
||||
const NUR_STREIFEN = document.documentElement.dataset.nur === "streifen";
|
||||
let stateLocked = NUR_STREIFEN;
|
||||
function lockState(grund) {
|
||||
stateLocked = true;
|
||||
clearTimeout(pushTimer); pushTimer = null;
|
||||
@@ -2893,7 +2927,10 @@
|
||||
variant = d.variant || variant;
|
||||
tilt = d.tilt || tilt;
|
||||
superSample = d.superSample || superSample;
|
||||
view = d.view || view;
|
||||
/* Der Uebersichts-Modus zeigt immer den Streifen. Steht im Stand
|
||||
"einzeln", waere sonst nur die zuletzt gewaehlte Slide zu sehen --
|
||||
und stripWidth() meldete 1080 statt der vollen Breite. */
|
||||
view = NUR_STREIFEN ? "streifen" : (d.view || view);
|
||||
tagOut = d.tagOut || tagOut;
|
||||
frameStyle = d.frameStyle || frameStyle;
|
||||
bandTon = d.bandTon || bandTon;
|
||||
@@ -2935,7 +2972,8 @@
|
||||
springt. */
|
||||
/* zoom === null bedeutet: automatisch einpassen. Sobald der Nutzer zoomt,
|
||||
hält der Wert, bis er auf "Fit" zurückgeht. */
|
||||
let zoom = null;
|
||||
// Im Uebersichts-Modus steht der Streifen fest auf 100 % -- skaliert wird aussen.
|
||||
let zoom = NUR_STREIFEN ? 1 : null;
|
||||
|
||||
function stripWidth() { return view === "streifen" ? 1080 * SLIDES.length : 1080; }
|
||||
|
||||
@@ -2946,6 +2984,16 @@
|
||||
return Math.min(availW / stripWidth(), availH / 1350, 0.45);
|
||||
}
|
||||
|
||||
/* Die Uebersicht muss wissen, wie breit dieser Streifen ist -- die Zahl
|
||||
der Slides ist je Projekt anders. Sie kommt per postMessage statt ueber
|
||||
einen Griff ins iframe-Dokument: so bleibt die Uebersicht von den
|
||||
Interna der Post-Seite unabhaengig. */
|
||||
function groesseMelden() {
|
||||
if (!NUR_STREIFEN || parent === window) return;
|
||||
parent.postMessage({ hg: "streifen", w: stripWidth(), h: 1350,
|
||||
titel: document.title }, "*");
|
||||
}
|
||||
|
||||
function fitStrip() {
|
||||
const wrap = document.getElementById("strip-wrap");
|
||||
const k = zoom || autoFit();
|
||||
@@ -2954,6 +3002,7 @@
|
||||
wrap.style.height = Math.round(1350 * k) + "px";
|
||||
const lvl = document.getElementById("zoom-fit");
|
||||
if (lvl) lvl.textContent = zoom ? Math.round(k * 100) + " %" : "Fit";
|
||||
groesseMelden();
|
||||
}
|
||||
|
||||
function setZoom(k, anchor) {
|
||||
|
||||
+52
-3
@@ -1,6 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<script>
|
||||
/* Uebersichts-Modus: die Seite laeuft dann als Kachel in uebersicht.html.
|
||||
Das Attribut muss vor dem ersten Zeichnen stehen, sonst blitzt die
|
||||
Bedienoberflaeche kurz auf. Deshalb hier oben und nicht im Hauptskript. */
|
||||
if (location.search.indexOf("nur=streifen") >= 0)
|
||||
document.documentElement.dataset.nur = "streifen";
|
||||
</script>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PostGrid Post 2</title>
|
||||
@@ -844,6 +851,26 @@
|
||||
--guide-axis: rgba(48,49,61,.45);
|
||||
--guide-mid: rgba(200,144,26,.75);
|
||||
}
|
||||
|
||||
/* ─── Uebersichts-Modus ──────────────────────────────────────────────────
|
||||
Die Seite zeigt dann nur den Streifen, in natuerlicher Groesse und ohne
|
||||
eigenen Rahmen: die Uebersicht setzt sie als iframe ein und skaliert
|
||||
selbst. Alles Bedienbare faellt weg, auch die Hilfslinien. */
|
||||
html[data-nur="streifen"] { overflow: hidden; }
|
||||
html[data-nur="streifen"] .menubar,
|
||||
html[data-nur="streifen"] .panel,
|
||||
html[data-nur="streifen"] .hud,
|
||||
html[data-nur="streifen"] .guides { display: none !important; }
|
||||
/* Der Auswahlrahmen ist eine Bedienhilfe und hat in der Uebersicht nichts
|
||||
verloren -- dort ist keine Slide "die aktuelle". */
|
||||
html[data-nur="streifen"] .holder.sel::after { display: none; }
|
||||
html[data-nur="streifen"] .stage { background: none; height: auto; }
|
||||
html[data-nur="streifen"] .scroller { position: static; overflow: visible; }
|
||||
html[data-nur="streifen"] .canvas { padding: 0; display: block;
|
||||
width: max-content; min-width: 0; height: max-content; min-height: 0; }
|
||||
html[data-nur="streifen"] body, html[data-nur="streifen"] .app {
|
||||
height: auto; min-height: 0; background: none; overflow: visible;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -858,6 +885,8 @@
|
||||
<button type="button" data-gehe="./post2.html">Post 2</button>
|
||||
<button type="button" data-gehe="./post2-alt.html">Post 2 · Alternative</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
<button type="button" data-klick="snap-save">Stand merken</button>
|
||||
<button type="button" data-klick="snap-load">Zurück zum gemerkten Stand</button>
|
||||
<hr>
|
||||
@@ -2454,7 +2483,12 @@
|
||||
Voreinstellung an und schreibt die beim ersten Klick ueber genau die
|
||||
Datei, die gerade nicht verstanden wurde. Solange die Sperre steht, geht
|
||||
nichts mehr raus, weder in die Datei noch in den localStorage. */
|
||||
let stateLocked = false;
|
||||
/* Im Uebersichts-Modus wird nichts gespeichert: drei Kacheln, die beim
|
||||
Schliessen ihren Stand zurueckschreiben, waeren genau die Falle aus
|
||||
POST-2.md 6. Die Konstante steht hier oben, weil stateLocked sie
|
||||
braucht -- weiter unten waere sie noch nicht initialisiert. */
|
||||
const NUR_STREIFEN = document.documentElement.dataset.nur === "streifen";
|
||||
let stateLocked = NUR_STREIFEN;
|
||||
function lockState(grund) {
|
||||
stateLocked = true;
|
||||
clearTimeout(pushTimer); pushTimer = null;
|
||||
@@ -2645,7 +2679,10 @@
|
||||
variant = d.variant || variant;
|
||||
tilt = d.tilt || tilt;
|
||||
superSample = d.superSample || superSample;
|
||||
view = d.view || view;
|
||||
/* Der Uebersichts-Modus zeigt immer den Streifen. Steht im Stand
|
||||
"einzeln", waere sonst nur die zuletzt gewaehlte Slide zu sehen --
|
||||
und stripWidth() meldete 1080 statt der vollen Breite. */
|
||||
view = NUR_STREIFEN ? "streifen" : (d.view || view);
|
||||
tagOut = d.tagOut || tagOut;
|
||||
frameStyle = d.frameStyle || frameStyle;
|
||||
current = Number.isInteger(d.current) ? Math.min(d.current, SLIDES.length - 1) : 0;
|
||||
@@ -2684,7 +2721,8 @@
|
||||
springt. */
|
||||
/* zoom === null bedeutet: automatisch einpassen. Sobald der Nutzer zoomt,
|
||||
hält der Wert, bis er auf "Fit" zurückgeht. */
|
||||
let zoom = null;
|
||||
// Im Uebersichts-Modus steht der Streifen fest auf 100 % -- skaliert wird aussen.
|
||||
let zoom = NUR_STREIFEN ? 1 : null;
|
||||
|
||||
function stripWidth() { return view === "streifen" ? 1080 * SLIDES.length : 1080; }
|
||||
|
||||
@@ -2695,6 +2733,16 @@
|
||||
return Math.min(availW / stripWidth(), availH / 1350, 0.45);
|
||||
}
|
||||
|
||||
/* Die Uebersicht muss wissen, wie breit dieser Streifen ist -- die Zahl
|
||||
der Slides ist je Projekt anders. Sie kommt per postMessage statt ueber
|
||||
einen Griff ins iframe-Dokument: so bleibt die Uebersicht von den
|
||||
Interna der Post-Seite unabhaengig. */
|
||||
function groesseMelden() {
|
||||
if (!NUR_STREIFEN || parent === window) return;
|
||||
parent.postMessage({ hg: "streifen", w: stripWidth(), h: 1350,
|
||||
titel: document.title }, "*");
|
||||
}
|
||||
|
||||
function fitStrip() {
|
||||
const wrap = document.getElementById("strip-wrap");
|
||||
const k = zoom || autoFit();
|
||||
@@ -2703,6 +2751,7 @@
|
||||
wrap.style.height = Math.round(1350 * k) + "px";
|
||||
const lvl = document.getElementById("zoom-fit");
|
||||
if (lvl) lvl.textContent = zoom ? Math.round(k * 100) + " %" : "Fit";
|
||||
groesseMelden();
|
||||
}
|
||||
|
||||
function setZoom(k, anchor) {
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"bandTon": "teal",
|
||||
"bgState": [
|
||||
{
|
||||
"bg": "raster",
|
||||
"int": "kraeftig"
|
||||
},
|
||||
{
|
||||
"bg": "raster",
|
||||
"int": "dezent"
|
||||
},
|
||||
{
|
||||
"bg": "raster",
|
||||
"int": "dezent"
|
||||
},
|
||||
{
|
||||
"bg": "raster",
|
||||
"int": "dezent"
|
||||
},
|
||||
{
|
||||
"bg": "raster",
|
||||
"int": "dezent"
|
||||
},
|
||||
{
|
||||
"bg": "raster",
|
||||
"int": "dezent"
|
||||
}
|
||||
],
|
||||
"current": 4,
|
||||
"deck": [
|
||||
{
|
||||
"ref": "titel",
|
||||
"werte": {
|
||||
"body": "<br>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ref": "prozesse",
|
||||
"werte": {}
|
||||
},
|
||||
{
|
||||
"ref": "wechseln",
|
||||
"werte": {}
|
||||
},
|
||||
{
|
||||
"ref": "mehr",
|
||||
"werte": {}
|
||||
},
|
||||
{
|
||||
"ref": "verbinden",
|
||||
"werte": {}
|
||||
},
|
||||
{
|
||||
"ref": "warteliste",
|
||||
"werte": {
|
||||
"body": "Setz dich auf die Warteliste, dann melden wir uns."
|
||||
}
|
||||
}
|
||||
],
|
||||
"edits": [
|
||||
{
|
||||
"body": {
|
||||
"dx": 102,
|
||||
"dy": 0
|
||||
},
|
||||
"headline": {
|
||||
"dx": 37,
|
||||
"dy": 3,
|
||||
"size": 88
|
||||
},
|
||||
"vis": {
|
||||
"dx": 3,
|
||||
"dy": -9
|
||||
}
|
||||
},
|
||||
{
|
||||
"body": {
|
||||
"dx": 0,
|
||||
"dy": 20
|
||||
},
|
||||
"headline": {
|
||||
"dy": 3
|
||||
},
|
||||
"vis": {
|
||||
"dx": 113,
|
||||
"dy": -59
|
||||
}
|
||||
},
|
||||
{
|
||||
"body": {
|
||||
"dx": 0,
|
||||
"dy": 20
|
||||
},
|
||||
"headline": {
|
||||
"dy": 3
|
||||
},
|
||||
"vis": {
|
||||
"dx": -3,
|
||||
"dy": -88
|
||||
}
|
||||
},
|
||||
{
|
||||
"body": {
|
||||
"dx": 0,
|
||||
"dy": 20
|
||||
},
|
||||
"headline": {
|
||||
"dx": 0,
|
||||
"dy": 3
|
||||
},
|
||||
"vis": {
|
||||
"dx": -68,
|
||||
"dy": -25,
|
||||
"size": 1028
|
||||
}
|
||||
},
|
||||
{
|
||||
"body": {
|
||||
"dy": 20
|
||||
},
|
||||
"headline": {
|
||||
"dy": 3
|
||||
},
|
||||
"vis": {
|
||||
"dx": 33,
|
||||
"dy": -138
|
||||
}
|
||||
},
|
||||
{
|
||||
"body": {
|
||||
"dy": 20
|
||||
},
|
||||
"headline": {
|
||||
"dy": 3
|
||||
},
|
||||
"vis": {
|
||||
"dx": 12,
|
||||
"dy": 74,
|
||||
"size": 1184
|
||||
}
|
||||
}
|
||||
],
|
||||
"frameStyle": "aus",
|
||||
"layoutState": [
|
||||
"standard",
|
||||
"standard",
|
||||
"standard",
|
||||
"standard",
|
||||
"standard",
|
||||
"standard"
|
||||
],
|
||||
"superSample": 3,
|
||||
"tagOut": "an",
|
||||
"tilt": "an",
|
||||
"v": 7,
|
||||
"variant": "hell",
|
||||
"view": "streifen"
|
||||
}
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PostGrid · Alle Projekte</title>
|
||||
<link rel="stylesheet" href="./assets/fonts/manrope.css">
|
||||
<style>
|
||||
/* ─── Projektübersicht ────────────────────────────────────────────────────
|
||||
Alle Streifen untereinander auf einer Fläche. Die Seite baut die Slides
|
||||
nicht selbst: jedes Projekt kommt als iframe im Modus ?nur=streifen, in
|
||||
dem die Post-Seite ihre Bedienoberfläche ausblendet und nur den Streifen
|
||||
in natürlicher Größe zeigt. Skaliert wird hier außen.
|
||||
|
||||
Warum nicht nachbauen: die Streifen müssen genau das zeigen, was der
|
||||
Generator zeigt — bis auf den letzten gespeicherten Versatz. Jede zweite
|
||||
Implementierung würde irgendwann abweichen, und dann wäre die Übersicht
|
||||
schlimmer als keine. */
|
||||
* { box-sizing: border-box; }
|
||||
:root { color-scheme: dark; }
|
||||
html, body { height: 100%; }
|
||||
body {
|
||||
margin: 0; font-family: Manrope, system-ui, sans-serif;
|
||||
background: #101116; color: #C7CCD6; overflow: hidden;
|
||||
}
|
||||
|
||||
.menubar {
|
||||
position: relative; z-index: 10; display: flex; align-items: center; gap: 2px;
|
||||
height: 34px; padding: 0 10px; background: #16181F;
|
||||
border-bottom: 1px solid #23262F;
|
||||
font-size: 13px; user-select: none;
|
||||
}
|
||||
.menubar .marke { font-weight: 800; color: #FFFFFF; margin-right: 14px; letter-spacing: -.01em; }
|
||||
.menu { position: relative; }
|
||||
.menu > button {
|
||||
font: inherit; color: #C7CCD6; background: none; border: 0;
|
||||
padding: 5px 10px; border-radius: 6px; cursor: pointer;
|
||||
}
|
||||
.menu > button:hover, .menu[data-auf] > button { background: #23262F; color: #FFFFFF; }
|
||||
.menu .liste {
|
||||
display: none; position: absolute; left: 0; top: 100%; min-width: 250px;
|
||||
padding: 5px; background: #1B1E26; border: 1px solid #2E313B;
|
||||
border-radius: 9px; flex-direction: column;
|
||||
}
|
||||
.menu[data-auf] .liste { display: flex; }
|
||||
.menu .liste button {
|
||||
font: inherit; text-align: left; color: #C7CCD6; background: none;
|
||||
border: 0; padding: 7px 10px; border-radius: 6px; cursor: pointer;
|
||||
}
|
||||
.menu .liste button:hover { background: #262A34; color: #FFFFFF; }
|
||||
.menu .liste hr { border: 0; border-top: 1px solid #2E313B; margin: 5px 2px; }
|
||||
|
||||
/* Dieselbe Fläche wie im Generator: gepunkteter Grund, ziehbar, Rad zoomt. */
|
||||
.stage { position: absolute; inset: 34px 0 0; overflow: hidden;
|
||||
background-color: #101116;
|
||||
background-image: radial-gradient(circle, #282B35 1.6px, transparent 1.6px);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.scroller { position: absolute; inset: 0; overflow: auto; overscroll-behavior: contain; }
|
||||
.scroller.panning { cursor: grabbing; }
|
||||
.flaeche {
|
||||
width: max-content; min-width: 100%; height: max-content; min-height: 100%;
|
||||
padding: 54px; display: flex; flex-direction: column; gap: 46px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.reihe { display: flex; flex-direction: column; gap: 10px; }
|
||||
.reihe h2 {
|
||||
margin: 0; font-size: 12px; font-weight: 800; letter-spacing: .14em;
|
||||
text-transform: uppercase; color: #6E7480;
|
||||
}
|
||||
.reihe h2 b { color: #C7CCD6; font-weight: 800; }
|
||||
/* Der Rahmen bekommt die Größe des skalierten Streifens, das iframe darin
|
||||
bleibt in Originalgröße und wird nur transformiert. Ohne den Rahmen
|
||||
würde das ungeskalierte iframe die Fläche aufblähen. */
|
||||
.rahmen { overflow: hidden; background: #FFFFFF; border: 1px solid #2E313B; }
|
||||
.rahmen iframe {
|
||||
display: block; border: 0; transform-origin: 0 0; background: #FFFFFF;
|
||||
}
|
||||
.reihe.fehlt .rahmen { display: grid; place-items: center; width: 420px; height: 90px;
|
||||
background: #16181F; color: #6E7480; font-size: 13px; }
|
||||
|
||||
.hud {
|
||||
position: absolute; left: 0; right: 0; bottom: 0; z-index: 5;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 8px 12px; background: rgba(16,17,22,.92);
|
||||
border-top: 1px solid #23262F;
|
||||
}
|
||||
.hud p { margin: 0; font-size: 12px; color: #6E7480; letter-spacing: .04em; }
|
||||
.zoom { margin-left: auto; display: flex; gap: 4px; }
|
||||
.zoom button {
|
||||
font: inherit; font-size: 13px; font-weight: 700; cursor: pointer;
|
||||
background: #1D1F26; color: #C7CCD6; border: 1px solid #2E313B;
|
||||
border-radius: 7px; padding: 6px 10px; min-width: 32px;
|
||||
}
|
||||
.zoom button:hover { background: #262A34; color: #FFFFFF; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="menubar">
|
||||
<span class="marke">PostGrid</span>
|
||||
<div class="menu" data-menu="projekt">
|
||||
<button type="button">Projekt</button>
|
||||
<div class="liste">
|
||||
<button type="button" data-gehe="./post1.html">Post 1 · Lösung</button>
|
||||
<button type="button" data-gehe="./post2.html">Post 2 · Die Geschichte</button>
|
||||
<button type="button" data-gehe="./post2-alt.html">Post 2 · Alternative</button>
|
||||
<hr>
|
||||
<button type="button" data-klick="neu">Streifen neu laden</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="stage">
|
||||
<div class="scroller" id="scroller">
|
||||
<div class="flaeche" id="flaeche"></div>
|
||||
</div>
|
||||
<div class="hud">
|
||||
<p id="meta">Lade Streifen ...</p>
|
||||
<div class="zoom">
|
||||
<button id="zoom-out" title="Auszoomen">−</button>
|
||||
<button id="zoom-fit" title="Einpassen">Fit</button>
|
||||
<button id="zoom-in" title="Einzoomen">+</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
const PROJEKTE = [
|
||||
{ datei: "./post1.html", name: "Post 1 · Lösung" },
|
||||
{ datei: "./post2.html", name: "Post 2 · Die Geschichte", theme: "notebook" },
|
||||
{ datei: "./post2-alt.html", name: "Post 2 · Alternative", theme: "modern" },
|
||||
];
|
||||
|
||||
const flaeche = document.getElementById("flaeche");
|
||||
const scroller = document.getElementById("scroller");
|
||||
const meta = document.getElementById("meta");
|
||||
|
||||
/* Ein Streifen ist bis zu 6480 px breit. Der Maßstab ist deshalb nicht
|
||||
frei, sondern immer relativ zu "passt in die Fläche" -- sonst müsste man
|
||||
bei jedem Öffnen erst herauszoomen. zoom === null heißt eingepasst. */
|
||||
let zoom = null;
|
||||
const reihen = [];
|
||||
|
||||
function bauen() {
|
||||
flaeche.innerHTML = "";
|
||||
reihen.length = 0;
|
||||
PROJEKTE.forEach(pr => {
|
||||
const reihe = document.createElement("div");
|
||||
reihe.className = "reihe";
|
||||
const h2 = document.createElement("h2");
|
||||
h2.innerHTML = "<b>" + pr.name + "</b>" + (pr.theme ? " · Theme " + pr.theme : "");
|
||||
const rahmen = document.createElement("div");
|
||||
rahmen.className = "rahmen";
|
||||
const ram = document.createElement("iframe");
|
||||
ram.src = pr.datei + "?nur=streifen";
|
||||
ram.loading = "eager";
|
||||
rahmen.appendChild(ram);
|
||||
reihe.appendChild(h2); reihe.appendChild(rahmen);
|
||||
flaeche.appendChild(reihe);
|
||||
reihen.push({ pr, reihe, rahmen, ram, w: 6480, h: 1350, da: false });
|
||||
});
|
||||
}
|
||||
|
||||
/* Jede Post-Seite meldet ihre Streifenbreite, sobald sie steht. Die Zahl
|
||||
der Slides ist je Projekt anders und kann sich ändern, deshalb wird sie
|
||||
nicht hier gepflegt. */
|
||||
window.addEventListener("message", e => {
|
||||
const d = e.data;
|
||||
if (!d || d.hg !== "streifen") return;
|
||||
const r = reihen.find(x => x.ram.contentWindow === e.source);
|
||||
if (!r) return;
|
||||
r.w = d.w; r.h = d.h; r.da = true;
|
||||
legen();
|
||||
});
|
||||
|
||||
/* Beschriftung, Abstände und Polster skalieren NICHT mit -- sie stehen
|
||||
außerhalb des transformierten iframes. Ein einfaches
|
||||
"verfügbar / Gesamthöhe" ließ die letzte Reihe deshalb unten
|
||||
hinauslaufen. Die feste Höhe muss vorher abgezogen werden. */
|
||||
const POLSTER = 54 * 2, LUECKE = 46, KOPFZEILE = 32;
|
||||
function passend() {
|
||||
const n = reihen.length;
|
||||
const breit = Math.max(...reihen.map(r => r.w), 1080);
|
||||
const hoch = reihen.reduce((s, r) => s + r.h, 0);
|
||||
const fest = POLSTER + (n - 1) * LUECKE + n * KOPFZEILE;
|
||||
return Math.min((scroller.clientWidth - POLSTER) / breit,
|
||||
Math.max(0.02, (scroller.clientHeight - fest) / hoch), 0.5);
|
||||
}
|
||||
|
||||
function legen() {
|
||||
const k = zoom || passend();
|
||||
reihen.forEach(r => {
|
||||
r.ram.style.width = r.w + "px";
|
||||
r.ram.style.height = r.h + "px";
|
||||
r.ram.style.transform = "scale(" + k + ")";
|
||||
r.rahmen.style.width = Math.round(r.w * k) + "px";
|
||||
r.rahmen.style.height = Math.round(r.h * k) + "px";
|
||||
});
|
||||
document.getElementById("zoom-fit").textContent = zoom ? Math.round(k * 100) + " %" : "Fit";
|
||||
const offen = reihen.filter(r => r.da).length;
|
||||
meta.textContent = offen < reihen.length
|
||||
? "Lade Streifen ... " + offen + " von " + reihen.length
|
||||
: reihen.length + " Projekte · " + reihen.map(r => Math.round(r.w / 1080) + " Slides").join(" · ");
|
||||
}
|
||||
|
||||
function zoomAuf(k, anker) {
|
||||
const vor = zoom || passend();
|
||||
const ax = anker ? anker.x : scroller.clientWidth / 2;
|
||||
const ay = anker ? anker.y : scroller.clientHeight / 2;
|
||||
const s0 = scroller.getBoundingClientRect();
|
||||
const f0 = flaeche.getBoundingClientRect();
|
||||
const u = (s0.left + ax - f0.left) / vor;
|
||||
const v = (s0.top + ay - f0.top) / vor;
|
||||
zoom = Math.min(1, Math.max(0.02, k));
|
||||
legen();
|
||||
const f1 = flaeche.getBoundingClientRect();
|
||||
scroller.scrollLeft += (f1.left + u * zoom) - (s0.left + ax);
|
||||
scroller.scrollTop += (f1.top + v * zoom) - (s0.top + ay);
|
||||
}
|
||||
|
||||
// ─── Bedienung: ziehen, Rad zoomt, Umschalt+Rad schiebt waagerecht ───────
|
||||
let zieht = null;
|
||||
scroller.addEventListener("pointerdown", e => {
|
||||
if (e.button !== 0 && e.button !== 1) return;
|
||||
zieht = { x: e.clientX, y: e.clientY, l: scroller.scrollLeft, t: scroller.scrollTop };
|
||||
scroller.classList.add("panning");
|
||||
scroller.setPointerCapture(e.pointerId);
|
||||
});
|
||||
scroller.addEventListener("pointermove", e => {
|
||||
if (!zieht) return;
|
||||
scroller.scrollLeft = zieht.l - (e.clientX - zieht.x);
|
||||
scroller.scrollTop = zieht.t - (e.clientY - zieht.y);
|
||||
});
|
||||
const losen = () => { zieht = null; scroller.classList.remove("panning"); };
|
||||
scroller.addEventListener("pointerup", losen);
|
||||
scroller.addEventListener("pointercancel", losen);
|
||||
|
||||
scroller.addEventListener("wheel", e => {
|
||||
if (e.shiftKey) return; // waagerecht schieben
|
||||
e.preventDefault();
|
||||
const r = scroller.getBoundingClientRect();
|
||||
zoomAuf((zoom || passend()) * (e.deltaY < 0 ? 1.12 : 1 / 1.12),
|
||||
{ x: e.clientX - r.left, y: e.clientY - r.top });
|
||||
}, { passive: false });
|
||||
|
||||
document.getElementById("zoom-in").addEventListener("click", () => zoomAuf((zoom || passend()) * 1.2));
|
||||
document.getElementById("zoom-out").addEventListener("click", () => zoomAuf((zoom || passend()) / 1.2));
|
||||
document.getElementById("zoom-fit").addEventListener("click", () => { zoom = null; legen(); });
|
||||
window.addEventListener("resize", () => { if (!zoom) legen(); });
|
||||
|
||||
// ─── Menü ────────────────────────────────────────────────────────────────
|
||||
document.querySelectorAll(".menu").forEach(m => {
|
||||
m.querySelector("button").addEventListener("click", e => {
|
||||
e.stopPropagation();
|
||||
const auf = m.hasAttribute("data-auf");
|
||||
document.querySelectorAll(".menu").forEach(x => x.removeAttribute("data-auf"));
|
||||
if (!auf) m.setAttribute("data-auf", "");
|
||||
});
|
||||
});
|
||||
document.addEventListener("click", e => {
|
||||
const b = e.target.closest("button[data-gehe], button[data-klick]");
|
||||
document.querySelectorAll(".menu").forEach(x => x.removeAttribute("data-auf"));
|
||||
if (!b) return;
|
||||
if (b.dataset.gehe) { location.href = b.dataset.gehe; return; }
|
||||
if (b.dataset.klick === "neu") { bauen(); legen(); }
|
||||
});
|
||||
document.addEventListener("keydown", e => {
|
||||
if (e.key === "0") { zoom = null; legen(); }
|
||||
});
|
||||
|
||||
bauen();
|
||||
legen();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user