Gelehrte Lektionen aus sechzig Tagen der Wiederbelebung von Zombies mit handgeschriebenem CSS

Avatar of John Rhea
John Rhea am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Vorsicht: Schrecklicher Sinn für Humor voraus. Wir werden über praktische Dinge sprechen, aber die Beispiele beinhalten fast alle Zombies und alberne Witze. Sie wurden gewarnt.

Ich werde auf einzelne Pens verlinken, während ich die Lektionen bespreche, die ich gelernt habe. Wenn Sie jedoch einen Eindruck vom gesamten Projekt bekommen möchten, schauen Sie sich 60 Tage Animation auf Undead Institute an. Ich habe dieses Projekt begonnen, um am 1. August 2020 zu enden, pünktlich zur Veröffentlichung eines Buches, das ich geschrieben habe und das CSS-Animation, Humor und Zombies enthält – denn, offensichtlich, Zombies werden die Welt zerstören, wenn Sie Ihre Webkenntnisse nicht einsetzen und die Apokalypse stoppen. Nichts tut der Horde mehr weh als ein HTML-Element in Bewegung!

Ich hatte mir während des gesamten Projekts ein paar Regeln aufgestellt. 

  1. Ich würde alles CSS von Hand codieren. (Ich bin Masochist.)
  2. Der Benutzer würde alle Animationen starten. (Ich hasse es, auf eine Animation zu stoßen, die schon halb durch ist.) 
  3. Ich würde JavaScript so wenig wie möglich verwenden und niemals für Animationen. (Ich habe JavaScript nur einmal verwendet, und das war, um Audio mit der letzten Animation zu starten. Ich habe nichts gegen JavaScript, es ist nur nicht das, was ich hier tun wollte.)

Lektion 1: Achtzig Tage sind eine lange Zeit.

Ähm, steht im Titel nicht „sechzig“ Tage? Ja, aber mein ursprüngliches Ziel war es, achtzig Tage zu machen, und als Tag eins näher rückte mit weniger als zwanzig Animationen vorbereitet und einer Durchschnittszeit von drei Tagen pro Produktion, geriet ich in Panik und wechselte zu sechzig Tagen. Das gab mir sowohl zwanzig zusätzliche Tage bis zum Anfangstermin als auch zwanzig weniger Stücke zu tun.

Lektion 1A: Sechzig Tage sind immer noch eine lange Zeit.

Das ist eine Menge Animation für eine begrenzte Zeit, Ideen und noch begrenztere künstlerische Fähigkeiten. Und obwohl ich daran dachte, auf dreißig Tage zu reduzieren, bin ich froh, dass ich es nicht getan habe. Sechzig Tage haben mich gedehnt und gezwungen, tiefer in die Funktionsweise von CSS-Animationen – und damit von CSS selbst – einzudringen. Ich bin auch am stolzesten auf viele der späteren Arbeiten, die ich gemacht habe, da meine Fähigkeiten zugenommen haben und ich innovativer sein und härter darüber nachdenken musste, wie ich Dinge interessant machen kann. Sobald Sie alle einfachen Optionen ausgeschöpft haben, beginnen die eigentliche Arbeit und die besten Ergebnisse. (Und ja, es wurden am Ende zweiundsechzig Tage, weil ich am 1. Juni begann und am 1. August eine letzte Animation machen wollte. Ein Start am 3. Juni fühlte sich einfach unangenehm und falsch an.)

Die eigentliche Lektion 1 also: Dehnen Sie sich.

Lektion 2: Interaktive Animationen sind schwierig und noch schwieriger responsiv zu gestalten.

Wenn Sie möchten, dass etwas über den Bildschirm fliegt und sich mit einem anderen Element verbindet oder den Zug eines anderen Elements zu starten scheint, müssen Sie entweder nur Standardeinheiten mit fester Breite oder nur flexible Einheiten verwenden. 

Drei Variablen bestimmen, wann und wo sich ein animiertes Element während einer Animation befindet: Dauer, Geschwindigkeit und Distanz. Die Dauer der Animation wird in der animation-Eigenschaft festgelegt und kann in Bezug auf die Bildschirmgröße nicht geändert werden. Die Timing-Funktion der Animation bestimmt die Geschwindigkeit; die Bildschirmgröße kann auch das nicht ändern. Wenn also die Distanz mit der Bildschirmgröße variiert, wird die Zeitgebung überall außer bei einer bestimmten Bildschirmbreite und -höhe falsch sein. 

