Bewegtbild: der richtige Weg von der Nachricht zur Aufgabe

Die erste Fassung hatte den Ablauf im Kreis: getippt wurde "Danke. Ich habe
dazu direkt eine Aufgabe erstellt.", abgeschickt -- und *danach* wuchs an
dieser Nachricht eine Aufgabenkarte. Sie meldete also eine Aufgabe, die es
noch gar nicht gab, und wurde anschliessend selbst zu einer.

Nachgesehen in GeneralTeamChatPage.tsx: eine Aufgabe entsteht ueber das Menue
hinter dem Pluszeichen ("Aufgabe erstellen", Zeile 275). Das Ergebnis ist
Nachricht m4 -- Text UND angehaengte Aufgabenkarte in einem -- gefolgt von der
Systemzeile m5. Ein Kontextmenue an einer bestehenden Nachricht gibt es dort
nicht; eine Umwandlung "Nachricht -> Aufgabe" zu zeigen waere eine Funktion,
die wir nicht haben.

Jetzt also: Notiz tippen, Pluszeichen, "Aufgabe erstellen" waehlen, und beides
geht zusammen als eine Nachricht raus, danach die Bestaetigung im Projekt. Der
Satz im Perfekt stimmt damit auch -- wenn die Nachricht ankommt, haengt die
Aufgabe daran.

Am Ende wird nichts mehr zurueckgenommen. Der Vorgaenger blendete die neue
Nachricht wieder aus, damit der Schleifenstoss unsichtbar wird; das sah aus,
als loesche sich eine abgeschickte Nachricht von selbst. Eine Schleife darf
springen, eine Nachricht darf nicht verschwinden.

Dazu ein Mauszeiger, der die Bedienung erzaehlt -- ohne ihn geht ein Menue
auf und man weiss nicht, warum -- und acht statt fuenf Sekunden: der Ablauf
hat jetzt fuenf Schritte, und jeder braucht seinen Moment.

Was ein Standbild traegt, traegt eine Animation nicht automatisch mit: auf
Slide 3 liest sich dieselbe Nachricht einwandfrei, weil dort ein Zustand steht
und keine Reihenfolge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
dejanandClaude Opus 5 committed 2026-08-26 00:09:45 +02:00
1 parent 9e8b2ea607
commit d4feaa175b
4 files changed
+231 -88

No files matched your search

