Eine Folie Schritt für Schritt aufdecken#
Eine Folie, die sich vor der Klasse entfaltet, statt fertig dazustehen: Sie ist dann kein Bild, sondern ein Ablauf aus Schritten. Ein Tastendruck zeigt den nächsten Stichpunkt, der übernächste die Formel darunter; erst wenn nichts mehr aussteht, blättert der Druck weiter.
Welches Mittel wofür#
Sechs Bausteine decken fast alles ab; sie lassen sich auf einer Folie mischen.
| Mittel | Wofür |
|---|---|
#pause | Die Folie entfaltet sich von oben nach unten. Der häufigste Fall. |
stagger[…] | Eine Liste Punkt für Punkt – Aufzählungszeichen und Text gemeinsam. Auch für mehrere Blöcke nacheinander. |
anim(…) | Ein bestimmtes Stück auf einem bestimmten Schritt, mit eigener Bewegung – überall dort, wo #pause nicht hinreicht: in Rasterzellen, Tabellen, Kästen. |
alternatives(…) | Mehrere Fassungen derselben Sache an derselben Stelle, jede die vorige ersetzend. |
build(…) | Eine Zeichnung oder ein Diagramm, das in Stufen entsteht – eine CeTZ-Linie, eine lilaq-Datenreihe, eine Beschriftung nach der anderen. |
scene(…) | Eine Zeichnung, die von einem Wert abhängt, und die Werte, an denen der Vortrag hält. Für alles, was sich bewegt, statt dazuzukommen. |
Dazu kommt tiles für ein Kachelraster, das sich von selbst staffelt (Kapitel „Das eigene Aussehen“), und morph für Objekte, die zwischen zwei Folien fliegen (Kapitel „Eine Rechnung entwickeln“).
Der Schrittzeiger#
Jede Folie führt einen Schrittzeiger mit. at ist vorgabemäßig auto: der nächste freie Schritt. Aufeinanderfolgende Einblendungen nummerieren sich damit von selbst; meist steht in einer Folie überhaupt keine Zahl.
#anim[Der Zeiger fängt bei zwei an.] // Schritt 2
#stagger[
- Die Liste zählt weiter, // Schritt 3
- wo die Folie stand, // Schritt 4
- Punkt für Punkt. // Schritt 5
]Automatische Einblendungen beginnen frühestens auf Schritt 2. Das gilt auch für die ersten Stücke von stagger, cue, alternatives, tiles, build und scene: Schritt 1 bleibt dem statischen Inhalt vorbehalten. Wer ein Element schon beim Folienwechsel sehen möchte, setzt ausdrücklich at: 1 oder start: 1.
Der Zeiger beginnt auf jeder Folie neu; Schrittnummern sind folienlokal. Eine ausgeschriebene Zahl setzt ihn neu, und von dort zählt er weiter:
#anim[zuerst] // 2
#anim(at: 4)[spät] // 4
#anim[danach] // 5Die Schreibweisen von at:
| Geschrieben | Bedeutung |
|---|---|
auto | der nächste freie Schritt (Vorgabe) |
3 | ab Schritt drei – dasselbe wie "3-" |
(2, 5) | auf Schritt zwei und auf Schritt fünf |
"2-" | ab Schritt zwei |
"1-2" | auf Schritt eins und zwei, danach nicht mehr |
"2,4" | auf Schritt zwei und auf Schritt vier |
"-2" | von Anfang an bis Schritt zwei |
"3" | genau auf Schritt drei |
Eine bloße Zahl ist ein offenes Ende: Was einmal da ist, bleibt bis zum Ende der Folie. Das ist der Regelfall. Eine geschlossene Angabe wie "1-2" oder "3" lässt das Element wieder verschwinden – dann greift exit. Ein Bereich steht vom ersten Schritt zum letzten da: ein umgedrehter wie "4-2" hält das Übersetzen an, statt im Browser nie und auf Papier trotzdem zu erscheinen.
Eine Folie ohne eine einzige Zahl#
Für eine Folie, die sich nur entfaltet, muss nichts umhüllt werden. #pause schiebt alles Folgende einen Schritt weiter:
== Wie eine Ableitung entsteht
Zwei Punkte auf dem Graphen, dazwischen eine Sekante.
#pause
Rückt der zweite Punkt an den ersten heran, wird aus der Sekante
eine Tangente.
#pause
Ihre Steigung ist die Ableitung an dieser Stelle.Die Läufe zwischen den Pausen werden durchnummeriert: Der erste steht von Beginn an, der zweite kommt auf Schritt zwei, der dritte auf Schritt drei. Danach zählt der Zeiger weiter – ein stagger unter zwei Pausen beginnt bei vier.
#pause wird nur auf der obersten Ebene des Folienrumpfs gelesen, #set- und #show-Regeln eingeschlossen. In einer Rasterzelle, einer Tabelle oder einer Abbildung wird sie nicht gesehen. Dort ist anim das Mittel der Wahl.#set text(size: 20pt)
Erster Lauf.
#pause // gesehen
Zweiter Lauf.
#grid(columns: (1fr, 1fr),
[links #pause danach], // ungesehen
[rechts],
)Eine Liste Punkt für Punkt#
stagger staffelt die Punkte einer Liste über die Schritte. start ist auto und schließt an den Zeiger an.
#stagger[
- Aufzählungszeichen und Text gehören zusammen
- und erscheinen deshalb gemeinsam
- Punkt für Punkt, einer je Schritt
]Nummerierte Listen funktionieren genauso. Ohne Liste im Rumpf wird dieser als ein Stück eingeblendet; mehrere Argumente staffeln beliebige Blöcke:
#stagger(
card(title: [Behauptung])[Die Innenwinkelsumme beträgt $180°$.],
card(title: [Begründung])[Parallele durch einen Eckpunkt, Wechselwinkel.],
card(title: [Beispiel])[$70° + 60° + 50° = 180°$],
)| Argument | Wirkung |
|---|---|
start | erster Schritt; auto schließt an den Zeiger an |
stride | Abstand der Schritte; 2 lässt je einen Schritt aus, 0 setzt alle Punkte auf denselben |
stagger | zusätzliche Verzögerung je Position, in Millisekunden (Vorgabe 60) |
enter | Bewegung der Punkte, wie bei anim |
spacing | Abstand zwischen den Zeilen (Vorgabe 0.65 em) |
dim | true lässt jeden Punkt gedämpft stehenbleiben, sobald der nächste kommt (Vorgabe false) |
duration | Dauer je Eintritt in Millisekunden |
easing | Kurve je Eintritt |
morph | statt einzublenden, wächst jede Zeile aus der vorherigen heraus – der Weg für eine Umformungskette. enter, easing und dim weist er dann zurück |
name | Name der Gruppe, nötig nur für stagger-layer |
dim: true macht aus der Staffelung einen Gang: Der Punkt, über den gerade gesprochen wird, steht da, die vorherigen bleiben lesbar, aber gedämpft.
#stagger(dim: true)[
- Was der Raum schon weiß
- Was er gleich lernt
- Was er danach kann
]Jeder Punkt hält dabei nur seinen eigenen Schritt und ruht danach gedimmt (siehe „Der gedimmte Ruhezustand“). Auch der letzte Punkt dimmt, sobald die Folie nach ihm noch einen Schritt hat.
stride: 0 legt alle Punkte auf einen Schritt; zusammen mit stagger kommen sie dann kurz nacheinander – eine Welle statt einer Folge.
#stagger(stride: 0, stagger: 60)[
- alle drei auf Schritt eins
- der zweite 60 ms später
- der dritte 120 ms später
]Was neben einem Stück steht#
stagger-layer hängt etwas an den Schritt eines bestimmten Stückes – etwa die Umformung am Rand einer Rechnung, die zu der Zeile gehört, aus der die nächste hervorgeht. Dafür braucht die Staffelung einen Namen: name: sagt ihn, ein morph:-Name tut es auch.
#stagger(morph: "umformung",
$ x^2 + 6x + 2 = 0 $,
$ x^2 + 6x = -2 $,
$ (x + 3)^2 = 7 $,
)
#stagger-layer("umformung", 2)[$| -2$]Die Schicht bleibt von ihrem Stück an stehen. Sie trägt keinen Morph-Namen, fliegt also nicht mit, sondern erscheint nur daneben.
Die Staffelung muss im Quelltext vor ihren Schichten stehen; eine Schicht liest nach, welchen Schritt ihr Stück bekommen hat. Und auf derselben Folie: wie bei cue gehört ein Name zu einer Folie, und eine Schicht, die eine Staffelung der Folie davor nennt, bricht die Übersetzung ab.
spacing: gilt nur für den Listenzweig. Stehen die Stücke einzeln da, zählt der gewöhnliche Blockabstand.Aufdecken in der Reihenfolge, in der es genannt wird#
Manche Stichpunkte haben keine Ordnung: Was ein Graph zeigt, was an einem Versuch auffällt, welche Rechenwege es gibt. Die Klasse nennt das in beliebiger Folge. Bei cue decken deshalb die Ziffern 1 bis 9 auf, was gerade genannt wurde.
#cue("ablesen", start: 2)[
- positive und negative Werte
- tiefster und höchster Wert
- Abnahme und Zunahme
]Die Gruppe braucht einen Namen, damit cue-layer sie findet. Sie verbraucht so viele Schritte, wie sie Punkte hat; die Reihenfolge ändert daran nichts. Beim Setzen behält die Liste ihre Leserichtung – ein noch ungenannter Punkt hält seinen Platz frei, damit nichts springt.
Ein Name gehört zu einer Folie. Derselbe Name auf der nächsten Folie ist eine neue Gruppe, die wieder bei der 1 beginnt – man kann also auf jeder Übungsfolie cue("markieren", …) schreiben, ohne die Namen durchzunummerieren. Mehrere cue-Aufrufe auf derselben Folie unter demselben Namen bilden dagegen zusammen eine Gruppe und zählen weiter.
Was mit dem Punkt zugleich erscheint#
cue-layer hängt etwas an denselben Schritt – eine Zeichenschicht, ein Bild, einen Satz daneben:
#cue("ablesen", start: 2)[
- positive und negative Werte
- tiefster und höchster Wert
- Abnahme und Zunahme
]
#cue-layer("ablesen", 1, [dazu das Passende])Punkt und Schicht teilen sich einen Schritt; wer den Schritt verschiebt, bewegt beide. An einen Punkt darf beliebig viel hängen.
Die Gruppe muss im Quelltext vor ihren Schichten stehen; sonst meldet das Paket einen Fehler.
Für eine CeTZ-Zeichnung, die mit den Punkten wächst, ist jede Schicht eine eigene, vollständige Zeichnung: Alles andere bleibt über cetz.draw.hide(rest, bounds: true) unsichtbar, zählt aber für den Ausschnitt. So liegen alle Schichten deckungsgleich übereinander und der Graph steht still.
Eine Schicht trägt dabei nur ihren eigenen Beitrag, kein Gitter und keine Grundkurve – sonst übermalt die zuletzt gesetzte Schicht die erste. Soll die Zeichnung in der geschriebenen Reihenfolge wachsen, nimmt man build.
Der Pfeil nach rechts deckt den nächsten noch ungenannten Punkt auf, in der geschriebenen Reihenfolge; wer eine Ziffer drückt, bekommt diesen Punkt. Beides mischt sich frei. Solange auf der Folie noch gewöhnliche Schritte vor der Gruppe stehen, blättert der Pfeil sie ab; er greift erst in die Gruppe, wenn deren nächster Punkt der nächste Halt ist. Und erst wenn die Gruppe voll ist, führt der Pfeil weiter. Ein Schritt zurück gibt den zuletzt genannten Punkt wieder frei.
In der Sprecheransicht steht jeder noch offene Punkt blass da, mit seiner Ziffer auf dem Aufzählungspunkt; im Saal ist er unsichtbar.
Ein einzelnes Stück auf einem eigenen Schritt#
anim blendet beliebigen Inhalt auf bestimmten Schritten ein – dort, wo #pause nicht hinsieht (Rasterzellen, Tabellen, Kästen), und überall dort, wo ein Stück eine eigene Bewegung bekommen soll.
#grid(
columns: (1fr, 1fr, 1fr),
gutter: 10pt,
anim(card[*von links* \ `enter: "fade-right"`], enter: "fade-right"),
anim(card[*von unten* \ `enter: "rise"`], enter: "rise"),
anim(card[*unscharf* \ `enter: "blur"`], enter: "blur"),
)Im Browser kommen die drei Karten nacheinander auf den Schritten eins bis drei, jede mit ihrer eigenen Bewegung. Auf Papier stehen sie nebeneinander; der Platz ist in beiden Zielen derselbe.
Auftritt und Abgang#
| Wert | Bewegung |
|---|---|
"fade" | nur Deckkraft; Vorgabe für exit |
"fade-up" | aufblendend nach oben; Vorgabe für enter |
"fade-down" | aufblendend nach unten |
"fade-left" | aufblendend nach links, kommt also von rechts |
"fade-right" | aufblendend nach rechts, kommt also von links |
"scale" | wächst heran |
"scale-down" | schrumpft zusammen |
"blur" | schärft sich ein |
"rise" | steigt von unten auf und wächst dabei ein wenig |
"draw" | zeichnet sich selbst – siehe „Ein Pfad, der sich selbst zeichnet“ |
"none" | ohne Bewegung – der Inhalt ist schlicht da |
"hold" | kein Abgang, sondern ein Warten: das Stück bleibt stehen, bis das nächste da ist. Als enter dasselbe wie "none" |
Die Himmelsrichtung im Namen ist die Bewegungsrichtung, nicht die Herkunft: "fade-right" läuft nach rechts und kommt daher von links.
Ein Name, den es nicht gibt, ist ein Fehler beim Übersetzen.
#anim(enter: "fade-upp")[Vertippt.] // Fehler beim Übersetzenenter wirkt in beide Richtungen: Beim Zurückblättern läuft derselbe Effekt rückwärts. exit betrifft nur den echten Abgang – wenn ein Element beim Vorwärtsblättern aus seinem Bereich fällt.
#anim(at: "1-2", exit: "fade-down")[Nur für zwei Schritte da.]
#anim(duration: 900, delay: 200)[Langsam und ein wenig später.]duration ist auto und übernimmt die Vorgabe der Präsentation (520). delay ist 0. Beide sind Millisekunden und gelten für den Auftritt; beim Zurückblättern entfällt die Verzögerung.
Die Kurve#
Alles bewegt sich vorgabemäßig auf derselben Kurve: langsam los, zügig durch, weich aus. easing ändert sie für ein einzelnes Element – ein Ergebnis darf über sein Ziel hinausschießen, ein Stapel Stichpunkte gleichmäßig ankommen.
#anim(ergebnis, enter: "rise", easing: "out-back")
#stagger(stride: 0, stagger: 60, easing: "out-quad")[
- erst dies
- dann das
]easing steht bei anim, stagger, alternatives, build und camera. Es gilt für Auftritt, Abgang und Dimmen des Elements – nicht für den Folienwechsel und nicht für den Flug eines Magic Move.
| Name | Kurve |
|---|---|
"standard" | die Kurve dieses Pakets, ausgeschrieben – dasselbe wie keine Angabe |
"linear" | gleichmäßig, ohne Anlauf und ohne Ausklang |
"ease", "ease-in", "ease-out", "ease-in-out" | die vier, die die Web Animations API von sich aus kennt |
"in-quad", "out-quad", "in-out-quad" | sachte |
"in-cubic", "out-cubic", "in-out-cubic" | deutlicher |
"in-expo", "out-expo", "in-out-expo" | scharf – fast alles geschieht an einem Ende |
"in-back", "out-back", "in-out-back" | holt aus und schwingt über |
in heißt langsam los, out heißt weich aus. Für einen Auftritt ist fast immer out das Richtige: Das Auge sieht dem Ende zu und nicht dem Anfang.
Ein Name, den es nicht gibt, ist ein Fehler beim Übersetzen. Die Meldung zählt auf, was zur Wahl steht.
#anim(ergebnis, easing: "out-bounce") // Fehler beim ÜbersetzenDie drei back-Kurven gehen über ihr Ziel hinaus. Auf einem Weg ist das der Rückschwung; auf der Deckkraft schneidet der Browser ab, was über 1 hinausgeht. easing: "out-back" auf einem schlichten "fade" ist deshalb nur ein schnelleres "fade" – es lohnt sich mit Effekten, die wandern: "rise", "scale", "fade-up".
Federn und Sprünge – elastic, bounce – gibt es nicht.
Der gedimmte Ruhezustand#
Ein Element, dessen Bereich endet, blendet danach mit exit aus und behält den Platz. after: "dimmed" gibt den zweiten Ruhezustand: Der Punkt bleibt stehen und wird gedämpft gezeichnet – lesbar, aber nicht mehr das, worüber gerade gesprochen wird.
#anim(at: "2-3", after: "dimmed")[Eine Randbemerkung.]
#anim(at: 4)[Und weiter im Text.]Das Element sinkt auf 65 Prozent Deckkraft und kommt beim Zurückblättern wieder herauf; bewegt oder umgefärbt wird nichts. after hat zwei Werte: "hidden" (die Vorgabe) und "dimmed".
Zwei Bedingungen prüft das Paket beim Übersetzen. Erstens braucht after einen Bereich, der endet: at: auto und at: 3 laufen bis zum Folienende, at: "3" und at: "2-3" enden. Zweitens braucht die Folie nach dem Bereich noch einen Schritt – sonst gäbe es keinen, auf dem das Element gedämpft zu sehen wäre. Deshalb steht oben die zweite Zeile.
Auf dem Papier ändert after nichts, ebenso wenig wie "hidden": Eine Seite zeigt alle Schritte auf einmal. Die 65 Prozent sind der kleinste Wert, bei dem gedimmter Fließtext den Kontrast 4,5 zu 1 des Kontrastvertrags noch erreicht.
ink über paper oder surface – also für Stichpunkte. Was schon leise ist (muted, Akzentfarbe), wird durch Dimmen zu leise, und über einer eigenen card(fill: ...) oder über einem Bild kann der Kontrast deutlich darunter fallen.Ein verfolgtes Element innerhalb eines gedimmten übernimmt das Dimmen nur, wenn es genau denselben Bereich hat; ein anim mit eigenem Bereich bleibt voll stehen. Früher als sein Wirt erscheint ein inneres Element aber nie.
morph, video, embed und flipbook haben at: "1-" als Vorgabe, einen offenen Bereich, und der passt zu keinem geschlossenen. In einem gedimmten Element bleiben sie voll stehen – eine Formel in einer gedimmten Zeile steht dann schwarz in einem grauen Satz. Wer das nicht will, gibt dem inneren Element von Hand denselben Bereich oder dimmt die Zeile nicht.Mehrere Fassungen an derselben Stelle#
alternatives stellt mehrere Fassungen derselben Sache an denselben Ort, jede die vorige ersetzend – für Zwischenstände, die sich austauschen statt zu bewegen.
#align(center, alternatives(
$ (a + b)^2 $,
$ (a + b)(a + b) $,
$ a^2 + 2 a b + b^2 $,
))Sie stehen in einem Kasten, der so groß ist wie die größte von ihnen, damit ringsherum nichts springt. Jede Fassung nimmt einen Schritt; die letzte bleibt bis zum Folienende. Deckt eine Fassung selbst etwas auf, bleibt sie, bis das getan ist, und die nächste kommt danach. Dieses Warten braucht start: auto: mit ausgeschriebenem start hält jede Fassung außer der letzten genau ihren Schritt, und eine Kette darin kommt erst, wenn ihre Fassung gegangen ist, und steht nie da. Auf Papier steht nur die letzte, im selben Kasten.
| Argument | Wirkung |
|---|---|
start | erster Schritt; auto schließt an den Zeiger an |
align | Ausrichtung im gemeinsamen Kasten (Vorgabe top + left) |
enter | Bewegung beim Wechsel (Vorgabe "fade") |
duration | Dauer des Wechsels in Millisekunden |
easing | Kurve des Wechsels |
inline | hält die Fassungen in der laufenden Zeile |
morph | die Fassungen fliegen ineinander, statt sich abzulösen. Sie stehen an derselben Stelle, der Flug hat also keine Strecke – zu sehen ist, wie die Zeichen sich an Ort und Stelle umordnen. Das ist der Weg für eine Formel, die umgeschrieben wird. Das Warten auf eine Kette gilt dabei nur auf Papier: im Browser kommt die nächste Fassung auf dem Schritt nach der vorigen, auch wenn diese selbst etwas aufdeckt, und das Aufgedeckte steht dort nie da. |
align: center + horizon setzt jede in die Mitte des Kastens, und der Wechsel wird ruhig.Eine Zeichnung, die wächst#
Eine CeTZ-Zeichnung und ein lilaq-Diagramm sind ein Stück, nicht viele. Was darin eine Linie und was eine Datenreihe war, ist von außen nicht zu greifen; ein anim um eine einzelne Linie gibt es deshalb nicht.
build ruft die Zeichnung einmal je Schritt und legt die Fassungen deckungsgleich übereinander: auf Stufe steht sie so, wie sie nach Schritten aussieht. Zu sehen ist immer genau eine.
Welches Stück wann dazukommt, sagt die Frage, die jede Stufe gereicht bekommt. Sie heißt ab, weil sie dasselbe sagt wie at: sonst:
#build(from => box(width: 260pt, height: 58pt, {
place(bottom + left, line(length: 100%))
place(bottom + left, line(angle: -90deg, length: 52pt))
place(bottom + left, dx: 22%, rect(width: 12%, height: 22pt, fill: from(2, accent)))
place(bottom + left, dx: 44%, rect(width: 12%, height: 36pt, fill: from(3, accent)))
place(bottom + left, dx: 66%, rect(width: 12%, height: 50pt, fill: from(4, accent)))
}), steps: 4)from(2, accent) gibt die Farbe zurück, sobald das zweite Stück an der Reihe ist, und sonst dieselbe Farbe mit Alpha 0. Das Achsenkreuz trägt keine Nummer und steht von Anfang an da. steps: 4 sagt, wie viele Stufen es gibt.
Warum Alpha 0 und nicht weglassen#
Weil ein Stück, das fehlt, den Platz mitnimmt, den es hatte: Die Zeichnung wird kleiner, ein Diagramm bekommt eine andere Achsenteilung, und beim nächsten Schritt springt alles. stroke: none hilft nicht – damit fällt die Geometrie ebenfalls weg. Alpha 0 lässt Maß und Pfad vollständig stehen und färbt nur.
ab macht daraus, was zu machen ist:
| Was hineingeht | Was herauskommt |
|---|---|
| eine Farbe | dieselbe Farbe mit Alpha 0 |
| ein Strich | derselbe Strich mit durchsichtigem Pinsel; Dicke, Strichelung und Enden bleiben, denn daran hängt das Maß |
| ein Wörterbuch | dasselbe Wörterbuch, in dem nur die Farben und Striche zu Luft geworden sind |
| Inhalt | hide(…) – gesetzt, aber nicht gezeichnet |
| ein Verlauf | nichts. Ein gradient hat keine Deckkraft, an der sich drehen ließe; das Paket sagt es, statt es zu versuchen |
Was ab nicht umfärben kann, bekommt die Frage mit einem einzigen Argument: from(3) ist wahr, sobald das dritte Stück an der Reihe ist. In CeTZ gehört dorthin hide(…, bounds: true), das ein ganzes Stück verschwinden lässt und sein Maß behält.
Eine CeTZ-Zeichnung#
Mit #import "@preview/cetz:0.5.2" daneben:
#build(from => cetz.canvas({
import cetz.draw: *
line((0,0), (4,0)) // steht von Anfang an
line((4,0), (4,3), stroke: from(2, black)) // ab Schritt 2
line((4,3), (0,0), stroke: from(3, 1.4pt + red))
content((2.2, 1.8), from(4, [$c$]))
if from(4) { circle((4,0), radius: 0.18) }
else { hide(circle((4,0), radius: 0.18), bounds: true) }
}), steps: 4)Ein lilaq-Diagramm#
Mit #import "@preview/lilaq:0.6.0" as lq daneben. Eine Datenreihe wird an zwei Stellen zu Luft: an ihrer Farbe und an ihrer Beschriftung in der Legende. Die zweite ist leicht zu vergessen – der Eintrag steht sonst schon in der Legende, während seine Kurve noch fehlt:
#build(from => lq.diagram(
width: 7cm, height: 4.5cm,
legend: (position: top + left),
lq.plot(x, messung, color: from(1, red), label: from(1, [gemessen])),
lq.plot(x, modell, color: from(2, blue), label: from(2, [Modell])),
), steps: 2)Weil die Reihe als Luft in den Daten stehen bleibt, rechnet lilaq seine Achsen über beide: Die Skala steht fest, und die erste Kurve springt nicht, wenn die zweite dazukommt.
Stufen, die nicht Klick für Klick kommen#
steps: 4 legt die vier Stufen auf vier aufeinanderfolgende Schritte. Das stimmt, solange die Zeichnung Klick für Klick wächst. Sobald zwischen zwei Stufen etwas anderes auf der Folie geschieht – eine Kamerafahrt, ein Urteil, ein zweites Diagramm daneben –, stimmt es nicht mehr: Die Zeichnung hat zwei Bilder, das zweite soll aber erst auf Schritt 9 kommen.
at: nennt je Stufe den Schritt, auf dem sie zuerst steht:
#build(from => cetz.canvas({
import cetz.draw: *
line((0,0), (4,0))
line((4,0), (4,3), stroke: from(2, black))
}), at: (1, 9))Zwei Stufen, die zweite ab Schritt 9; dazwischen steht die erste. Die Länge der Liste ist die Zahl der Stufen, steps und start haben damit nichts mehr zu sagen und werden zurückgewiesen, statt still übergangen zu werden.
from zählt weiter die Stufen und nicht die Schritte. from(2, …) heißt: ab dem zweiten Bild. Wo dieses Bild steht, sagt allein at:. Anders ginge es nicht – bei start: auto weiß beim Schreiben niemand, auf welchem Schritt die Zeichnung landet.
Was das spart, ist keine Tipparbeit, sondern Arbeit. Ohne at: bräuchte dasselbe Bild steps: 9, und die Stufen 1 bis 8 wären pixelgleich und würden trotzdem alle gesetzt. Gemessen an einer Folie mit drei Diagrammen, die nacheinander besprochen werden: zehn Sprites statt 22, und die ganze Datei 2,98 MB statt 3,45 MB.
Die Argumente#
| Argument | Wirkung |
|---|---|
steps | Zahl der Stufen und damit der Schritte (Vorgabe 2) |
start | erster Schritt; auto schließt an den Zeiger an |
at | je Stufe der Schritt, auf dem sie zuerst steht, etwa at: (1, 9); jede Stufe hält bis zur nächsten. Damit entfallen steps und start |
enter | Bewegung beim Erscheinen einer Stufe (Vorgabe "fade"); "draw" ist hier ein Fehler, siehe den nächsten Abschnitt |
duration | Dauer in Millisekunden |
easing | Kurve der Bewegung, siehe „Die Kurve“ |
Auf Papier steht nur die letzte Stufe, im Block derselben Größe. Unter „Bewegung reduzieren“ ändert sich nichts: Die Stufen blenden, sie wandern nicht.
Jede Stufe wird wirklich gesetzt. Vier Stufen heißen vier Layouts und vier SVG-Bäume in der Datei – bei einer aufwendigen Zeichnung wächst beides ebenso schnell wie beim Daumenkino. Eine Zeichnung in zwanzig Stufen ist keine gute Idee.
Deshalb at:, wo die Stufen weit auseinanderliegen. Was zählt, ist die Zahl der Bilder, nicht die Zahl der Schritte dazwischen.
Ein Pfad, der sich selbst zeichnet#
enter: "draw" lässt einen Strich entstehen, statt ihn einzublenden: die Feder setzt an und fährt den Pfad ab, von seinem Anfang bis zu seinem Ende.
#anim(schaltbild, enter: "draw", duration: 900)
#stagger(enter: "draw", stride: 1, achse, kurve, tangente)duration gilt wie überall, aber eine Zeichnung will mehr Zeit als ein Stichpunkt. 900 ist ein brauchbarer Anfang; die Vorgabe der Präsentation (520) ist für drei lange Linien knapp.
Was sich abfahren lässt und was nicht#
Text nicht. Typst setzt Glyphen als gefüllte Umrisse ohne Kontur, und eine Fläche hat keine Länge, an der entlang etwas zu fahren wäre. Dasselbe gilt für alles Gefüllte: eine Pfeilspitze, ein ausgefüllter Punkt, die Fläche einer Karte.
Deshalb ist draw zweierlei zugleich: Die Striche zeichnen sich, alles übrige blendet auf. Die Beschriftung einer Zeichnung kommt also, während die Linien entstehen, und steht mit ihnen zusammen fertig da.
Ein Element, an dem sich gar nichts abfahren lässt, blendet vollständig und meldet das in der Konsole des Browsers.
Alle zugleich, und wie man sie nacheinander bekommt#
Alle gestrichenen Pfade eines Elements fahren zugleich los, in der Malreihenfolge von Typst. Wer eine Reihenfolge will, gibt jedem Stück seinen eigenen Schritt:
#stagger(enter: "draw", stride: 1, achse, kurve, tangente)Wo eine Zeichnung stehen muss#
Nicht auf dem ersten Schritt ihrer Folie. Wer eine Folie betritt, sieht keine Auftritte; die Laufzeit stellt beim Folienwechsel nur den Zustand her. Eine Zeichnung auf Schritt eins stünde also einfach da. Sie braucht einen Schritt vor sich:
#anim[Erst der Satz, der die Zeichnung ankündigt.]
#anim(schaltbild, enter: "draw", duration: 900)Das gilt für jeden Effekt; bei draw fällt es nur besonders auf.
Wer Konturen liefert#
Die Regel ist einfach: Was in Typst einen stroke bekommt, wird zu einem Pfad mit Kontur und lässt sich abfahren; was eine fill bekommt, nicht. Ein Zeichenpaket liefert also genau so viel, wie es strichelt.
Eine CeTZ-Zeichnung aus wenigen langen Linien ist der Fall, für den draw gemacht ist: Ein Auge kann ihnen folgen. Ein lilaq-Diagramm bringt dagegen Gitter, Teilstriche, Rahmen und Marken mit – dutzende Pfade, die alle zugleich loslaufen und eher hereinwischen als entstehen. Für ein Diagramm ist build das bessere Mittel.
Gestrichelte Linien bleiben bei der Blende. Strichelung und Feder brauchen dasselbe SVG-Attribut. Eine gestrichelte Hilfslinie blendet deshalb auf, während ihre durchgezogenen Nachbarn sich zeichnen.
In beide Richtungen, und was an den Rändern gilt#
Beim Zurückblättern fährt die Feder heraus: Was sich gezeichnet hat, zeichnet sich zurück. Ein Sprung – über die Adresse, über die Übersicht, beim Neuladen – stellt dagegen den Endzustand her, die fertige Zeichnung; die Sprecheransicht zeigt in ihrer Vorschau ebenfalls den Ruhezustand. Auf Papier steht die Zeichnung fertig da, enter erreicht die PDF nie.
exit: "draw" ist erlaubt und symmetrisch: Ein Element, das seinen Bereich verlässt, nimmt seine Striche zurück, statt zu verblassen.
Unter „Bewegung reduzieren“#
Die Regel des Pakets lautet: Deckkraft bleibt, Ortsveränderung fällt weg. Bei draw ist das Zeichnen der Weg; ohne ihn bleibt die Blende stehen, und die Zeichnung erscheint wie jedes andere Element.
Wo die Reihenfolge der Striche selbst etwas erklärt, gehört sie zusätzlich in Worte – die liest auch, wer sie nicht laufen sieht.
Zusammen mit einer Zeichnung in Stufen#
Beides zugleich geht nicht, und das Paket sagt es beim Übersetzen statt es zu versuchen:
#build(zeichner, enter: "draw") // Fehler beim ÜbersetzenJede Stufe eines build ist die ganze Zeichnung. Eine Stufe, die sich selbst zeichnete, zöge bei jedem Schritt sämtliche Striche noch einmal nach – über der abtretenden Stufe, die längst dieselbe Tinte trägt. Zu sehen wäre nichts.
Wer eine Zeichnung Strich für Strich entstehen lassen will, gibt die Striche als eigene Stücke hin und lässt jedes sich selbst zeichnen.
Eine Zeichnung, die sich bewegt#
Bei build kommt Stück für Stück etwas hinzu. Bei scene ändert sich eine Größe, und das Bild hängt daran.
Das Deck schreibt eine Funktion von einem Wert auf ein Bild und sagt, an welchen Werten der Vortrag hält. Typst rendert jeden Halt und die Bilder dazwischen. Ein Schritt zieht das Bild von Halt zu Halt.
#scene(
x => zeichnung-bei(x),
stops: (-3, 0, 1.5, 3), // vier Halte, drei Schritte
tween: 8, // Bilder zwischen zwei Halten
)stops sind die Werte selbst, nicht 0.0 bis 1.0: Wer die Tangente an der Stelle , im Scheitel und bei zeigen will, schreibt diese drei Zahlen hin.
Die Szene verbraucht stops.len() - 1 Schritte. Der erste Halt steht da, sobald die Szene erscheint; jeder weitere kostet einen Tastendruck.
Was zu einem Halt gehört#
scene-layer legt einen Satz, eine Formel oder eine zweite Zeichnung auf den Schritt eines bestimmten Halts. Dafür bekommt die Szene einen Namen.
#scene("ableitung", x => tangente-an(f, x), stops: (-3, 0, 1.5, 3))
#scene-layer("ableitung", 2)[Im Scheitel ist die Steigung null.]
#scene-layer("ableitung", 4, enter: "scale")[$f'(x) = 1/2 x$]Wer einen Halt verschiebt, verschiebt alles mit, was daran hängt. Die Szene muss im Quelltext vor ihren Schichten stehen; sonst meldet das Paket einen Fehler.
Mehrere Größen zugleich#
Ein Halt darf ein Tupel sein. Der Zeichner bekommt dann ebenso viele Argumente:
#scene(
(a, b) => rechteck-mit(breite: a, hoehe: b),
stops: ((1, 1), (1, 3), (2, 3)),
tween: 6,
)Erst wächst die Höhe, dann die Breite. Was nicht geht: zwei Größen, die sich unabhängig voneinander bewegen. Alles reist gemeinsam von Halt zu Halt.
Die Argumente#
| Argument | Wirkung |
|---|---|
stops | Die Werte, an denen der Vortrag hält. Mindestens zwei. Eine Zahl, eine Länge, ein Winkel, ein Anteil – oder ein Tupel davon. |
tween | Bilder zwischen zwei Halten (Vorgabe 8). Mit 0 springt die Szene. |
start | Erster Schritt; auto nimmt den laufenden. |
width, height | Der Kasten, in dem die Szene steht (Vorgabe 100% und 190pt). |
duration | Wie lange ein Zug von Halt zu Halt dauert, in Millisekunden. |
enter | Bewegung, mit der die Szene selbst auftritt (Vorgabe "fade"). |
still | Was auf Papier steht, wenn nicht der letzte Halt. |
steady | Nachmessung der Bilder: auto meldet, false nimmt die Szene aus der Prüfung, true besteht darauf. |
duration ist die Dauer des Wegs, nicht die der Blende, mit der die Szene auftritt.
Die Bilder einer Szene sind Zeichnungen zu verschiedenen Werten und dürfen verschieden groß ausfallen. Deshalb steht eine Szene in einem Kasten fester Größe, und jedes Bild wird darauf beschnitten. Wer width und height zu klein wählt, sieht es sofort.
Der Kasten steht still, die Tinte darin nicht von selbst. Eine CeTZ-Leinwand wächst mit ihrem Inhalt. Reicht die Tangente bei weiter nach links als bei , sitzt das Achsenkreuz an einer anderen Stelle im Kasten – beim Blättern wandert dann das ganze Bild, obwohl sich nur ein Punkt bewegen sollte.
Geradebiegen kann das Paket das nicht, bemerken schon: Jede Szene misst ihre Bilder nach und meldet abweichende Maße als Fehler beim Übersetzen.
Der Ausweg liegt in der Zeichnung: ihr eine feste Ausdehnung geben und das, was sich bewegt, darin halten. In CeTZ ist das ein rect mit durchsichtigem Strich, dieselbe Luft, mit der ab arbeitet:
#scene(x => cetz.canvas({
import cetz.draw: *
// Hält die Leinwand auf, egal wo der Punkt steht.
rect((-4.4, -0.8), (4.4, 4.6), stroke: rgb(0, 0, 0, 0))
line((-4, 0), (4, 0))
circle((x, 0.25 * x * x), radius: 0.1)
}), stops: (-3, 0, 3), height: 160pt)Damit steht die Breite fest. Was trotzdem hinausreicht – eine Tangente etwa –, muss gekappt werden, sonst zieht sie die Leinwand doch wieder auf.
Wenn die Bilder verschieden groß sein sollen, sagt man das: steady:. Ein Rechteck, das wächst, eine Zahl, die hochzählt – dort ist der Unterschied die Sache selbst, und die Szene wird gar nicht erst gemessen. Was mit den Befunden geschieht, entscheidet
falsedrift an der Präsentation.
Wer sich festlegen will, schreibt steady: true. Dann bricht die Szene an Ort und Stelle ab, statt am Ende des Decks in einer Liste zu stehen:
#scene(x => cetz.canvas({
import cetz.draw: *
line((0, 0), (x, 0.25 * x * x)) // zieht die Leinwand mit
}), stops: (-3, 3), steady: true) // Fehler beim ÜbersetzenAuf Papier steht der letzte Halt; still setzt etwas anderes an seine Stelle. Der Schrittzeiger läuft dort trotzdem, damit info().step.total in beiden Ausgaben dieselbe Zahl nennt.
Unter „Bewegung reduzieren“ fallen die Zwischenbilder weg: Die Szene springt von Halt zu Halt. Siehe „Weniger Bewegung“.
Was eine Szene kostet#
Jedes Bild ist ein echtes Typst-Layout und liegt als eigener SVG-Baum in der Datei. Übersetzungszeit und Dateigröße wachsen also mit tween. Gepackt fällt die Größe kaum ins Gewicht, weil die Bäume einander sehr ähnlich sind; auf Papier kostet eine Szene gar nichts, dort steht ein einziges Standbild.
Das Nachmessen kostet ein weiteres Layout je Bild, nur im Browserzweig. steady: false gibt es einer einzelnen Szene zurück, drift: "none" allen. Es ist vorgabemäßig an, weil sein Befund beim Schreiben unsichtbar ist: Jedes Bild für sich sieht richtig aus, erst das Blättern zeigt die wandernde Zeichnung.
In ein Detail hineinfahren#
Manchmal ist der nächste Schritt derselbe Satz aus der Nähe: das eine Feld der Tabelle, der eine Term der Gleichung, das eine Bauteil im Schaltbild. camera fährt darauf zu und wieder weg. Sie zielt auf ein pin, und auf sonst nichts.
#pin(<messwerk>, card(title: [Messwerk])[Thermoelement, Brücke, Verstärker.])
#camera(<messwerk>)
#anim[Und wieder heraus, im Schritt danach.]Das Deck nennt damit einen Namen, keine Koordinate – wer die Folie umräumt, muss nichts nachrechnen.
Wie man wieder herauskommt#
at ist ein Schrittbereich wie überall sonst: Die Folie wird genau so lange durch die Kamera gesehen, wie er gilt.
| Geschrieben | Was passiert |
|---|---|
at: auto | Der nächste freie Schritt, und der danach führt wieder heraus. Die Vorgabe. |
at: "3" | Hinein auf Schritt drei, heraus auf vier. |
at: "3-5" | Der Ausschnitt hält über drei Schritte. |
at: 3 | Hinein auf Schritt drei und bleiben; der Folienwechsel führt heraus. |
Der Rückweg ist ein Schritt und wird als einer gezählt: Eine Folie mit nichts als einem Pin und einer Fahrt darauf hat drei Schritte – ganze Folie, Ausschnitt, ganze Folie.
at: auto ist hier ein geschlossener Bereich, bei anim dagegen ein offener: Ein Auftritt hat kein natürliches Ende, eine Kamerafahrt schon. Und nie auf Schritt eins – das ist die Folie, wie man sie betritt.Was mitfährt und was stehenbleibt#
Gefahren wird die Folie: ihr Hintergrund und die Ebene der eingeblendeten Teile darüber. Die Folienzier fährt nicht mit – Fußzeile, Seitenzahl, Fortschritt und laufender Kopf stehen still und bleiben lesbar. Der Titel der Folie fährt dagegen mit; er steht im Rumpf, und ebenso eine Fußzeile, die man selbst in den Rumpf baut.
Was aus dem Bild fährt, wird an der Kante der Bühne abgeschnitten. Auch die Tinte bleibt stehen, wo sie gezogen wurde: Was jemand auf die Folie malt, gehört nicht zur Folie.
Wie weit sie fährt#
margin sagt, wie viel von der Folie um das Detail herum stehenbleibt, gemessen an der unverfahrenen Folie (Vorgabe 16 pt). Die Kamera passt Detail plus Rand ins Bild; die engere der beiden Richtungen entscheidet, damit das Ganze zu sehen ist und nicht seine Mitte. Die Fahrt dauert duration Millisekunden, vorgegeben 700.
#pin(<term>, $b^2$)
#camera(<term>, margin: 4pt, duration: 900, easing: "out-quad")
#anim[Danach.]Eine Grenze nach oben gibt es nicht. Ein Pin von der Größe eines Kommas wird wandgroß gezeigt und bleibt scharf, weil Typst ihn als Vektor setzt. Ein Video, ein Bild oder eine Einbettung wird es nicht.
Ein Detail, das schon so groß ist wie die Folie, gibt nichts zu fahren.
Zwei Sonderfälle#
Zwei Pins desselben Namens auf einer Folie. Die Kamera rahmt beide, also den Kasten um sie herum.
Zwei Fahrten, die sich auf einem Schritt überlappen. Die spätere im Quelltext gewinnt.
Beim Springen, beim Zurückblättern, auf Papier#
Der Ausschnitt ist eine Funktion des Schritts und nichts sonst:
- Zurückblättern fährt den Weg rückwärts und landet sauber wieder auf der ganzen Folie.
- Ein Sprung – über die Übersicht, über
#3in der Adresse, über einen Klick in der Sprecheransicht – stellt den Ausschnitt, statt ihn zu fahren. - Die Sprecheransicht zeigt die laufende Folie samt Fahrt, und die Vorschau daneben trägt den Ausschnitt mit.
- Unter Bewegung reduzieren springt die Kamera auf den Ausschnitt, statt dorthin zu fahren.
Auf Papier gibt es keine Kamera. Das Handout setzt jede Folie ganz, und die Druckansicht des Browsers ebenso.
Daraus folgt eine Pflicht für das Deck: Die Folie muss ohne die Fahrt vollständig und lesbar sein. Wer das Detail nur im Ausschnitt beschriftet – eine 6-Punkt-Zeile, die man ja gleich heranholt –, hat auf Papier eine Zeile, die niemand liest. Die Kamera ist eine Betonung, kein Layout.
Wenn der Name nicht steht#
Eine Kamera, die auf ein pin zielt, das es auf ihrer Folie nicht gibt, ist ein Fehler beim Übersetzen und kein stummes Stehenbleiben:
#pin(<messwerk>, card[…])
#camera(<messerk>) // ein Buchstabe zu wenigEine Fahrt darf vor ihrem Ziel stehen – oft gehört sie an den Kopf der Folie –, deshalb wird erst am Ende des Dokuments geprüft. Ein Pin auf der Folie davor zählt nicht.
Eines bleibt ungeprüft: Ein Pin, der in einem noch nicht aufgedeckten anim steckt, hat ein Rechteck, aber nichts Sichtbares darin. Die Kamera fährt dann auf eine leere Stelle. Welcher Schritt was zeigt, entscheidet sich erst im Browser.
Drei Stolpersteine#
Nur Einblendungen zählen. Der Zeiger zählt anim, stagger, alternatives und #pause. Ein Applet, ein Video oder ein morph verbraucht keinen Schritt; solche Elemente sind von Anfang an da. In einer zweispaltigen Folie beginnen die Stichpunkte neben einem Applet deshalb bei eins:
#side-by-side(
embed(url: "…", width: 100%, height: 220pt), // kein Schritt
stagger[
- erster Stichpunkt // Schritt 1
- zweiter Stichpunkt // Schritt 2
],
)Das gilt, solange at beim Vorgabewert bleibt. Wer einem Video, einer Einbettung, einem Daumenkino oder einem morph ein at hinter Schritt eins gibt, lässt es erscheinen, und dann zählt es wie ein anim: die Folie hat mindestens so viele Schritte, wie das at nennt, und was mit auto folgt, kommt danach.
#video("versuch.mp4", at: 2) // Schritt 2
#anim[Was wir sehen] // Schritt 3Ein Schritt vererbt sich nicht nach innen. Jedes verfolgte Element trägt seinen eigenen Schritt. Steckt eines im anderen, gilt für das innere trotzdem dessen eigene Angabe:
#anim(at: 3)[Ab Schritt drei -- #morph(<m>, $x^2$) aber ab Schritt eins.]Bei morph ist das richtig so: Das Ziel eines Fluges muss beim Betreten der Folie schon stehen. Bei einem anim in einem anim ist es dagegen meist ein Versehen – und es fällt erst beim Blättern auf.
Ein Morph steht ab dem ersten Schritt. Weil beim Zurückblättern die Rollen tauschen, gilt das für beide Enden einer Kette. Daraus folgt: Ein Morph gehört nicht in etwas hinein, das erst später erscheint. Steht er in einer Kachel, die im zweiten Schritt kommt, schwebt er schon im ersten Schritt allein an dieser Stelle:
== In drei Schritten
#statement[#morph(<satz>, $a^2 + b^2 = c^2$)] // steht ab Schritt eins
#tiles(card[…], card[…], card[…]) // erscheinen nacheinanderFür das erste Glied einer Kette gibt es eine Ausnahme: Dort kommt kein Flug an, die Vorfolie trägt ja keinen Morph dieses Namens. Es nimmt deshalb ein at: und darf mit seiner Kachel erscheinen:
== In drei Schritten
#tiles(
card[Benennen …],
card[#morph(<satz>, $a^2 + b^2 = c^2$, at: 2)], // mit der zweiten Kachel
card[Wurzel ziehen …],
)Trägt die Vorfolie doch einen gleichnamigen Morph, ginge der Flug verloren – die Formel erschiene einfach, statt zu fliegen. Das Paket prüft das beim Übersetzen.
Ein verschachteltes Element erbt sein Einblenden. Erscheinen ein äußeres und ein inneres verfolgtes Element im selben Schritt, übernimmt das innere enter, duration und delay vom äußeren – sonst liefen die beiden nicht im Gleichschritt. Wer etwas Eigenes angibt, behält es; wer einen anderen Schritt wählt, erbt nichts.
Ein fr-Abstand gehört nicht ins verfolgte Element. fr ist ein Anteil an dem, was übrig bleibt, und das verteilt der Elternteil unter den Geschwistern; ein verfolgtes Element wird aber allein gemessen. Ein #v(1fr) unmittelbar in einem anim wird deshalb durchgereicht; steht es zwischen anderem Inhalt, meldet das Paket einen Fehler:
#anim[Links #v(1fr) Rechts] // Fehler -- das fr gehört nach draußen
#anim[Links] #v(1fr) #anim[Rechts] // so ist es gemeintEin fr innerhalb eines Rasters ist davon nicht betroffen: anim(grid(rows: (1fr, 1fr), …)) verteilt das Raster unter sich selbst.