Sehen Sie sich Tank! an. Führen Sie die Animation bei breiten und schmalen Bildschirmgrößen aus. Obwohl ich die Zeitsteuerung knapp geschafft habe, werden Sie, wenn Sie die beiden vergleichen, feststellen, dass sich der Panzer an einer anderen Stelle relativ zu den Zombies befindet, wenn die letzten Zombies fallen.

Showing the same brown take, side by side, where the tank on the left is further along than the tank on the right.

Um diese Timing-Probleme zu vermeiden, können Sie feste Einheiten und eine große Zahl wie 2000 oder 5000 Pixel oder mehr verwenden, sodass die Animation für alle außer den größten Monitoren die Breite (oder Höhe) des Bildschirms abdeckt.  

Lektion 3: Wenn Sie eine responsive Animation wünschen, setzen Sie alles in (eine der) Viewport-Einheiten.

Die Verwendung von halben Einheitenproportionen (z. B. Breite und Höhe in Pixeln, aber Position und Bewegung mit Viewport-Einheiten) führt zu unvorhersehbaren Ergebnissen. Verwenden Sie auch nicht sowohl vw als auch vh, sondern eines davon; je nachdem, welche die dominante Ausrichtung sein wird. Das Mischen von vh- und vw-Einheiten lässt Ihre Animation „komisch“ werden, was, glaube ich, der technische Begriff ist. 

Nehmen wir zum Beispiel Superbly Zomborrific. Es mischt Pixel-, vw- und vh-Einheiten. Die Prämisse ist, dass der Super Zombie nach oben fliegt, während die „Kamera“ ihm folgt. Der Super Zombie schlägt gegen einen Vorsprung und fällt, während die Kamera weiterläuft, aber das würden Sie nicht verstehen, wenn Ihr Bildschirm ausreichend hoch wäre.

Two animation frames, side by side where the left shows the flying green zombie hitting a building ceiling and the right shows the zombie leaving the frame after impact.

Das bedeutet auch, dass, wenn etwas von oben hereinmuss – wie ich es in Nobody Here But Us Humans getan habe – müssen Sie die vw-Höhe hoch genug einstellen, um sicherzustellen, dass der Ninja-Zombie bei den meisten Seitenverhältnissen nicht sichtbar ist.

Lektion 3A: Verwenden Sie Pixeleinheiten für Bewegungen innerhalb eines SVG-Elements.

Bei all dem gesagt, sollten Transformationen von Elementen innerhalb eines SVG-Elements keine Viewport-Einheiten verwenden. SVG-Tags sind ihr eigenes proportionales Universum. Das SVG „Pixel“ bleibt innerhalb des SVG-Elements proportional zu allen anderen SVG-Element-Kindern, während Viewport-Einheiten das nicht tun. Transformieren Sie also mit Pixeleinheiten innerhalb eines SVG-Elements, aber verwenden Sie Viewport-Einheiten überall sonst.

Lektion 4: SVGs skalieren zur Laufzeit schrecklich.

Für Animationen, wie in Oops…, habe ich das SVG-Bild des Zombies auf das Fünffache seiner Größe skaliert, aber das macht die Kanten unscharf. [Schüttelt die Faust in Richtung „skalierbarer“ Vektorgrafiken.]

/* Original code resulting in fuzzy edges */
.zombie {
  transform: scale(1);
  width: 15vw;
}

.toggle-checkbox:checked ~ .zombie {
  animation: 5s ease-in-out 0s reverseshrinkydink forwards;
}

@keyframes reverseshrinkydink {
  0% {
    transform: scale(1);
  }
  100% {
    transform: scale(5);
  }
}

Ich habe gelernt, ihre Abmessungen auf die endgültigen Abmessungen zu setzen, die am Ende der Animation gültig sein würden, und dann eine Skalierungstransformation zu verwenden, um sie auf die Größe für den Anfang der Animation zu verkleinern. 

/* Revised code */
.zombie {
  transform: scale(0.2);
  width: 75vw;
}

.toggle-checkbox:checked ~ .zombie {
  animation: 5s ease-in-out 0s reverseshrinkydink forwards;
}

