Eine Rechnung entwickeln#
Bei einer Kette von Zwischenschritten ist die entscheidende Frage, welches Zeichen wohin gewandert ist. Genau das leistet morph: dasselbe Objekt zweimal, und es fliegt von seinem alten Platz an seinen neuen.
Ein Name, zwei Folien#
Mehr als ein gemeinsamer Name ist nicht nötig. Das Ding erscheint dann nicht woanders neu, sondern fliegt hinüber und nimmt die neue Größe und Gestalt an:
== Der Satz des Pythagoras
#align(center, morph(<pythagoras>, $a^2 + b^2$))
==
#place(center + horizon,
morph(<pythagoras>, text(size: 2.6em)[$a^2 + b^2 = c^2$]))Der Name ist eine Zeichenkette oder eine Marke: morph("pythagoras", …) und morph(<pythagoras>, …) bedeuten dasselbe. Ein Morph ohne at verbraucht keinen Schritt und steht von Anfang an auf seiner Folie. Auf Papier bleibt nur sein Inhalt.
Und auf einer Folie#
Der Flug findet zwischen zwei Schritten statt – auch zwischen zwei Schritten derselben Folie. Zwei Aufrufe desselben Namens mit Bereichen, die sich nicht überschneiden:
== Quadratische Ergänzung
#statement[#morph(<sq>, $ x^2 + 6 x $, at: "1")]
#statement[#morph(<sq>, $ (x + 3)^2 - 9 $, at: "2-")]Den zweiten Schritt gibt der Folie der zweite Aufruf: mit einem at hinter Schritt eins zählt ein Morph wie ein anim. Und der Name muss auf der Folie davor frei sein, sonst ginge der Flug zwischen den Folien verloren; das Paket sagt es beim Übersetzen. Eine Kette zählt dabei als eine: steht ein Morph des Namens ab Schritt eins, landet der Flug über den Folienrand dort, und die späteren folgen auf ihren eigenen Schritten.
Zwei Abkürzungen für den häufigsten Fall#
alternatives(morph: true) lässt seine Fassungen ineinander fliegen, statt sie zu ersetzen. Sie stehen alle an derselben Stelle: was man sieht, ist die Umformung an Ort und Stelle.
#alternatives(morph: true,
$ (a + b)^2 $,
$ (a + b)(a + b) $,
$ a^2 + 2 a b + b^2 $,
)stagger(morph: true) ist der nützlichere Fall: die Kette, bei der jede Zeile stehen bleibt. Die neue Zeile wächst aus der Zeile darüber, und die darüber bleibt stehen. Zurückgeblättert geht derselbe Weg rückwärts: Die letzte Zeile fliegt in die zurück, aus der sie hervorgegangen ist, statt auszublenden:
#stagger(morph: true, spacing: 14pt,
$ x^2 + 6 x + 2 = 0 $,
$ (x + 3)^2 - 7 = 0 $,
$ x = -3 plus.minus sqrt(7) $,
)Beide nehmen statt true auch einen Namen. Nötig ist er nur, wenn der Flug über den Folienrand hinaus weitergehen soll.
enter: und easing: weisen beide deshalb zurück, statt sie stillschweigend fallenzulassen; stagger zusätzlich dim:, das alternatives gar nicht kennt. Gelesen wird duration:, und das ist die Dauer des Fluges.Eine Umformungskette#
Eine Rechnung über mehrere Folien folgt einem einfachen Muster: eine Folie je Zwischenschritt, auf jeder derselbe Morph-Name, und darunter ein Satz, der sagt, was gerade geschehen ist. Ein kleiner Helfer nimmt den immer gleichen Aufbau auf:
#let umform-dauer = 1500
#let umformung(gleichung, erklaerung) = {
block(width: 100%, align(center, text(size: 1.9em, gleichung)))
v(18pt)
anim(block(width: 100%, align(center, text(size: 0.92em, erklaerung))),
at: 2, enter: "fade-up")
}Damit besteht jede Folie der Kette aus zwei Zeilen:
== Die Regel am Term -- Schritt 1 von 3
#umformung(
morph(<term>, $f(x) = 3 x^4$, duration: umform-dauer),
[Ein Faktor $3$ und ein Exponent $4$ -- die Potenzregel behandelt
die beiden verschieden.])
== Die Regel am Term -- Schritt 2 von 3
#umformung(
morph(<term>, $f'(x) = 4 dot 3 x^(4 - 1)$, duration: umform-dauer),
[Der Exponent tritt vorn als Faktor auf -- und bleibt zugleich oben
stehen, wo nun eins abgezogen wird.])
== Die Regel am Term -- Schritt 3 von 3
#umformung(
morph(<term>, $f'(x) = 12 x^3$, duration: umform-dauer),
[Erst jetzt vorn ausgerechnet: $4 dot 3 = 12$.])Zweierlei ist Absicht. Die Dauer von 1500 ms ist länger als eine gewöhnliche Einblendung: man soll dem einzelnen Zeichen folgen können. Und die Erklärung steht auf Schritt 2 – erst fliegt der Term, dann kommt der Satz dazu.
Auf dem Papier setzt jede Folie ihre eigene Fassung – die Kette wird zur Rechnung, Zeile für Zeile:
// drei aufeinanderfolgende Folien, wie sie im Foliensatz stehen
#morph(<term>, $f(x) = 3 x^4$)
#morph(<term>, $f'(x) = 4 dot 3 x^(4 - 1)$)
#morph(<term>, $f'(x) = 12 x^3$)Über die Höhenlage entscheidet die Quellreihenfolge, im Stillstand wie im Flug: Was nach dem morph notiert ist, liegt darüber, was davor steht, darunter.
Wenn die Zeichen falsch fliegen: pin#
Die Paarung sucht zu jedem Zeichen der alten Folie das passende der neuen – zuerst nach der Form, und wo das nicht reicht, nach Nähe. Das versagt, sobald zwei gleiche Zeichen die Plätze tauschen sollen: In kann die Form nicht wissen, welche die aus war.
pin gibt einem Stück innerhalb des Morphs einen eigenen Namen. Gleiche Namen finden zueinander, bevor die Form befragt wird.
== Die Regel am Term -- Schritt 1 von 2
#morph(<term>, $f(x) = #pin(<faktor>)[3] x^#pin(<hoch>)[4]$)
== Die Regel am Term -- Schritt 2 von 2
#morph(<term>, $f'(x) = #pin(<hoch>)[4] dot #pin(<faktor>)[3] x^3$)#morph(<term>, $f(x) = #pin(<faktor>)[3] x^#pin(<hoch>)[4]$)
#morph(<term>, $f'(x) = #pin(<hoch>)[4] dot #pin(<faktor>)[3] x^3$)Ein Pin ohne Gegenstück fällt geräuschlos in den Formabgleich zurück und kostet nichts, wo er nicht gebraucht wird. Nötig ist er, wenn ein Zeichen beim Blättern stehen bleibt oder sichtbar an die falsche Stelle fliegt.
Wo der Magic Move aufhört#
Drei Grenzen sind zu kennen.
Es fliegt nur von einem Schritt zum nächsten – innerhalb einer Folie oder zur unmittelbar nächsten oder vorigen. Sprünge (o, Pos 1, Ende, die Adresszeile) setzen das Ziel ohne Bewegung. Ein Name auf Folie 3 und derselbe auf Folie 7 tun nichts.
Zwei gleiche Namen auf der Zielfolie teilen sich dieselbe Quelle. Beide starten am selben Ort, das Zeichen spaltet sich sichtbar. Auf der Quellfolie zählt bei gleichem Namen dagegen nur der letzte.
Nicht alles ist Schrift. Wie gepaart wird, sagt match:
match | Bedeutung |
|---|---|
"auto" | Zeichenweise, sofern beide Seiten Zeichen enthalten und keine von beiden mehr als 120; sonst als ein Block. Die Vorgabe. |
"glyph" | Immer zeichenweise, auch bei vielen Zeichen. |
"block" | Immer als ein Rechteck: Das ganze Objekt wandert und wird dabei verzerrt. Das Richtige für Bilder, Zeichnungen und alles, was keine Schrift ist. |
Die Grenze von 120 Zeichen ist eine Frage des Aussehens und der Kosten: Jedes Zeichen kostet zwei Geister, und eine sehr lange Formel, Zeichen für Zeichen umgelegt, wirkt als Schwarm und nicht als Bewegung. Gemessen in Chrome auf einer Bühne von 1600 px: 51 Zeichen fliegen ohne ein ausgefallenes Bild, bei 121 Zeichen fällt eines aus, bei 261 stockt der Flug um 117 ms. Ein Deck mit langen Formeln setzt die Grenze selbst:
#show: presentation.with(morph: (glyph-limit: 400))Was einen Namen trägt, reist immer. Ein pin ist die ausdrückliche Ansage, dass zwei Stücke zusammengehören, und die gilt auch über der Grenze: Dort fliegen die benannten Stücke, und der Rest wechselt an Ort und Stelle.
Ein pin darf dabei mehrere Zeichen fassen – #pin(<s>, $sum_(i=1)^n$) –, und sie reisen gemeinsam: Jedes Zeichen der Gruppe findet sein Gegenstück in der Gruppe drüben, Summenzeichen zu Summenzeichen und Grenze zu Grenze. Solange die Gruppe drüben gleich angeordnet ist, ziehen sie als ein Stück; steht sie anders, geht jedes Zeichen an seinen eigenen neuen Platz.
duration gibt die Dauer des Fluges in Millisekunden an (Vorgabe 900). Der Wert der Zielfolie gilt, sonst der der Quelle, sonst duration der Präsentation.
Wie die Folie selbst wechselt#
transition bestimmt, wie eine Folie hereinkommt. Die Präsentation setzt die Vorgabe für alle, eine einzelne Folie darf davon abweichen:
#show: presentation.with(transition: "slide", transition-duration: 420)
== Diese eine anders
#transition("cover", from: "bottom")
// oder, in der Argumentform:
#slide([Diese eine anders], transition: (kind: "cover", from: "bottom"))[…]| Art | Angaben | Was geschieht |
|---|---|---|
"none" | – | Harter Schnitt. |
"fade" | – | Überblendung, nichts bewegt sich. |
"slide" | from | Die neue Folie rückt ein kurzes Stück heran und blendet dabei auf, die alte weicht in die Gegenrichtung. |
"push" | from | Die neue schiebt die alte über den Rand hinaus. |
"cover" | from | Die neue legt sich über die alte, die liegen bleibt. |
"uncover" | from | Die alte zieht fort und gibt die neue frei. |
"zoom" | direction | "in" lässt die neue nach vorn wachsen, "out" die alte nach hinten treten. |
"blur" | – | Unscharf hinüber. |
"iris" | direction | Eine runde Blende: "open" öffnet die neue Folie auf, "close" schließt die alte über ihr zu. |
"wipe" | direction, from | Dasselbe als gerade Kante; from nennt die Kante, an der sie beginnt. |
"flip" | axis | Umschlagen im Raum, wie ein gewendetes Blatt. |
"cube" | axis | Wie flip, aber als zwei Seiten eines Würfels, der sich weiterdreht. |
from ist "right" (Vorgabe), "left", "top" oder "bottom". direction ist "in"/"out" bei "zoom" und "open"/"close" bei "iris" und "wipe", jeweils der erste Wert als Vorgabe. axis ist "y" (Vorgabe, Drehung um die Senkrechte) oder "x". transition-duration gilt für alle Übergänge (Vorgabe 420 ms).
Drei Dinge sind daran nicht selbstverständlich.
Der Übergang gehört der Grenze zwischen zwei Folien, nicht der Blätterrichtung. Maßgeblich ist die Angabe der späteren der beiden Folien.
Rückwärts läuft er als echte Umkehrung. Was hinausgeschoben wurde, kommt von derselben Seite zurück; was sich zugezogen hat, öffnet sich wieder.
Trifft ein Morph auf die Folie, überblendet sie. Fliegt zwischen zwei Folien etwas, weicht der eingestellte Übergang einer schlichten Überblendung. Für eine Umformungskette heißt das: nichts eigens abschalten.
#transition("iirs") und presentation(transition: "iirs") brechen ab und zählen die gültigen Namen auf. slide(transition: (kind: "iirs")) nicht – dort geht das Wörterbuch ungeprüft durch, und der Tippfehler fällt erst auf, wenn nichts geschieht.Mitten in der Bewegung#
Eine Aufdeckung dauert eine halbe Sekunde, ein Flug fast eine, ein Folienwechsel eine knappe halbe. Wer zügig vorführt, drückt in dieser Zeit schon die nächste Taste, und das darf nichts kaputt machen.
Weiterblättern unterbricht, ohne zu springen. Die neue Bewegung beginnt dort, wo das Bild gerade steht, und nicht bei dem Wert, mit dem die alte angefangen hat. Sie bekommt dafür auch nur die Zeit, die der Rest des Weges noch wert ist – wer bei vier Fünfteln unterbricht, sieht das letzte Fünftel und nicht die volle Dauer noch einmal.
Zurückblättern kehrt um. Eine laufende Aufdeckung, ein laufender Flug und ein laufender Folienwechsel laufen rückwärts weiter statt neu zu beginnen: Der Geist fährt seine Bahn zurück, die Folie schiebt sich dorthin zurück, woher sie kam. Die Zeit, die schon verstrichen ist, ist zugleich die Zeit, die der Rückweg noch braucht.
prefers-reduced-motion: reduce gibt es nichts zu unterbrechen: Dort wechselt das Bild ohne Bewegung.Wenn der Inhalt über die Folie hinausgeht#
Eine Folie ist ein Ausschnitt. Gesetzt wird der Rumpf auf ein Blatt, und gewöhnlich sind beide gleich groß: Was auf die Folie passt, steht darauf.
Reicht der Inhalt weiter, wächst das Blatt mit ihm – ohne dass irgendwo ein Maß dafür stünde. Zwei Dinge lassen es wachsen:
- Ein Fluss, der weiterläuft
- Eine Rechnung, die untereinander weitergeht, eine Liste, die länger ist als die Folie. Das Blatt wird nach unten größer.
- Ein
placeauf der obersten Ebene des Rumpfs - Ein Kasten mit
dx: 780ptsteht rechts neben der Folie. Das Blatt wird zur Seite größer.
Im Vortrag zeigt die Bühne den Ausschnitt, und die Ansicht folgt dem, was gerade aufgedeckt wird: Solange der neue Schritt zu sehen ist, steht das Bild still; sobald er unten aus dem Ausschnitt liefe, schwenkt die Ansicht ihm nach. Der Kopf der Folie – Band oder Titelzeile – schwenkt nicht mit. Er liegt als eigene Schicht über dem Blatt, damit der Titel stehen bleibt, wenn die Rechnung unter ihm durchfährt.
== Eine Rechnung, Schritt für Schritt
#stagger(dim: true)[
$ 3x + 5 = 20 $
][
$ 3x = 15 $
][
$ x = 5 $
][
// … und so weiter, über die Folie hinaus
]Auf Papier gibt es nichts zu schwenken. Dort kommt das ganze Blatt auf die Seite, eingepasst und mittig, und darunter steht eine Zeile, die sagt, was im Vortrag geschieht: „Blatt 1 × 1.29 Folien, im Vortrag geschwenkt„. Für den Handzettel und für pages: "step" gilt dasselbe – eine Folie, die größer ist als ihr Ausschnitt, steht auch dort vollständig in ihrem Rahmen.
Nur ein place auf der obersten Ebene zählt. Eines in einem box, einer Rasterzelle oder in einem anim rechnet seinen Versatz gegen diesen Behälter, und von außen ist das nicht zu unterscheiden. Wer etwas neben die Folie stellen will, schreibt sein place unmittelbar in den Folienrumpf. Sein Anker zählt dabei mit: place(bottom + right, dx: 20pt) steht 20pt hinter der rechten unteren Ecke des Rumpfes, nicht 20pt hinter der linken oberen. Ein place ohne Anker steht an seiner Stelle im Fluss, und die ist von außen nicht zu erfahren; es zählt wie top + left.
Die Überlaufprüfung meldet es weiterhin. Sie fragt, ob der Rumpf über den Ausschnitt hinausgeht, und das tut er hier. Sie steht per Vorgabe auf overflow: "none"; wer sie anschaltet, will genau diese Auskunft. Ist das Schwenken gewollt, bleibt sie aus.