Bewegtbild: die Aufgabe entsteht an der Nachricht, nicht im Plus-Menue

Geprueft im laufenden Team-Chat: an jeder Nachricht haengt ein Menue mit
Reagieren, Antworten, Im Thread antworten, Anpinnen, Aufgabe erstellen,
Bearbeiten, Loeschen. Das ist die Funktion, die die Website meint mit "Aus
'mach das mal' wird mit einem Klick eine echte Aufgabe".

Die Animation zeigt jetzt genau das: Tobias' Nachricht anfassen, der Pfeil
erscheint an ihr, Menue auf, "Aufgabe erstellen", die Aufgabenkarte klappt in
der Nachricht auf, die Systemzeile bestaetigt sie im Projekt.

Tobias' Nachricht und keine andere: die Aufgabenkarte aus der Quelle traegt
"Tobias Wagner · offen" als Zustaendigkeit. An einer fremden Nachricht muesste
der Name erfunden werden. So bleiben alle vier Felder der Karte genau die aus
GeneralTeamChatPage.tsx.

Der Vorgaenger nahm das Menue hinter dem Pluszeichen. Das war sauber belegt --
Datei, Zeile, Grep ueber beide Repos, dazu die Staging-API, die eine Aufgabe
nur mit contact/company/lead/project verknuepft und eine Nachricht als Bezug
gar nicht kennt -- und trotzdem falsch. Ein Pitch-Paket vom Juli belegt, was im
Juli gebaut war, nicht was das Produkt heute kann. Wo eine Aussage der Website
keine Entsprechung im vorliegenden Code hat, ist das ein Grund nachzufragen und
nicht, die Aussage auf den Code zu biegen. Steht so in POST-6.md §6.

Sechs statt acht Sekunden: das Tippen faellt mit dem richtigen Weg weg.
Instagram nimmt je Carousel-Element drei bis sechzig Sekunden an, die Laenge
richtet sich also allein nach dem Ablauf.

Dazu in den Bausteinen: fuenf Symbole fuer das Menue, und eine Blase kann eine
zweite Anhangkarte tragen, die ueber einen Anteil aufklappt -- aufgedeckt per
clipPath, nicht gestaucht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
dejanandClaude Opus 5 committed 2026-08-26 13:42:53 +02:00
1 parent d4feaa175b
commit a8f79c1224
4 files changed
+227 -215

No files matched your search