@keyframes reverseshrinkydink {
  0% {
    transform: scale(0.2);
  }
  100% {
    transform: scale(1);
  }
}

Kurz gesagt, der überarbeitete Code geht von einer heruntergeschalteten Version des Bildes bis zur vollen Breite und Höhe. Der Browser rendert immer bei 1, wodurch die Kanten bei einer Skalierung von 1 gestochen scharf und sauber sind. Anstatt also von 1 auf 5 zu skalieren, habe ich von 0,2 auf 1 skaliert.

The same animation frame of a scientist holding a coffee mug standing to the left of a growing zombie where the frame on the left shows the zombie with blurry edges and the frame on the right is clear.

Lektion 5: Die Achse ist keine universelle Wahrheit.

Die Achsen eines Elements bleiben mit dem Element synchron, nicht mit der Seite. Eine 90-Grad-Drehung vor einer translateX ändert die Richtung der translateX von horizontal zu vertikal. In Nobody Here But Us Humans… 2 habe ich die Zombies mit einer 180-Grad-Drehung gespiegelt. Aber positive Y-Werte bewegen die Ninjas nach oben und negative nach unten (das Gegenteil von normal). Achten Sie darauf, wie eine Drehung Transformationen weiter unten beeinflussen kann.

Showing the main character facing us in the foreground with 7 ninja characters hanging upside down from the ceiling against a light pink background.

Lektion 6. Teilen Sie komplexe Animationen in konzentrische Elemente auf, um Anpassungen zu erleichtern.

Beim Erstellen einer komplexen Animation, die sich in mehrere Richtungen bewegt, wird das Hinzufügen von Wrapper-Divs, oder besser gesagt Elternelementen, und das individuelle Animieren jedes einzelnen die widersprüchlichen Transformationen reduzieren und verhindern, dass Sie zu einer weinerlichen Masse werden.

Zum Beispiel hatte ich in Space Cadet drei verschiedene Transformationen. Die erste ist die Auf-und-Ab-Bewegung des Zomb-o-nauts. Die zweite ist eine Bewegung über den Bildschirm. Die dritte ist eine Drehung. Anstatt zu versuchen, alles in einer einzigen Transformation zu machen, habe ich zwei Wrapper-Elemente hinzugefügt und jede Animation auf jedem Element ausgeführt (ich habe auch meine Haare gerettet... zumindest einige davon.) Dies half, die Achsenprobleme zu vermeiden, die in der letzten Lektion besprochen wurden, da ich die Drehung auf dem innersten Element durchgeführt habe, wodurch die Achsen seines Elternelements und Großelternelements erhalten blieben.

Lektion 7: SVG- und CSS-Transformationen sind gleich.

Einige Pfade, Gruppen und andere SVG-Elemente haben bereits Transformationen darauf definiert. Das kann von einem Optimierungsalgorithmus stammen oder einfach daran liegen, wie die Illustrationssoftware den Code generiert. Wenn ein Pfad, eine Gruppe oder ein beliebiges Element in einem SVG bereits eine SVG-Transformation darauf hat, wird durch das Entfernen dieser Transformation das Element zurückgesetzt, oft an eine bizarre Stelle oder Größe im Vergleich zum Rest der Zeichnung. 

Da SVG- und CSS-Transformationen gleich sind, ersetzt jede CSS-Transformation, die Sie durchführen, die SVG-Transformation, was bedeutet, dass Ihre CSS-Transformation von diesem bizarren Ort oder dieser Größe ausgeht und nicht von dem Ort oder der Größe, die im SVG festgelegt ist.

Sie können die Transformation vom SVG-Element in Ihr CSS kopieren und sie als Startposition in CSS festlegen (natürlich zuerst in die CSS-Syntax aktualisieren). Sie können sie dann in Ihrer CSS-Animation modifizieren.

Zum Beispiel hatte in Uhhh, Yeah…, meiner Hommage an Office Space, Undead Lumberghs rechter Oberarm (das Element #arm2) eine Transformation im ursprünglichen SVG-Code.

<path id="arm2" fill="#91c1a3" fill-rule="nonzero" d="M0 171h9v9H0z" transform="translate(0 -343) scale(4 3.55)"/>
A side by side comparison of a zombie dressed in a blue button-up shirt and black suspenders while holding a coffee cup. On the left, the arm holding the coffee mugs the the correct position but the right shows the arm detached from the body.

Diese Transformation nach CSS zu verschieben, wie folgt:

<path id="arm2" fill="#91c1a3" fill-rule="nonzero" d="M0 171h9v9H0z"/>
#arm2 {
  transform: translate(0, -343px) scale(4, 3.55);
}