+55 -20
View File
@@ -36,7 +36,7 @@ Dazu, außerhalb des Streifens:
| Schlüssel | Tag | Überschrift | Was zu sehen ist | | Schlüssel | Tag | Überschrift | Was zu sehen ist |
|---|---|---|---| |---|---|---|---|
| `animChat` | Bewegtbild | Getippt, **und schon im Projekt.** | 5 s Animation: die Nachricht wird geschrieben, abgeschickt und landet mit ihrer Aufgabe im Verlauf | | `animChat` | Bewegtbild | Getippt, **und schon im Projekt.** | 8 s Animation: eine Notiz wird getippt, über das Pluszeichen wird „Aufgabe erstellen" gewählt, und beides geht zusammen als eine Nachricht raus |
**Slide 3 ist der Kern.** Der Redaktionsplan verlangt „Foto rein, Aufgabe **Slide 3 ist der Kern.** Der Redaktionsplan verlangt „Foto rein, Aufgabe
raus", und genau das liegt im Werkzeug als Abfolge vor: Tobias schickt die raus", und genau das liegt im Werkzeug als Abfolge vor: Tobias schickt die
@@ -324,7 +324,7 @@ violett.** Im Original steht dort `bg-surface-neutral-primary` mit Rand und
--- ---
## 6. Vierzehn Fallen aus diesem Bau ## 6. Fünfzehn Fallen aus diesem Bau
**Die Listenzeile hat drei Zeilen, nicht zwei.** Der erste Anlauf setzte Titel **Die Listenzeile hat drei Zeilen, nicht zwei.** Der erste Anlauf setzte Titel
und Vorschau untereinander und schob Nadel und Ungelesen-Punkt rechts neben und Vorschau untereinander und schob Nadel und Ungelesen-Punkt rechts neben
@@ -429,42 +429,77 @@ Spalte aus zwei Zeilen — oben eine neu eingezogene `#stageflaeche` mit Scrolle
und Fußleiste, darunter die Leiste. Eingezogen wird sie beim Start per Skript, und Fußleiste, darunter die Leiste. Eingezogen wird sie beim Start per Skript,
damit der HTML-Rumpf jeder Datei unverändert bleibt. damit der HTML-Rumpf jeder Datei unverändert bleibt.
**Eine Animation deckt auf, was ein Standbild verdeckt.** Auf Slide 3 steht
die Nachricht „Danke. Ich habe dazu direkt eine Aufgabe erstellt." mit
angehängter Aufgabenkarte, und das liest sich einwandfrei: ein Zustand, eine
Momentaufnahme. Sobald derselbe Inhalt einen Ablauf bekommt, fällt auf, dass
Text und Anhang einander bedingen — die Nachricht meldete eine Aufgabe, die im
selben Zug erst entstand. Der Fehler lag nicht in der Quelle, sondern darin,
eine Momentaufnahme für eine Reihenfolge zu halten. Mehr dazu in §7.
--- ---
## 7. Das Bewegtbild ## 7. Das Bewegtbild
Fünf Sekunden, 150 Bilder, `animation/post6-animChat.mp4`. Gezeichnet wird es Acht Sekunden, 240 Bilder, `animation/post6-animChat.mp4`. Gezeichnet wird es
von `animChat(i, t)` in `post6.html`, mit denselben Bausteinen wie jede Slide — von `animChat(i, t)` in `post6.html`, mit denselben Bausteinen wie jede Slide —
nur bekommt die Funktion neben der Nummer den Fortschritt `t` und leitet daraus nur bekommt die Funktion neben der Nummer den Fortschritt `t` und leitet daraus
alles ab. Der Eintrag steht in `ANIMS`, nicht in `SLIDES`. alles ab. Der Eintrag steht in `ANIMS`, nicht in `SLIDES`.
| Zeit | Was passiert | | Zeit | Was passiert |
|---|---| |---|---|
| 0,3 – 2,2 s | Die Antwort wird Zeichen für Zeichen ins Eingabefeld getippt, der Cursor blinkt. Sobald Text darin steht, wird aus dem Mikrofon der Senden-Knopf | | 0,4 – 2,9 s | Die Notiz wird Zeichen für Zeichen ins Eingabefeld getippt, der Cursor blinkt. Sobald Text darin steht, wird aus dem Mikrofon der Senden-Knopf |
| 2,2 – 2,5 s | Das Feld leert sich | | 2,9 – 3,8 s | Der Zeiger fährt auf das Pluszeichen und klickt |
| 2,4 – 3,3 s | Die Blase wächst von unten in den Verlauf, der Verlauf schiebt sich nach oben | | 3,6 – 4,5 s | Das Menü klappt nach oben auf, die Zeilen gestaffelt |
| 3,2 – 4,0 s | Die Aufgabenkarte klappt in der Blase auf, die Blase wächst mit | | 4,5 – 5,8 s | Der Zeiger geht auf „Aufgabe erstellen", die Zeile hebt sich, Klick |
| 3,9 – 4,6 s | Die Systemzeile „Aufgabe „Materialliste prüfen" im Projekt erstellt" erscheint darunter | | 5,7 – 6,1 s | Das Menü schließt, das Feld leert sich |
| 4,6 – 5,0 s | Alles blendet zurück auf den Anfangsstand, damit die Schleife schließt | | 5,8 – 6,7 s | Die Nachricht wächst in den Verlauf, Text und Aufgabenkarte zusammen |
| 6,7 – 7,4 s | Die Systemzeile „Aufgabe „Materialliste prüfen" im Projekt erstellt" erscheint |
| 7,4 – 8,0 s | Halt auf dem Ergebnis |
Vier Entscheidungen dahinter: ### Der erste Anlauf hatte den Ablauf falsch herum
Dort wurde die Nachricht „Danke. Ich habe dazu direkt eine Aufgabe erstellt."
getippt, abgeschickt, und **danach** wuchs an ihr eine Aufgabenkarte. Die
Nachricht meldete also eine Aufgabe, die es noch gar nicht gab, und wurde
anschließend selbst zu einer. Ein Kreis, kein Ablauf.
Nachgesehen in `GeneralTeamChatPage.tsx`: eine Aufgabe entsteht über das Menü
hinter dem Pluszeichen („Aufgabe erstellen", Zeile 275), und das Ergebnis ist
Nachricht `m4` — Text **und** angehängte Aufgabenkarte in einem — gefolgt von
der Systemzeile `m5`. **Ein Kontextmenü an einer bestehenden Nachricht gibt es
dort nicht.** Wer eine Umwandlung „Nachricht → Aufgabe" zeigt, zeigt eine
Funktion, die wir nicht haben, auch wenn die Website den Nutzen so beschreibt
(„Aus „mach das mal" wird mit einem Klick eine echte Aufgabe").
Die Reihenfolge ist deshalb: Notiz tippen, Pluszeichen, „Aufgabe erstellen",
und beides geht zusammen als eine Nachricht raus. Der Satz im Perfekt stimmt
dann auch: wenn die Nachricht ankommt, hängt die Aufgabe daran.
### Vier Entscheidungen
- **Der Verlauf hängt am unteren Rand, nicht am oberen.** Damit erzeugt jedes - **Der Verlauf hängt am unteren Rand, nicht am oberen.** Damit erzeugt jedes
Wachstum das Hochschieben von selbst, ohne eine zweite Rechnung für das Wachstum das Hochschieben von selbst, ohne eine zweite Rechnung für das
Scrollen. Die drei vorherigen Nachrichten sind dieselben wie auf Slide 1 und Scrollen. Die drei vorherigen Nachrichten sind dieselben wie auf Slide 1 und
stammen aus `GeneralTeamChatPage.tsx`, nicht aus einem erfundenen Szenario. stammen aus `GeneralTeamChatPage.tsx`, nicht aus einem erfundenen Szenario.
- **Sie liegt neben den Slides, nicht darin.** Ein erster Anlauf machte sie zur - **Sie liegt neben den Slides, nicht darin.** Ein Zwischenstand machte sie zur
sechsten Slide im Streifen. Damit wanderte sie in die Slide-Auswahl, in die sechsten Slide im Streifen. Damit wanderte sie in die Slide-Auswahl, in die
Zählung „Carousel, 6 Slides" und in den Bildexport, der sie als Standbild Zählung „Carousel, 6 Slides" und in den Bildexport, der sie als Standbild
mitschrieb. Ein Video, das man nur zusammen mit den Bildern herausbekommt, mitschrieb. Ein Video, das man nur zusammen mit den Bildern herausbekommt,
ist im Carousel aber nicht getrennt einsetzbar. ist im Carousel nicht getrennt einsetzbar.
- **Getippt wird ein Satz, der ohnehin im Verlauf steht.** `ANIM_TIPP` ist - **Der Mauszeiger ist erzählerisches Mittel, kein Bestandteil des Werkzeugs.**
`VERLAUF[3].text` — die Nachricht, die im Werkzeug an dieser Stelle folgt. Ohne ihn sieht man ein Menü aufgehen und weiß nicht, warum. Er ist weiß mit
Kein Text ist für die Animation erfunden worden, auch keine Uhrzeit. dunkler Kante, damit er auf dem Verlauf wie auf dem Menü liest, und geht
- **Das letzte Bild ist nicht das erste.** Der Fortschritt läuft über nach dem zweiten Klick zügig weg — ein Zeiger, der ohne Aufgabe über dem
`t = n / anzahl`; bei `n / (anzahl − 1)` wären Anfang und Ende dasselbe Bild Verlauf stehen bleibt, zieht den Blick von der ankommenden Nachricht ab.
und der Stoß der Schleife stünde eine Dreißigstelsekunde still. - **Am Ende wird nichts zurückgenommen.** Der erste Anlauf blendete die neue
Nachricht wieder aus, damit der Schleifenstoß unsichtbar wird. Das sah aus,
als lösche sich eine abgeschickte Nachricht von selbst. Eine Schleife darf
springen, eine Nachricht darf nicht verschwinden.
Nachgeprüft mit `ffprobe`: H.264 High, `yuv420p`, 1080 × 1350, 30 fps, Dauer Kein Text und keine Uhrzeit ist für die Animation erfunden worden. Getippt wird
exakt 5,000000 s, stumme AAC-Spur, `moov` am Dateianfang, 1,1 MB. `VERLAUF[3].text`, also genau die Nachricht, die im Werkzeug an dieser Stelle
steht.
Nachgeprüft mit `ffprobe`: H.264 High, `yuv420p`, 1080 × 1350, 30 fps, 240
Bilder, Dauer exakt 8,000000 s, stumme AAC-Spur, `moov` am Dateianfang, 1,5 MB.
+1 -1
View File
@@ -374,7 +374,7 @@ ist sie ein eigenes Element, und ob sie mitkommt, entscheidest du beim Posten.
```js ```js
const ANIMS = [ const ANIMS = [
{ key: "animChat", type: "feature", dauer: 5, { key: "animChat", type: "feature", dauer: 8,
eyebrow: "Bewegtbild", eyebrow: "Bewegtbild",
head: 'Getippt,<br><span class="a">und schon im Projekt.</span>', head: 'Getippt,<br><span class="a">und schon im Projekt.</span>',
body: "Eine Nachricht, ein Klick, und die Aufgabe hängt am richtigen Projekt.", body: "Eine Nachricht, ein Klick, und die Aufgabe hängt am richtigen Projekt.",
Binary file not shown.
+175 -67
View File
@@ -1464,9 +1464,12 @@ if (location.search.indexOf("nur=anim") >= 0)
ist `dauer` und der zweite Wert t, den die Zeichenfunktion bekommt. */ ist `dauer` und der zweite Wert t, den die Zeichenfunktion bekommt. */
const ANIMS = [ const ANIMS = [
{ {
/* Fuenf Sekunden: unter drei nimmt Instagram ein Video im Carousel /* Acht Sekunden. Fuenf waren zu knapp, sobald der Ablauf stimmt:
nicht an, ueber sechs verliert eine Schleife ihren Reiz. */ tippen, Menue, waehlen, abschicken, bestaetigen -- das sind fuenf
key: "animChat", type: "feature", dauer: 5, Schritte, und jeder braucht seinen Moment. Unter drei Sekunden nimmt
Instagram ein Video im Carousel gar nicht erst an, nach oben sind
sechzig erlaubt. */
key: "animChat", type: "feature", dauer: 8,
eyebrow: "Bewegtbild", eyebrow: "Bewegtbild",
head: 'Getippt,<br><span class="a">und schon im Projekt.</span>', head: 'Getippt,<br><span class="a">und schon im Projekt.</span>',
body: "Eine Nachricht, ein Klick, und die Aufgabe hängt am richtigen Projekt.", body: "Eine Nachricht, ein Klick, und die Aufgabe hängt am richtigen Projekt.",
@@ -3261,29 +3264,103 @@ if (location.search.indexOf("nur=anim") >= 0)
} }
/* ─── Slide 6 · Die Animation ──────────────────────────────────────────── /* ─── Das Bewegtbild ────────────────────────────────────────────────────
Eine Nachricht entsteht: getippt, abgeschickt, und aus ihr wird eine Wie aus einer Nachricht eine echte Aufgabe wird, in dem Weg, den das
Aufgabe im Projekt. Darueber liegen drei Beitraege, die schon da waren -- Werkzeug tatsaechlich hat.
man sieht also einen laufenden Verlauf und nicht ein leeres Fenster.
Alle Texte sind dieselben wie auf Slide 1 und 3, aus INITIAL_MESSAGES. Das ist der Kern, und ein erster Anlauf hatte ihn falsch: dort wurde die
Eine Animation ist kein Anlass, etwas zu erfinden. Nachricht "Danke. Ich habe dazu direkt eine Aufgabe erstellt." getippt,
abgeschickt -- und *dann* wuchs an ihr eine Aufgabenkarte. Die Nachricht
meldete also eine Aufgabe, die es noch gar nicht gab, und wurde
anschliessend selbst zu einer. Ein Kreis, kein Ablauf.
Der Ablauf, in Anteilen der Laufzeit: Nachgesehen in GeneralTeamChatPage.tsx: eine Aufgabe entsteht ueber das
Menue hinter dem Pluszeichen ("Aufgabe erstellen", Zeile 275), und das
Ergebnis ist Nachricht m4 -- Text *und* angehaengte Aufgabenkarte in
einem -- gefolgt von der Systemzeile m5. Ein Kontextmenue an einer
bestehenden Nachricht gibt es dort nicht; wer eine Umwandlung zeigt,
zeigt eine Funktion, die wir nicht haben.
0,00 - 0,06 ruhig, der Verlauf steht, das Feld ist leer Also die richtige Reihenfolge: Notiz tippen, Pluszeichen, "Aufgabe
0,06 - 0,44 die Nachricht wird getippt, Zeichen fuer Zeichen erstellen", und beides geht zusammen als eine Nachricht raus. Der Satz
0,44 - 0,50 abgeschickt: das Feld leert sich im Perfekt stimmt dann auch -- wenn die Nachricht ankommt, haengt die
0,48 - 0,66 die Blase waechst von unten herein, der Verlauf rutscht hoch Aufgabe daran.
0,64 - 0,80 die Aufgabenkarte darin blendet auf
0,78 - 0,92 die Bestaetigung erscheint, der Verlauf rutscht nach
0,92 - 1,00 zurueck auf Anfang
Der letzte Abschnitt ist wichtig: im Carousel laeuft so ein Bild in Der Ablauf, in Anteilen der Laufzeit (8 s):
Schleife, und ein harter Schnitt zurueck faellt auf. Deshalb blenden die
beiden neuen Teile am Ende wieder aus und der Verlauf faehrt zurueck -- 0,00 - 0,05 ruhig, der Verlauf steht, das Feld ist leer
das letzte Bild ist damit dasselbe wie das erste. */ 0,05 - 0,36 die Notiz wird getippt, Zeichen fuer Zeichen
const ANIM_TIPP = VERLAUF[3].text; 0,36 - 0,45 der Zeiger faehrt auf das Pluszeichen und klickt
0,45 - 0,56 das Menue klappt nach oben auf, die Zeilen gestaffelt
0,56 - 0,71 der Zeiger geht auf "Aufgabe erstellen" und klickt
0,71 - 0,76 das Menue schliesst, das Feld leert sich
0,72 - 0,84 die Nachricht mit der Aufgabenkarte waechst in den Verlauf
0,84 - 0,92 die Bestaetigung im Projekt erscheint
0,92 - 1,00 Halt auf dem Ergebnis
Am Ende wird nichts zurueckgenommen. Der Vorgaenger blendete die neue
Nachricht wieder aus, damit der Schleifenstoss unsichtbar wird -- das
sah aus, als loesche sich eine abgeschickte Nachricht von selbst. Eine
Schleife darf springen, eine Nachricht darf nicht verschwinden. */
const ANIM_NOTIZ = VERLAUF[3].text;
/* Der Mauszeiger. Kein Bestandteil des Werkzeugs, sondern das Mittel, mit
dem eine Bedienung erzaehlt wird -- ohne ihn sieht man ein Menue
aufgehen und weiss nicht, warum. Weiss mit dunkler Kante, damit er auf
dem Verlauf wie auf dem Menue liest. */
const zeiger = (x, y, gr) =>
'<g transform="translate(' + n1(x) + ',' + n1(y) + ') scale(' + n1(gr) + ')" ' +
'filter="url(#hgklein)">' +
'<path d="M0,0 L0,17.5 L4.3,13.4 L7,19.4 L10.1,18 L7.4,12.2 L13,12.2 Z" ' +
'fill="#FFFFFF" stroke="#24242B" stroke-width="1.5" stroke-linejoin="round"/></g>';
/* Der Klick: ein Ring, der einmal aufgeht und dabei verschwindet. */
const klickRing = (x, y, p) => p <= 0 || p >= 1 ? "" :
'<circle cx="' + n1(x) + '" cy="' + n1(y) + '" r="' + n1(7 + 17 * p) +
'" fill="none" stroke="#24242B" stroke-width="' + n1(2.4 * (1 - p)) +
'" opacity="' + n1(.55 * (1 - p)) + '"/>';
/* Das Menue hinter dem Pluszeichen, aus GeneralTeamChatPage.tsx Zeile 275.
Dieselben sieben Eintraege und derselbe Trenner wie auf Slide 4, nur
kleiner gesetzt: hier haengt es im Fenster und steht nicht allein auf
der Slide. side="top" align="start" heisst, es klappt nach oben auf und
sitzt links am Knopf. */
function animMenue(ax, ay, auf, hoverNr) {
const MW = 306, ZH = 34, PAD = 9, TRENN = 11;
const hoehe = 2 * PAD + MENUE.reduce((a, e) => a + (e ? ZH : TRENN), 0);
const x = ax, y = ay - hoehe;
let g = "";
let yy = y + PAD, nr = 0;
for (const e of MENUE) {
if (!e) {
g += '<path d="M' + n1(x + PAD + 4) + ',' + n1(yy + TRENN / 2) + 'H' +
n1(x + MW - PAD - 4) + '" stroke="' + CHAT_RAND + '" stroke-width="1.1"/>';
yy += TRENN; continue;
}
/* Die gewaehlte Zeile bekommt den Grund, den sie im Werkzeug beim
Ueberfahren bekommt: hover:bg-surface-neutral-tertiary. */
if (nr === hoverNr)
g += '<rect x="' + n1(x + PAD - 1) + '" y="' + n1(yy) + '" width="' +
n1(MW - 2 * PAD + 2) + '" height="' + ZH + '" rx="7" fill="' + FL_SPALTE + '"/>';
g += feather(e[0], x + PAD + 18, yy + ZH / 2, 16, CHAT_TEXT2, 1.9);
g += uiText(x + PAD + 36, yy + ZH / 2 + 5.5, e[1], 15, 500, CHAT_TEXT);
yy += ZH; nr++;
}
/* Es faehrt aus dem Knopf heraus auf: von unten links skaliert, damit es
am Pluszeichen zu haengen scheint. */
const sk = .94 + .06 * auf;
return { hoehe, html:
'<g opacity="' + n1(Math.min(1, auf * 3.2)) + '" transform="translate(' +
n1(x) + ',' + n1(ay) + ') scale(' + n1(sk) + ') translate(' + n1(-x) + ',' +
n1(-ay) + ')">' +
'<g filter="url(#hgschatten)"><path d="' + rundRechteck(x, y, MW, hoehe, 14) +
'" fill="#FFFFFF" stroke="' + CHAT_RAND + '" stroke-width="1.2"/></g>' +
g + '</g>',
// Mitte der Zeile "Aufgabe erstellen", fuer den Zeiger.
zielY: y + PAD + 5 * ZH + TRENN + ZH / 2, zielX: x + MW / 2 };
}
function animChat(j, t) { function animChat(j, t) {
/* 620 x 740: hoeher waere das Fenster oben leer, weil der Verlauf unten /* 620 x 740: hoeher waere das Fenster oben leer, weil der Verlauf unten
@@ -3292,20 +3369,23 @@ if (location.search.indexOf("nur=anim") >= 0)
const B = 620, H = 740, RAND = 22, KOPF = 74, FELD = 96; const B = 620, H = 740, RAND = 22, KOPF = 74, FELD = 96;
const innenB = B - 2 * RAND; const innenB = B - 2 * RAND;
const bahnO = KOPF + 8, bahnU = H - FELD - 8; const bahnO = KOPF + 8, bahnU = H - FELD - 8;
const dauer = (ANIMS[j] && ANIMS[j].dauer) || 8;
/* Die Abschnitte. stufe() liefert 0 davor, 1 danach und dazwischen den const tippen = stufe(t, .05, .36);
Anteil; daempfen() nimmt der Bewegung die Maschinenhaftigkeit. */ const zeigerAn = stufe(t, .36, .43); // Zeiger faehrt zum Plus
const tippen = stufe(t, .06, .44); const klick1 = stufe(t, .43, .48);
const leeren = stufe(t, .44, .50); const auf = daempfen(stufe(t, .45, .56)); // Menue klappt auf
const wachsen = daempfen(stufe(t, .48, .66)); const wandern = daempfen(stufe(t, .56, .68)); // Zeiger zur Zeile
const karte = daempfen(stufe(t, .64, .80)); const klick2 = stufe(t, .68, .73);
const system = daempfen(stufe(t, .78, .92)); const zu = daempfen(stufe(t, .71, .76)); // Menue schliesst
const zurueck = daempfen(stufe(t, .92, 1)); /* Die Blase setzt an, waehrend sich das Feld leert. Ein Zwischenstand
hatte dazwischen eine Viertelsekunde Leere: die Notiz war weg, die
Nachricht noch nicht da -- das las sich, als sei sie verschluckt
worden. Die beiden Bewegungen greifen jetzt ineinander. */
const wachsen = daempfen(stufe(t, .72, .84)); // Nachricht kommt herein
const system = daempfen(stufe(t, .84, .92));
// Am Ende faehrt alles wieder auf den Anfang. const menueAn = Math.max(0, auf - zu);
const neuAn = Math.max(0, wachsen - zurueck);
const karteAn = Math.max(0, karte - zurueck);
const sysAn = Math.max(0, system - zurueck);
let g = ""; let g = "";
@@ -3324,37 +3404,33 @@ if (location.search.indexOf("nur=anim") >= 0)
const spalte = innenB; const spalte = innenB;
const hAlt = alt.map(m => blaseHoehe(m, spalte)); const hAlt = alt.map(m => blaseHoehe(m, spalte));
// Die neue Blase ohne und mit Karte -- dazwischen wird geblendet. const hNeu = blaseHoehe(neu, spalte) * wachsen;
const mOhne = { autor: neu.autor, kuerzel: neu.kuerzel, mein: true, const hSys = blaseHoehe(sys, spalte) * system;
zeit: neu.zeit, text: neu.text };
const hOhne = blaseHoehe(mOhne, spalte);
const hMit = blaseHoehe(neu, spalte);
const hNeu = (hOhne + (hMit - hOhne) * karteAn) * neuAn;
const hSys = blaseHoehe(sys, spalte) * sysAn;
const LUFT = 16; const LUFT = 16;
const gesamt = hAlt.reduce((a, h) => a + h + LUFT, 0) + const gesamt = hAlt.reduce((a, h) => a + h + LUFT, 0) +
(neuAn > .01 ? hNeu + LUFT : 0) + (sysAn > .01 ? hSys + LUFT : 0); (wachsen > .01 ? hNeu + LUFT : 0) + (system > .01 ? hSys + LUFT : 0);
let y = bahnU - gesamt; let y = bahnU - gesamt;
let bahn = ""; let bahn = "";
alt.forEach((m, i) => { bahn += blase(RAND, y, spalte, m); y += hAlt[i] + LUFT; }); alt.forEach((m, i) => { bahn += blase(RAND, y, spalte, m); y += hAlt[i] + LUFT; });
if (neuAn > .01) { if (wachsen > .01) {
/* Die neue Blase kommt von unten und waechst auf. Sie wird um ihren /* Sie kommt von unten und richtet sich auf: skaliert um den eigenen
unteren Rand skaliert, damit sie sich aus dem Eingabefeld heraus unteren Rand, damit sie aus dem Eingabefeld heraus zu wachsen
aufzurichten scheint und nicht aus der Mitte heraus aufplatzt. */ scheint und nicht aus der Mitte heraus aufplatzt. Text und
const s = .86 + .14 * neuAn; Aufgabenkarte kommen zusammen -- es ist eine Nachricht, und der
const inhalt = karteAn > .01 ? neu : mOhne; Klick auf "Aufgabe erstellen" hat beides erzeugt. */
bahn += '<g opacity="' + n1(Math.min(1, neuAn * 1.6)) + '" transform="translate(0,' + const sk = .88 + .12 * wachsen;
n1((1 - neuAn) * 34) + ') translate(' + n1(B / 2) + ',' + n1(y + hNeu) + bahn += '<g opacity="' + n1(Math.min(1, wachsen * 1.7)) + '" transform="translate(0,' +
') scale(' + n1(s) + ') translate(' + n1(-B / 2) + ',' + n1(-(y + hNeu)) + ')">' + n1((1 - wachsen) * 30) + ') translate(' + n1(B / 2) + ',' + n1(y + hNeu) +
blase(RAND, y, spalte, inhalt) + '</g>'; ') scale(' + n1(sk) + ') translate(' + n1(-B / 2) + ',' + n1(-(y + hNeu)) + ')">' +
blase(RAND, y, spalte, neu) + '</g>';
y += hNeu + LUFT; y += hNeu + LUFT;
} }
if (sysAn > .01) { if (system > .01) {
bahn += '<g opacity="' + n1(sysAn) + '" transform="translate(0,' + bahn += '<g opacity="' + n1(system) + '" transform="translate(0,' +
n1((1 - sysAn) * 12) + ')">' + blase(RAND, y, spalte, sys) + '</g>'; n1((1 - system) * 12) + ')">' + blase(RAND, y, spalte, sys) + '</g>';
} }
// Was ueber die Bahn hinausragt, wird oben abgeschnitten -- wie im Fenster. // Was ueber die Bahn hinausragt, wird oben abgeschnitten -- wie im Fenster.
@@ -3364,23 +3440,25 @@ if (location.search.indexOf("nur=anim") >= 0)
/* Das Eingabefeld. Waehrend des Tippens laeuft der Text mit, dahinter /* Das Eingabefeld. Waehrend des Tippens laeuft der Text mit, dahinter
blinkt der Strich; sobald etwas drinsteht, wird aus dem Mikrofon der blinkt der Strich; sobald etwas drinsteht, wird aus dem Mikrofon der
Sendeknopf -- so macht es Composer auch. */ Sendeknopf -- so macht es der Composer auch. */
const fy = H - FELD; const fy = H - FELD;
const plusX = RAND + 14, plusY = fy + 44;
g += '<path d="M0,' + n1(fy) + 'H' + B + '" stroke="' + CHAT_RAND + '" stroke-width="1.2"/>'; g += '<path d="M0,' + n1(fy) + 'H' + B + '" stroke="' + CHAT_RAND + '" stroke-width="1.2"/>';
g += feather("plus", RAND + 14, fy + 44, 21, CHAT_TEXT2, 2); g += feather("plus", plusX, plusY, 21, menueAn > .3 ? CHAT_TEXT : CHAT_TEXT2, 2);
const feldX = RAND + 36, feldB = B - 2 * RAND - 100; const feldX = RAND + 36, feldB = B - 2 * RAND - 100;
g += '<g filter="url(#hgklein)"><rect x="' + n1(feldX) + '" y="' + n1(fy + 24) + g += '<g filter="url(#hgklein)"><rect x="' + n1(feldX) + '" y="' + n1(fy + 24) +
'" width="' + n1(feldB) + '" height="40" rx="20" fill="' + FL_KARTE + '" stroke="' + '" width="' + n1(feldB) + '" height="40" rx="20" fill="' + FL_KARTE + '" stroke="' +
CHAT_RAND + '" stroke-width="1.2"/></g>'; CHAT_RAND + '" stroke-width="1.2"/></g>';
const zeichen = Math.round(ANIM_TIPP.length * tippen); // Nach dem Klick auf "Aufgabe erstellen" geht die Notiz mit raus.
const getippt = leeren > .5 || zurueck > .5 ? "" : ANIM_TIPP.slice(0, zeichen); const abgeschickt = zu > .45;
const zeichen = Math.round(ANIM_NOTIZ.length * tippen);
const getippt = abgeschickt ? "" : ANIM_NOTIZ.slice(0, zeichen);
if (getippt) { if (getippt) {
const passt = umbrechen(getippt, feldB - 44, 16, 400, 1)[0]; const passt = umbrechen(getippt, feldB - 44, 16, 400, 1)[0];
g += uiText(feldX + 18, fy + 50, passt, 16, 400, CHAT_TEXT); g += uiText(feldX + 18, fy + 50, passt, 16, 400, CHAT_TEXT);
// Der Strich blinkt zweimal je Sekunde, gerechnet auf die Laufzeit. // Der Strich blinkt zweimal je Sekunde, gerechnet auf die Laufzeit.
const blink = Math.floor(t * (SLIDES[j].dauer || 5) * 2) % 2 === 0; if (tippen < 1 && Math.floor(t * dauer * 2) % 2 === 0) {
if (blink && tippen < 1) {
const bx = feldX + 20 + messen(passt, 16, 400); const bx = feldX + 20 + messen(passt, 16, 400);
g += '<rect x="' + n1(bx) + '" y="' + n1(fy + 36) + '" width="2" height="18" ' + g += '<rect x="' + n1(bx) + '" y="' + n1(fy + 36) + '" width="2" height="18" ' +
'fill="' + CHAT_TEXT + '"/>'; 'fill="' + CHAT_TEXT + '"/>';
@@ -3388,13 +3466,43 @@ if (location.search.indexOf("nur=anim") >= 0)
} else { } else {
g += uiText(feldX + 18, fy + 50, "Nachricht schreiben …", 16, 400, "#C9C9CE"); g += uiText(feldX + 18, fy + 50, "Nachricht schreiben …", 16, 400, "#C9C9CE");
} }
const senden = getippt.length > 0; if (getippt) {
if (senden) { const px = B - RAND - 24;
const px = B - RAND - 24, py = fy + 44; g += '<circle cx="' + n1(px) + '" cy="' + n1(plusY) + '" r="19" fill="#24242B"/>';
g += '<circle cx="' + n1(px) + '" cy="' + n1(py) + '" r="19" fill="#24242B"/>'; g += feather("send", px - 1, plusY, 18, "#FFFFFF", 2);
g += feather("send", px - 1, py, 18, "#FFFFFF", 2);
} else { } else {
g += feather("mic", B - RAND - 24, fy + 44, 21, CHAT_TEXT2, 2); g += feather("mic", B - RAND - 24, plusY, 21, CHAT_TEXT2, 2);
}
/* Das Menue liegt ueber allem, auch ueber dem Eingabefeld -- es haengt am
Pluszeichen und klappt nach oben auf. Die Zeile "Aufgabe erstellen"
hebt sich, sobald der Zeiger dort angekommen ist. */
const m = animMenue(RAND + 2, fy - 6, Math.max(auf, .001),
wandern > .55 && menueAn > .2 ? 5 : -1);
if (menueAn > .01) g += m.html;
/* Der Zeiger. Er kommt von rechts unten herein, faehrt auf das
Pluszeichen, spaeter auf die Zeile, und geht nach dem zweiten Klick
wieder hinaus. */
/* Nach dem zweiten Klick geht er zuegig weg. Ein Zeiger, der ohne
Aufgabe ueber dem Verlauf stehen bleibt, zieht den Blick auf sich,
waehrend gerade die Nachricht ankommt. */
const zWeg = stufe(t, .72, .77);
const zAn = zeigerAn * (1 - zWeg);
if (zAn > .01) {
const vonX = B - 70, vonY = H + 40;
const p1 = daempfen(zeigerAn);
let zx = vonX + (plusX + 2 - vonX) * p1;
let zy = vonY + (plusY + 2 - vonY) * p1;
if (wandern > 0) {
const p2 = wandern;
zx += (m.zielX - 40 - zx) * p2;
zy += (m.zielY - 6 - zy) * p2;
}
g += klickRing(zx - 2, zy - 2, klick1 < 1 ? klick1 : 0);
g += klickRing(zx - 2, zy - 2, klick2 < 1 && klick2 > 0 ? klick2 : 0);
g += '<g opacity="' + n1(Math.min(1, zeigerAn * 2) * (1 - zWeg)) + '">' +
zeiger(zx, zy, 1.5) + '</g>';
} }
return stapelKasten(B, H, 22, g); return stapelKasten(B, H, 22, g);