+60 -46
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.** | 8 s Animation: eine Notiz wird getippt, über das Pluszeichen wird „Aufgabe erstellen" gewählt, und beides geht zusammen als eine Nachricht raus | | `animChat` | Bewegtbild | Getippt, **und schon im Projekt.** | 6 s Animation: eine Nachricht wird angeklickt, im Menü „Aufgabe erstellen" gewählt, und die Aufgabe klappt in der Nachricht auf |
**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. Fünfzehn Fallen aus diesem Bau ## 6. Sechzehn 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
@@ -437,69 +437,83 @@ 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, 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. eine Momentaufnahme für eine Reihenfolge zu halten. Mehr dazu in §7.
**Das Pitch-Paket ist ein Stand, nicht das Produkt.** Der zweite Anlauf für die
Animation nahm das Menü hinter dem Pluszeichen, weil `GeneralTeamChatPage.tsx`
den einzigen dort ausgezeichneten Weg zu einer Aufgabe so zeigt. Belegt war das
sauber — Datei, Zeile, Grep über beide Repos und über die Staging-API, die eine
Aufgabe nur mit `contact | company | lead | project` verknüpft und eine
Nachricht als Bezug gar nicht kennt. Trotzdem falsch: im laufenden Team-Chat
hängt an jeder Nachricht ein Menü mit „Aufgabe erstellen".
Die Lehre ist nicht „schlechter prüfen", sondern **wissen, was eine Quelle
belegt.** Ein Pitch-Paket vom Juli belegt, was im Juli gebaut war. Es belegt
nicht, was das Produkt heute kann. Wo eine Aussage der Website (hier:
„Aus „mach das mal" wird mit einem Klick eine echte Aufgabe") keine Entsprechung
im vorliegenden Code hat, ist das ein Grund nachzufragen und nicht, die Aussage
auf den vorhandenen Code zu biegen.
--- ---
## 7. Das Bewegtbild ## 7. Das Bewegtbild
Acht Sekunden, 240 Bilder, `animation/post6-animChat.mp4`. Gezeichnet wird es Sechs Sekunden, 180 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`.
**Gezeigt wird der Weg, den das laufende Werkzeug hat:** Nachricht anfassen,
Menü öffnet sich, „Aufgabe erstellen", die Aufgabe klappt in der Nachricht auf,
das Projekt bestätigt sie. Das ist die Funktion, die die Website meint mit
„Aus „mach das mal" wird mit einem Klick eine echte Aufgabe."
| Zeit | Was passiert | | Zeit | Was passiert |
|---|---| |---|---|
| 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 | | 0,0 – 0,6 s | Ruhe. Der Verlauf steht: Sandra, Maike, Tobias |
| 2,9 – 3,8 s | Der Zeiger fährt auf das Pluszeichen und klickt | | 0,6 – 1,3 s | Der Zeiger fährt auf Tobias' Nachricht, der Pfeil erscheint an ihr |
| 3,6 – 4,5 s | Das Menü klappt nach oben auf, die Zeilen gestaffelt | | 1,3 – 1,6 s | Klick |
| 4,5 – 5,8 s | Der Zeiger geht auf „Aufgabe erstellen", die Zeile hebt sich, Klick | | 1,6 – 2,4 s | Das Menü klappt nach oben auf: Reagieren, Antworten, Im Thread antworten, Anpinnen, Aufgabe erstellen, Bearbeiten, Löschen |
| 5,7 – 6,1 s | Das Menü schließt, das Feld leert sich | | 2,4 – 3,4 s | Der Zeiger geht auf „Aufgabe erstellen", die Zeile hebt sich, Klick |
| 5,8 – 6,7 s | Die Nachricht wächst in den Verlauf, Text und Aufgabenkarte zusammen | | 3,6 – 4,8 s | Das Menü schließt, die Aufgabenkarte klappt in Tobias' Nachricht auf, der Verlauf rutscht nach |
| 6,7 – 7,4 s | Die Systemzeile „Aufgabe „Materialliste prüfen" im Projekt erstellt" erscheint | | 4,8 – 5,4 s | Die Systemzeile „Aufgabe „Materialliste prüfen" im Projekt erstellt" erscheint |
| 7,4 – 8,0 s | Halt auf dem Ergebnis | | 5,4 – 6,0 s | Halt auf dem Ergebnis |
### Der erste Anlauf hatte den Ablauf falsch herum ### Warum Tobias' Nachricht
Dort wurde die Nachricht „Danke. Ich habe dazu direkt eine Aufgabe erstellt." Nicht irgendeine: die Aufgabenkarte aus der Quelle trägt **„Tobias Wagner ·
getippt, abgeschickt, und **danach** wuchs an ihr eine Aufgabenkarte. Die offen"** als Zuständigkeit. Wer sie an einer fremden Nachricht entstehen lässt,
Nachricht meldete also eine Aufgabe, die es noch gar nicht gab, und wurde muss den Namen erfinden. So bleiben alle vier Felder der Karte
anschließend selbst zu einer. Ein Kreis, kein Ablauf. (`square-check`, „Materialliste prüfen · fällig 31.07.2026", „Tobias Wagner ·
offen", „Offen") genau die aus `GeneralTeamChatPage.tsx`.
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 ### 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. Wenn die Aufgabenkarte aufklappt, rutscht der Verlauf nach.
stammen aus `GeneralTeamChatPage.tsx`, nicht aus einem erfundenen Szenario. - **Die Karte wird aufgedeckt, nicht gestaucht.** Sie liegt in voller Größe da
- **Sie liegt neben den Slides, nicht darin.** Ein Zwischenstand machte sie zur und wird über eine `clipPath` von oben freigegeben. Eine gestauchte Karte
sechsten Slide im Streifen. Damit wanderte sie in die Slide-Auswahl, in die sieht kaputt aus, eine aufgedeckte sieht nach Aufklappen aus.
Zählung „Carousel, 6 Slides" und in den Bildexport, der sie als Standbild
mitschrieb. Ein Video, das man nur zusammen mit den Bildern herausbekommt,
ist im Carousel nicht getrennt einsetzbar.
- **Der Mauszeiger ist erzählerisches Mittel, kein Bestandteil des Werkzeugs.** - **Der Mauszeiger ist erzählerisches Mittel, kein Bestandteil des Werkzeugs.**
Ohne ihn sieht man ein Menü aufgehen und weiß nicht, warum. Er ist weiß mit Ohne ihn öffnet sich ein Menü und man weiß nicht, warum. Weiß mit dunkler
dunkler Kante, damit er auf dem Verlauf wie auf dem Menü liest, und geht Kante, damit er auf Verlauf wie Menü liest, und nach dem zweiten Klick zügig
nach dem zweiten Klick zügig weg — ein Zeiger, der ohne Aufgabe über dem weg — sonst zieht er den Blick von der aufklappenden Aufgabe ab.
Verlauf stehen bleibt, zieht den Blick von der ankommenden Nachricht ab. - **Am Ende wird nichts zurückgenommen.** Ein früherer Stand blendete die neue
- **Am Ende wird nichts zurückgenommen.** Der erste Anlauf blendete die neue
Nachricht wieder aus, damit der Schleifenstoß unsichtbar wird. Das sah aus, Nachricht wieder aus, damit der Schleifenstoß unsichtbar wird. Das sah aus,
als lösche sich eine abgeschickte Nachricht von selbst. Eine Schleife darf als lösche sich eine abgeschickte Nachricht von selbst. Eine Schleife darf
springen, eine Nachricht darf nicht verschwinden. springen.
Kein Text und keine Uhrzeit ist für die Animation erfunden worden. Getippt wird ### Zur Länge
`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 Instagram nimmt je Carousel-Element **3 bis 60 Sekunden** an. Sechs liegt weit
Bilder, Dauer exakt 8,000000 s, stumme AAC-Spur, `moov` am Dateianfang, 1,5 MB. von beiden Grenzen weg; die Länge richtet sich also allein nach dem Ablauf.
Vier Schritte — anfassen, wählen, aufklappen, bestätigen — brauchen etwa so
lange. Eine kurze Schleife wiederholt sich außerdem öfter, während jemand liest.
Eine Zwischenfassung lief acht Sekunden, weil dort zusätzlich eine Nachricht
getippt wurde; das fällt mit dem richtigen Weg weg.
Die Symbole im Menü sind unsere Strichsymbole. Im Werkzeug steht bei
„Reagieren" ein Emoji; ein Emoji zwischen sechs Strichsymbolen bricht die
Bildsprache, und die Slides tragen durchgehend Feather-Symbole.
Nachgeprüft mit `ffprobe`: H.264 High, `yuv420p`, 1080 × 1350, 30 fps, 180
Bilder, Dauer exakt 6,000000 s, stumme AAC-Spur, `moov` am Dateianfang, 1,1 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: 8, { key: "animChat", type: "feature", dauer: 6,
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.
+166 -168
View File
@@ -1464,12 +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 = [
{ {
/* Acht Sekunden. Fuenf waren zu knapp, sobald der Ablauf stimmt: /* Sechs Sekunden. Instagram nimmt im Carousel drei bis sechzig an; das
tippen, Menue, waehlen, abschicken, bestaetigen -- das sind fuenf ist also weit von jeder Grenze weg, und die Laenge richtet sich
Schritte, und jeder braucht seinen Moment. Unter drei Sekunden nimmt allein nach dem Ablauf. Vier Schritte -- anfassen, waehlen,
Instagram ein Video im Carousel gar nicht erst an, nach oben sind aufklappen, bestaetigen -- brauchen etwa so lange, und eine kurze
sechzig erlaubt. */ Schleife wiederholt sich oefter, waehrend jemand liest. */
key: "animChat", type: "feature", dauer: 8, key: "animChat", type: "feature", dauer: 6,
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.",
@@ -2051,6 +2051,12 @@ if (location.search.indexOf("nur=anim") >= 0)
"workflow": '<rect width="8" height="8" x="3" y="3" rx="2"/><path d="M7 11v4a2 2 0 0 0 2 2h4"/><rect width="8" height="8" x="13" y="13" rx="2"/>', "workflow": '<rect width="8" height="8" x="3" y="3" rx="2"/><path d="M7 11v4a2 2 0 0 0 2 2h4"/><rect width="8" height="8" x="13" y="13" rx="2"/>',
"x": '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>', "x": '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
"zap": '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>', "zap": '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
/* Fuer das Menue an der Nachricht, aus dem laufenden Team-Chat. */
"thumbs-up": '<path d="M7 10v12"/><path d="M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2a3.13 3.13 0 0 1 3 3.88Z"/>',
"corner-up-left": '<polyline points="9 14 4 9 9 4"/><path d="M20 20v-7a4 4 0 0 0-4-4H4"/>',
"pencil": '<path d="M21.2 6.8a1 1 0 0 0-4-4L3.8 16.2a2 2 0 0 0-.5.8l-1.3 4.4a.5.5 0 0 0 .6.6l4.4-1.3a2 2 0 0 0 .8-.5z"/><path d="m15 5 4 4"/>',
"trash-2": '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M10 11v6"/><path d="M14 11v6"/>',
"chevron-down": '<path d="m6 9 6 6 6-6"/>',
"rocket": '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>' "rocket": '<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/><path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>'
}; };
@@ -2703,12 +2709,19 @@ if (location.search.indexOf("nur=anim") >= 0)
const innen = (max || BLASE_MAX) - 32; const innen = (max || BLASE_MAX) - 32;
const zeilen = m.text ? umbrechen(m.text, innen, 17, 400) : []; const zeilen = m.text ? umbrechen(m.text, innen, 17, 400) : [];
const innenB = (max || BLASE_MAX) - 32; const innenB = (max || BLASE_MAX) - 32;
/* Eine Blase kann eine zweite Anhangkarte tragen: die Aufgabe, die aus
ihr entstanden ist. `a2` ist ihr Anteil zwischen 0 und 1 -- damit
waechst die Blase mit, waehrend die Karte aufklappt, und der ganze
Verlauf rutscht von selbst nach. */
const f2 = m.anhang2 ? (m.a2 == null ? 1 : m.a2) : 0;
let h = 14 + (m.mein ? 0 : 22) + zeilen.length * 25 + let h = 14 + (m.mein ? 0 : 22) + zeilen.length * 25 +
(m.anhang ? anhangHoehe(m.anhang, innenB) + 8 : 0) + 24; (m.anhang ? anhangHoehe(m.anhang, innenB) + 8 : 0) +
(f2 > 0 ? (anhangHoehe(m.anhang2, innenB) + 8) * f2 : 0) + 24;
let b = 32 + Math.max( let b = 32 + Math.max(
...zeilen.map(z => messen(z, 17, 400)), ...zeilen.map(z => messen(z, 17, 400)),
m.mein ? 0 : messen(m.autor, 14, 800), m.mein ? 0 : messen(m.autor, 14, 800),
m.anhang ? anhangNoetig(m.anhang) : 0, 90); m.anhang ? anhangNoetig(m.anhang) : 0,
m.anhang2 ? anhangNoetig(m.anhang2) : 0, 90);
return { zeilen, hoehe: Math.round(h), breite: Math.round(Math.min(b, max || BLASE_MAX)) }; return { zeilen, hoehe: Math.round(h), breite: Math.round(Math.min(b, max || BLASE_MAX)) };
} }
@@ -2717,6 +2730,7 @@ if (location.search.indexOf("nur=anim") >= 0)
als die Rechnung ein zweites Mal zu fuehren und sie auseinanderlaufen zu als die Rechnung ein zweites Mal zu fuehren und sie auseinanderlaufen zu
lassen. */ lassen. */
let letzterAnhang = null; let letzterAnhang = null;
let klappNr = 0;
function blase(x, y, b, m) { function blase(x, y, b, m) {
// Die Systemmeldung ist keine Blase, sondern eine Zeile in der Mitte. // Die Systemmeldung ist keine Blase, sondern eine Zeile in der Mitte.
@@ -2762,6 +2776,21 @@ if (location.search.indexOf("nur=anim") >= 0)
m.anhang[2], m.anhang[3]); m.anhang[2], m.anhang[3]);
cy += ah + 8; cy += ah + 8;
} }
/* Die zweite Karte klappt auf: sie wird von oben freigegeben, nicht
gestaucht -- eine gestauchte Karte sieht kaputt aus, eine
aufgedeckte sieht nach Aufklappen aus. */
const f2 = m.anhang2 ? (m.a2 == null ? 1 : m.a2) : 0;
if (f2 > .004) {
const ah = anhangHoehe(m.anhang2, ma.breite - 32);
const sicht = ah * f2;
const id = "anhklapp" + (++klappNr);
g += '<clipPath id="' + id + '"><rect x="' + n1(bx + 14) + '" y="' + n1(cy + 4) +
'" width="' + n1(ma.breite - 28) + '" height="' + n1(sicht) + '"/></clipPath>' +
'<g clip-path="url(#' + id + ')" opacity="' + n1(Math.min(1, f2 * 2.2)) + '">' +
anhangKarte(bx + 16, cy + 4, ma.breite - 32, m.anhang2[0], m.anhang2[1],
m.anhang2[2], m.anhang2[3]) + '</g>';
cy += sicht + 8 * f2;
}
g += uiText(bx + ma.breite - 16, cy + 16, m.zeit + (m.mein ? " ✓✓" : ""), g += uiText(bx + ma.breite - 16, cy + 16, m.zeit + (m.mein ? " ✓✓" : ""),
13, 500, CHAT_TEXT3, { anker: "end" }); 13, 500, CHAT_TEXT3, { anker: "end" });
return g; return g;
@@ -3265,127 +3294,120 @@ if (location.search.indexOf("nur=anim") >= 0)
/* ─── Das Bewegtbild ──────────────────────────────────────────────────── /* ─── Das Bewegtbild ────────────────────────────────────────────────────
Wie aus einer Nachricht eine echte Aufgabe wird, in dem Weg, den das Wie aus einer Nachricht eine echte Aufgabe wird -- der Weg, den das
Werkzeug tatsaechlich hat. laufende Werkzeug hat: Nachricht anklicken, Menue oeffnet sich,
"Aufgabe erstellen".
Das ist der Kern, und ein erster Anlauf hatte ihn falsch: dort wurde die Zwei Anlaeufe lagen davor daneben, beide aus derselben Ursache. Der
Nachricht "Danke. Ich habe dazu direkt eine Aufgabe erstellt." getippt, erste liess die Nachricht "Danke. Ich habe dazu direkt eine Aufgabe
abgeschickt -- und *dann* wuchs an ihr eine Aufgabenkarte. Die Nachricht erstellt." tippen und danach eine Aufgabenkarte an ihr wachsen -- sie
meldete also eine Aufgabe, die es noch gar nicht gab, und wurde meldete also eine Aufgabe, die es noch nicht gab, und wurde dann selbst
anschliessend selbst zu einer. Ein Kreis, kein Ablauf. zu einer. Der zweite nahm das Menue hinter dem Pluszeichen, weil das im
Pitch-Paket der einzige Weg ist, der dort ausgezeichnet ist.
Nachgesehen in GeneralTeamChatPage.tsx: eine Aufgabe entsteht ueber das Beides war zu wenig geprueft. Das Pitch-Paket ist ein Stand vom Juli;
Menue hinter dem Pluszeichen ("Aufgabe erstellen", Zeile 275), und das im laufenden Team-Chat haengt an jeder Nachricht ein Menue mit
Ergebnis ist Nachricht m4 -- Text *und* angehaengte Aufgabenkarte in Reagieren, Antworten, Im Thread antworten, Anpinnen, Aufgabe erstellen,
einem -- gefolgt von der Systemzeile m5. Ein Kontextmenue an einer Bearbeiten, Loeschen. Das ist die Funktion, die die Website meint, wenn
bestehenden Nachricht gibt es dort nicht; wer eine Umwandlung zeigt, sie schreibt: "Aus 'mach das mal' wird mit einem Klick eine echte
zeigt eine Funktion, die wir nicht haben. Aufgabe."
Also die richtige Reihenfolge: Notiz tippen, Pluszeichen, "Aufgabe Gewaehlt wird Tobias' Nachricht, nicht irgendeine: die Aufgabenkarte aus
erstellen", und beides geht zusammen als eine Nachricht raus. Der Satz der Quelle traegt "Tobias Wagner · offen" als Zustaendigkeit. Wer sie an
im Perfekt stimmt dann auch -- wenn die Nachricht ankommt, haengt die einer fremden Nachricht entstehen laesst, muss den Namen erfinden.
Aufgabe daran.
Der Ablauf, in Anteilen der Laufzeit (8 s): Der Ablauf, in Anteilen der Laufzeit (6 s):
0,00 - 0,05 ruhig, der Verlauf steht, das Feld ist leer 0,00 - 0,10 ruhig, der Verlauf steht
0,05 - 0,36 die Notiz wird getippt, Zeichen fuer Zeichen 0,10 - 0,22 der Zeiger faehrt auf die Nachricht, der Pfeil erscheint
0,36 - 0,45 der Zeiger faehrt auf das Pluszeichen und klickt 0,22 - 0,27 Klick
0,45 - 0,56 das Menue klappt nach oben auf, die Zeilen gestaffelt 0,26 - 0,40 das Menue klappt auf, die Zeilen gestaffelt
0,56 - 0,71 der Zeiger geht auf "Aufgabe erstellen" und klickt 0,40 - 0,56 der Zeiger geht auf "Aufgabe erstellen", die Zeile hebt sich
0,71 - 0,76 das Menue schliesst, das Feld leert sich 0,56 - 0,61 Klick
0,72 - 0,84 die Nachricht mit der Aufgabenkarte waechst in den Verlauf 0,60 - 0,66 das Menue schliesst, der Zeiger geht
0,84 - 0,92 die Bestaetigung im Projekt erscheint 0,62 - 0,80 die Aufgabenkarte klappt in der Nachricht auf
0,92 - 1,00 Halt auf dem Ergebnis 0,80 - 0,90 die Bestaetigung im Projekt erscheint
0,90 - 1,00 Halt auf dem Ergebnis
Am Ende wird nichts zurueckgenommen. Der Vorgaenger blendete die neue Am Ende wird nichts zurueckgenommen. Ein frueherer Stand blendete die
Nachricht wieder aus, damit der Schleifenstoss unsichtbar wird -- das Aufgabe wieder aus, damit der Schleifenstoss unsichtbar wird -- das sah
sah aus, als loesche sich eine abgeschickte Nachricht von selbst. Eine aus, als loesche sie sich von selbst. Eine Schleife darf springen. */
Schleife darf springen, eine Nachricht darf nicht verschwinden. */
const ANIM_NOTIZ = VERLAUF[3].text; /* Das Menue an der Nachricht, aus dem laufenden Team-Chat. Reihenfolge und
Beschriftung wie dort; die Symbole sind unsere, weil dort ein Emoji
steht und wir durchgehend Strichsymbole benutzen. */
const NACHRICHT_MENUE = [
["thumbs-up", "Reagieren"],
["corner-up-left", "Antworten"],
["message-square", "Im Thread antworten"],
["pin", "Anpinnen"],
["square-check", "Aufgabe erstellen"],
["pencil", "Bearbeiten"],
["trash-2", "Löschen"],
];
const MENUE_ZIEL = 4; // "Aufgabe erstellen"
/* Die Aufgabe, die daraus entsteht. Alle vier Felder stehen so in
GeneralTeamChatPage.tsx an der Nachricht m4. */
const ANIM_AUFGABE = ["square-check", "Materialliste prüfen · fällig 31.07.2026",
"Tobias Wagner · offen", "Offen"];
/* 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) => const zeiger = (x, y, gr) =>
'<g transform="translate(' + n1(x) + ',' + n1(y) + ') scale(' + n1(gr) + ')" ' + '<g transform="translate(' + n1(x) + ',' + n1(y) + ') scale(' + n1(gr) + ')" ' +
'filter="url(#hgklein)">' + '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" ' + '<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>'; '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 ? "" : const klickRing = (x, y, p) => p <= 0 || p >= 1 ? "" :
'<circle cx="' + n1(x) + '" cy="' + n1(y) + '" r="' + n1(7 + 17 * p) + '<circle cx="' + n1(x) + '" cy="' + n1(y) + '" r="' + n1(7 + 17 * p) +
'" fill="none" stroke="#24242B" stroke-width="' + n1(2.4 * (1 - p)) + '" fill="none" stroke="#24242B" stroke-width="' + n1(2.4 * (1 - p)) +
'" opacity="' + n1(.55 * (1 - p)) + '"/>'; '" opacity="' + n1(.55 * (1 - p)) + '"/>';
/* Das Menue hinter dem Pluszeichen, aus GeneralTeamChatPage.tsx Zeile 275. /* Es klappt nach oben auf, weil die Nachricht unten im Fenster haengt --
Dieselben sieben Eintraege und derselbe Trenner wie auf Slide 4, nur nach unten waere kein Platz, und ein Menue dreht dort um. */
kleiner gesetzt: hier haengt es im Fenster und steht nicht allein auf function animMenue(rechtsX, untenY, auf, hoverNr) {
der Slide. side="top" align="start" heisst, es klappt nach oben auf und const MW = 262, ZH = 34, PAD = 8;
sitzt links am Knopf. */ const hoehe = 2 * PAD + NACHRICHT_MENUE.length * ZH;
function animMenue(ax, ay, auf, hoverNr) { const x = rechtsX - MW, y = untenY - hoehe;
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 g = "";
let yy = y + PAD, nr = 0; NACHRICHT_MENUE.forEach((e, nr) => {
for (const e of MENUE) { const yy = y + PAD + nr * ZH;
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) if (nr === hoverNr)
g += '<rect x="' + n1(x + PAD - 1) + '" y="' + n1(yy) + '" width="' + g += '<rect x="' + n1(x + PAD - 1) + '" y="' + n1(yy) + '" width="' +
n1(MW - 2 * PAD + 2) + '" height="' + ZH + '" rx="7" fill="' + FL_SPALTE + '"/>'; 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 += 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); 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; const sk = .94 + .06 * auf;
return { hoehe, html: return { hoehe,
'<g opacity="' + n1(Math.min(1, auf * 3.2)) + '" transform="translate(' + zielX: x + PAD + 30,
n1(x) + ',' + n1(ay) + ') scale(' + n1(sk) + ') translate(' + n1(-x) + ',' + zielY: y + PAD + MENUE_ZIEL * ZH + ZH / 2,
n1(-ay) + ')">' + html:
'<g filter="url(#hgschatten)"><path d="' + rundRechteck(x, y, MW, hoehe, 14) + '<g opacity="' + n1(Math.min(1, auf * 4.5)) + '" transform="translate(' +
'" fill="#FFFFFF" stroke="' + CHAT_RAND + '" stroke-width="1.2"/></g>' + n1(rechtsX) + ',' + n1(untenY) + ') scale(' + n1(sk) + ') translate(' +
g + '</g>', n1(-rechtsX) + ',' + n1(-untenY) + ')">' +
// Mitte der Zeile "Aufgabe erstellen", fuer den Zeiger. '<g filter="url(#hgschatten)"><path d="' + rundRechteck(x, y, MW, hoehe, 14) +
zielY: y + PAD + 5 * ZH + TRENN + ZH / 2, zielX: x + MW / 2 }; '" fill="#FFFFFF" stroke="' + CHAT_RAND + '" stroke-width="1.2"/></g>' +
g + '</g>' };
} }
function animChat(j, t) { function animChat(j, t) {
/* 620 x 740: hoeher waere das Fenster oben leer, weil der Verlauf unten
haengt, und die Slide stiesse an den Fliesstext. Was nicht mehr
hineinpasst, schiebt sich oben hinaus -- so macht es ein Chat auch. */
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;
const tippen = stufe(t, .05, .36); const zeigerAn = stufe(t, .10, .22);
const zeigerAn = stufe(t, .36, .43); // Zeiger faehrt zum Plus const pfeil = daempfen(stufe(t, .16, .24)); // der Pfeil an der Blase
const klick1 = stufe(t, .43, .48); const klick1 = stufe(t, .22, .27);
const auf = daempfen(stufe(t, .45, .56)); // Menue klappt auf const auf = daempfen(stufe(t, .26, .40));
const wandern = daempfen(stufe(t, .56, .68)); // Zeiger zur Zeile const wandern = daempfen(stufe(t, .40, .54));
const klick2 = stufe(t, .68, .73); const klick2 = stufe(t, .56, .61);
const zu = daempfen(stufe(t, .71, .76)); // Menue schliesst const zu = daempfen(stufe(t, .60, .66));
/* Die Blase setzt an, waehrend sich das Feld leert. Ein Zwischenstand const klappen = daempfen(stufe(t, .62, .80)); // die Aufgabe klappt auf
hatte dazwischen eine Viertelsekunde Leere: die Notiz war weg, die const system = daempfen(stufe(t, .80, .90));
Nachricht noch nicht da -- das las sich, als sei sie verschluckt const menueAn = Math.max(0, auf - zu);
worden. Die beiden Bewegungen greifen jetzt ineinander. */
const wachsen = daempfen(stufe(t, .72, .84)); // Nachricht kommt herein
const system = daempfen(stufe(t, .84, .92));
const menueAn = Math.max(0, auf - zu);
let g = ""; let g = "";
@@ -3397,110 +3419,86 @@ if (location.search.indexOf("nur=anim") >= 0)
g += uiText(RAND + 52, RAND + 30, "P-2026-0042 · 8 Mitglieder · aktiv", 15, 500, CHAT_TEXT3); g += uiText(RAND + 52, RAND + 30, "P-2026-0042 · 8 Mitglieder · aktiv", 15, 500, CHAT_TEXT3);
g += '<path d="M0,' + KOPF + 'H' + B + '" stroke="' + CHAT_RAND + '" stroke-width="1.2"/>'; g += '<path d="M0,' + KOPF + 'H' + B + '" stroke="' + CHAT_RAND + '" stroke-width="1.2"/>';
/* Der Verlauf haengt unten. Waechst er, schiebt er sich nach oben aus dem /* Der Verlauf haengt unten. Waechst die Blase, schiebt er sich nach oben
Fenster -- so entsteht das Scrollen von selbst, ohne eigenen Zaehler. */ aus dem Fenster -- so entsteht das Scrollen von selbst. */
const alt = [VERLAUF[0], VERLAUF[1], VERLAUF[2]];
const neu = VERLAUF[3], sys = VERLAUF[4];
const spalte = innenB; const spalte = innenB;
const hAlt = alt.map(m => blaseHoehe(m, spalte)); const m1 = VERLAUF[0], m2 = VERLAUF[1];
const hNeu = blaseHoehe(neu, spalte) * wachsen; // Tobias' Nachricht bekommt die Aufgabe, die aus ihr entsteht.
const m3 = Object.assign({}, VERLAUF[2], { anhang2: ANIM_AUFGABE, a2: klappen });
const sys = VERLAUF[4];
const h1 = blaseHoehe(m1, spalte), h2 = blaseHoehe(m2, spalte);
const h3 = blaseHoehe(m3, spalte);
const hSys = blaseHoehe(sys, spalte) * system; const hSys = blaseHoehe(sys, spalte) * system;
const LUFT = 16; const LUFT = 16;
const gesamt = hAlt.reduce((a, h) => a + h + LUFT, 0) + const gesamt = h1 + h2 + h3 + 3 * LUFT + (system > .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; }); bahn += blase(RAND, y, spalte, m1); y += h1 + LUFT;
bahn += blase(RAND, y, spalte, m2); y += h2 + LUFT;
/* Die gewaehlte Nachricht. Ihre Lage wird gemerkt, damit Pfeil, Zeiger
und Menue daran haengen und nicht an einer zweiten Rechnung. */
const dritteY = y;
const ma3 = blaseMasse(m3, blaseBreite(spalte));
const bx3 = RAND + 52, br3 = ma3.breite;
bahn += blase(RAND, y, spalte, m3);
y += h3 + LUFT;
if (wachsen > .01) {
/* Sie kommt von unten und richtet sich auf: skaliert um den eigenen
unteren Rand, damit sie aus dem Eingabefeld heraus zu wachsen
scheint und nicht aus der Mitte heraus aufplatzt. Text und
Aufgabenkarte kommen zusammen -- es ist eine Nachricht, und der
Klick auf "Aufgabe erstellen" hat beides erzeugt. */
const sk = .88 + .12 * wachsen;
bahn += '<g opacity="' + n1(Math.min(1, wachsen * 1.7)) + '" transform="translate(0,' +
n1((1 - wachsen) * 30) + ') translate(' + n1(B / 2) + ',' + n1(y + hNeu) +
') scale(' + n1(sk) + ') translate(' + n1(-B / 2) + ',' + n1(-(y + hNeu)) + ')">' +
blase(RAND, y, spalte, neu) + '</g>';
y += hNeu + LUFT;
}
if (system > .01) { if (system > .01) {
bahn += '<g opacity="' + n1(system) + '" transform="translate(0,' + bahn += '<g opacity="' + n1(system) + '" transform="translate(0,' +
n1((1 - system) * 12) + ')">' + blase(RAND, y, spalte, sys) + '</g>'; n1((1 - system) * 12) + ')">' + blase(RAND, y, spalte, sys) + '</g>';
} }
/* Der Pfeil an der Blase, wie im Werkzeug: er erscheint, sobald die
Nachricht angefasst wird, und sitzt oben rechts in ihr. */
if (pfeil > .01 && zu < .5) {
const px = bx3 + br3 - 18, py = dritteY + 16;
bahn += '<g opacity="' + n1(pfeil * (1 - stufe(t, .60, .66))) + '">' +
'<circle cx="' + n1(px) + '" cy="' + n1(py) + '" r="12" fill="' + FL_SPALTE + '"/>' +
feather("chevron-down", px, py + 1, 14, CHAT_TEXT2, 2.2) + '</g>';
}
// Was ueber die Bahn hinausragt, wird oben abgeschnitten -- wie im Fenster. // Was ueber die Bahn hinausragt, wird oben abgeschnitten -- wie im Fenster.
g += '<clipPath id="animbahn"><rect x="0" y="' + bahnO + '" width="' + B + g += '<clipPath id="animbahn"><rect x="0" y="' + bahnO + '" width="' + B +
'" height="' + (bahnU - bahnO + 6) + '"/></clipPath>'; '" height="' + (bahnU - bahnO + 6) + '"/></clipPath>';
g += '<g clip-path="url(#animbahn)">' + bahn + '</g>'; g += '<g clip-path="url(#animbahn)">' + bahn + '</g>';
/* Das Eingabefeld. Waehrend des Tippens laeuft der Text mit, dahinter // Das Eingabefeld bleibt ruhig: hier wird nichts geschrieben.
blinkt der Strich; sobald etwas drinsteht, wird aus dem Mikrofon der
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", plusX, plusY, 21, menueAn > .3 ? CHAT_TEXT : CHAT_TEXT2, 2); g += feather("plus", RAND + 14, fy + 44, 21, 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>';
g += uiText(feldX + 18, fy + 50, "Nachricht schreiben …", 16, 400, "#C9C9CE");
g += feather("mic", B - RAND - 24, fy + 44, 21, CHAT_TEXT2, 2);
// Nach dem Klick auf "Aufgabe erstellen" geht die Notiz mit raus. /* Das Menue haengt am Pfeil und liegt ueber allem. */
const abgeschickt = zu > .45; const chevX = bx3 + br3 - 18, chevY = dritteY + 16;
const zeichen = Math.round(ANIM_NOTIZ.length * tippen); const m = animMenue(chevX + 14, chevY - 16, Math.max(auf, .001),
const getippt = abgeschickt ? "" : ANIM_NOTIZ.slice(0, zeichen); wandern > .55 && menueAn > .2 ? MENUE_ZIEL : -1);
if (getippt) { /* Unter drei Prozent ist es kein Aufblenden mehr, sondern ein Schemen
const passt = umbrechen(getippt, feldB - 44, 16, 400, 1)[0]; ueber dem Verlauf. Dann lieber gar nichts. */
g += uiText(feldX + 18, fy + 50, passt, 16, 400, CHAT_TEXT); if (menueAn > .03) g += m.html;
// Der Strich blinkt zweimal je Sekunde, gerechnet auf die Laufzeit.
if (tippen < 1 && Math.floor(t * dauer * 2) % 2 === 0) {
const bx = feldX + 20 + messen(passt, 16, 400);
g += '<rect x="' + n1(bx) + '" y="' + n1(fy + 36) + '" width="2" height="18" ' +
'fill="' + CHAT_TEXT + '"/>';
}
} else {
g += uiText(feldX + 18, fy + 50, "Nachricht schreiben …", 16, 400, "#C9C9CE");
}
if (getippt) {
const px = B - RAND - 24;
g += '<circle cx="' + n1(px) + '" cy="' + n1(plusY) + '" r="19" fill="#24242B"/>';
g += feather("send", px - 1, plusY, 18, "#FFFFFF", 2);
} else {
g += feather("mic", B - RAND - 24, plusY, 21, CHAT_TEXT2, 2);
}
/* Das Menue liegt ueber allem, auch ueber dem Eingabefeld -- es haengt am /* Der Zeiger: herein auf den Pfeil, dann auf die Zeile, dann weg. */
Pluszeichen und klappt nach oben auf. Die Zeile "Aufgabe erstellen" const zWeg = stufe(t, .61, .67);
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); const zAn = zeigerAn * (1 - zWeg);
if (zAn > .01) { if (zAn > .01) {
const vonX = B - 70, vonY = H + 40; const vonX = B - 60, vonY = H + 40;
const p1 = daempfen(zeigerAn); const p1 = daempfen(zeigerAn);
let zx = vonX + (plusX + 2 - vonX) * p1; let zx = vonX + (chevX - 2 - vonX) * p1;
let zy = vonY + (plusY + 2 - vonY) * p1; let zy = vonY + (chevY - 2 - vonY) * p1;
if (wandern > 0) { if (wandern > 0) {
const p2 = wandern; zx += (m.zielX - zx) * wandern;
zx += (m.zielX - 40 - zx) * p2; zy += (m.zielY - 4 - zy) * wandern;
zy += (m.zielY - 6 - zy) * p2;
} }
g += klickRing(zx - 2, zy - 2, klick1 < 1 ? klick1 : 0); g += klickRing(zx + 2, zy + 2, klick1 < 1 ? klick1 : 0);
g += klickRing(zx - 2, zy - 2, klick2 < 1 && klick2 > 0 ? klick2 : 0); g += klickRing(zx + 2, zy + 2, klick2 > 0 && klick2 < 1 ? klick2 : 0);
g += '<g opacity="' + n1(Math.min(1, zeigerAn * 2) * (1 - zWeg)) + '">' + g += '<g opacity="' + n1(Math.min(1, zeigerAn * 2) * (1 - zWeg)) + '">' +
zeiger(zx, zy, 1.5) + '</g>'; zeiger(zx, zy, 1.5) + '</g>';
} }