…konnte ich dann eine Animation erstellen, die nicht versehentlich den Ort und die Skalierung zurücksetzt

.toggle-checkbox:checked ~ .z #arm2 { 
  animation: 6s ease-in-out 0.15s arm2move forwards;
}

@keyframes arm2move {
  0%, 100% {
    transform: translate(0, -343px) scale(4, 3.55);
  }
  40%, 60% {
    transform: translate(0, -403px) scale(4, 3.55);
  }
  50% {
    transform: translate(0, -408px) scale(4, 3.55);
  }
} 

Dieser Prozess ist schwieriger, wenn das Werkzeug, das den SVG-Code generiert, versucht, die Transformation in eine Matrix zu „vereinfachen“. Obwohl Sie die Matrix-Transformation durch Kopieren in das CSS wiederherstellen können, ist dies eine schwierige Aufgabe. Sie sind ein besserer Entwickler als ich – was ohnehin der Fall sein mag –, wenn Sie eine Matrix-Transformation nehmen und sie so skalieren, drehen oder verschieben können, wie Sie es möchten.

Alternativ können Sie die Matrix-Transformation mit Translation, Rotation und Skalierung wiederherstellen, aber wenn der Pfad komplex ist, ist die Wahrscheinlichkeit gering, dass Sie ihn in angemessener Zeit wiederherstellen können, ohne in einer Zwangsjacke zu enden. 

Die letzte und wahrscheinlich einfachste Option ist, das Element in ein <g>-Tag (Gruppe) zu wickeln. Fügen Sie ihm eine Klasse oder ID hinzu, um den CSS-Zugriff zu erleichtern, und transformieren Sie die Gruppe selbst, wodurch die Transformationen getrennt werden, wie in der letzten Lektion besprochen. 

Lektion 8: Behalten Sie Ihre geistige Gesundheit, indem Sie transform-origin verwenden, wenn Sie Teile eines SVG transformieren

Die CSS-Eigenschaft transform-origin verschiebt den Punkt, um den die Transformation stattfindet. Wenn Sie versuchen, einen Arm zu drehen – wie ich es in Clubbin’ It getan habe – sieht Ihre Animation natürlicher aus, wenn Sie den Arm vom Zentrum der Schulter aus drehen, aber der natürliche Ursprung der Transformation dieses Pfades liegt in der oberen linken Ecke. Verwenden Sie transform-origin, um dies für ein flüssigeres, natürlicheres Gefühl zu korrigieren… Sie wissen schon, diesen wirklich natürlichen Pixel-Art-Look…

Four sequential frames of an animation showing a caveman character facing left, holding a large wooden club, and raising it up from the bottom to behind his head.

Das Ändern des Ursprungs kann auch beim Skalieren nützlich sein, wie ich es in Mustachioed Oops getan habe, oder beim Drehen von Mundbewegungen, wie dem Kiefer des Dinosauriers in Super Tasty. Wenn Sie den Ursprung nicht ändern, verwenden die Transformationen einen Ursprungspunkt in der oberen linken Ecke des SVG-Elements. 

Lektion 9: Sprite-Animationen können responsiv sein

Ich habe am Ende viele Sprite-Animationen für dieses Projekt gemacht (d.h. wo man mehrere, inkrementelle Frames verwendet und sie schnell genug wechselt, dass die Charaktere sich zu bewegen scheinen). Ich habe die Bilder in einer breiten Datei erstellt, sie als Hintergrundbild zu einem Element der Größe eines einzelnen Frames hinzugefügt, background-size verwendet, um das Hintergrundbild auf die Breite des Bildes zu setzen, und den Überlauf versteckt. Dann habe ich background-position und die Timing-Funktion step() verwendet, um durch die Bilder zu gehen; zum Beispiel: Post-Apocalyptic Celebrations.

Vor dem Projekt habe ich immer unflexible Bilder verwendet. Ich habe die Dinge ein wenig verkleinert, damit es zumindest etwas responsiven Spielraum gibt, aber ich dachte nicht, dass man es zu einer voll flexiblen Breite machen könnte. Wenn Sie jedoch SVG als Hintergrundbild verwenden, können Sie dann Viewport-Einheiten verwenden, um das Element zusammen mit der sich ändernden Bildschirmgröße zu skalieren. Das einzige Problem ist die Hintergrundposition. Wenn Sie jedoch Viewport-Einheiten dafür verwenden, bleibt es synchron. Sehen Sie sich das in Finally, Alone with my Sandwich… an.

Lektion 9A: Verwenden Sie Viewport-Einheiten, um die Hintergrundgröße eines Bildes beim Erstellen responsiver Sprite-Animationen festzulegen

Wie ich während dieses Projekts gelernt habe, ist die Verwendung eines einzigen Einheitentyps fast immer der richtige Weg. Anfangs habe ich die Hintergrundgröße meiner Sprites mit Prozentangaben festgelegt. Die Mathematik war einfach (100 % * (Anzahl der Schritte + 1)) und sie funktionierte in den meisten Fällen gut. Bei längeren Animationen konnte die exakte Rahmenverfolgung jedoch fehlerhaft sein und Teile des falschen Sprite-Frames angezeigt werden. Das Problem verschlimmert sich, je mehr Frames dem Sprite hinzugefügt werden. 

Ich bin mir nicht sicher, aus welchem genauen Grund dies ein Problem verursacht, aber ich glaube, es liegt an Rundungsfehlern, die sich über die Länge des Sprite-Sheets (die Höhe der Verschiebung nimmt mit der Anzahl der Frames zu) anhäufen. 

Für meine letzte Animation, It Ain’t Over Till the Zombie Sings, öffnete ein Dinosaurier sein Maul, um einen singenden Zombie-Wikinger zu enthüllen (während im Hintergrund Laser feuerten, plus tanzten, Akkordeons spielten und Zombies aus Kanonen feuerten, natürlich). Ja, ich weiß, wie man eine Party schmeißt… eine Nerd-Party.

Der Dinosaurier und der Wikinger waren eine der längsten Sprite-Animationen, die ich für das Projekt gemacht habe. Aber als ich Prozente verwendete, um die Hintergrundgröße festzulegen, stimmte die Verfolgung bei bestimmten Größen in Safari nicht. Am Ende der Animation erschien ein Teil der Nase des Dinosauriers aus einem anderen Frame rechts und ein ähnlicher Teil der Nase fehlte links.

A large green dinosaur behind a crowd of people, all facing and looking forward.
Der Dinosaurier links fehlt ein Teil seiner linken Wange und wächst eine neue neben seiner rechten Wange.

Das war extrem frustrierend zu diagnostizieren, weil es in Chrome gut zu funktionieren schien und ich dachte, ich hätte es in Safari behoben, nur um eine leicht andere Bildschirmgröße zu sehen und wieder den verschobenen Frame zu sehen. Wenn ich jedoch konsistente Einheiten verwendete – d.h. vw für background-size, Rahmenbreite und background-position – funktionierte alles gut. Wieder einmal geht es darum, mit konsistenten Einheiten zu arbeiten!

Lektion 10: Laden Sie Leute in das Projekt ein.

A crowd of 32 pixel-art characters from the previous demos facing the screen.

Obwohl ich während dieses Prozesses tonnenweise Dinge gelernt habe, habe ich die meiste Zeit mit dem Kopf gegen die Wand geschlagen (oft, bis die Wand brach oder mein Kopf brach… ich kann es nicht sagen). Obwohl das eine Möglichkeit ist, es zu tun, werden Sie selbst mit einem harten Kopf immer noch Kopfschmerzen bekommen. Laden Sie andere in Ihr Projekt ein, sei es für Ratschläge, um auf einen offensichtlichen blinden Fleck hinzuweisen, den Sie übersehen haben, Feedback zu geben, beim Projekt zu helfen oder einfach nur, um Sie zu ermutigen, weiterzumachen, wenn der Umfang verrückt und willkürlich groß ist. 

Lassen Sie mich diese Lektion also in die Praxis umsetzen. Was denken Sie? Wie werden Sie die Zombiehorden mit CSS-Animationen stoppen? Welches verrückte und willkürlich große Projekt werden Sie in Angriff nehmen, um sich selbst zu dehnen?