Wie man einen SVG mit border-image animiert

Avatar of Uri Shaked
Uri Shaked am

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

Werfen wir einen Blick darauf, wie man die CSS-Eigenschaft border-image mit animierten SVGs kombiniert, die sich um einen Rand bewegen. Dabei behandeln wir, wie man resizable, im Neuner-Schnitt animierte SVGs von Hand erstellt, die man nicht nur verwenden kann, um den Effekt nachzubilden, sondern um ihn zu seinem eigenen zu machen.

Hier ist, was wir machen

Animated gif of red skulls moving around a list of high scores in a retro arcade font that go from first place to tenth place.
Gruselige Totenköpfe? Retro-Arcade? Was spricht dagegen?!

Das ist eigentlich Teil von The Skull, einem Capture-the-Flag-Rätsel, an dem ich arbeite und das darauf ausgelegt ist, die Interna von Arduino und seinem Mikrocontroller zu erforschen. Ich habe recherchiert, wie man einen Rand wie diesen animiert, konnte aber keine nützlichen Beispiele finden. Die meiste Recherche drehte sich um marching ants, aber leider funktioniert der stroke-dasharray-Trick nicht mit Totenköpfen, geschweige denn mit komplexeren Formen.

Also, im Sinne des Lernens und Teilens, blogge ich hier mit Ihnen darüber!

Sollen wir background oder border-image verwenden?

Zuerst wusste ich nicht einmal, dass border-image existiert. Ich versuchte, ein ::before Pseudoelement in meinem ersten Versuch zu verwenden und animierte seine background-position Eigenschaft. Das brachte mich bis hierher

Wie Sie sehen können, hat es funktioniert, aber um den Rand zu vervollständigen, wären mindestens acht verschiedene Elemente (oder Pseudoelemente) erforderlich. Es ist nicht ideal, das HTML so zu überladen.

Ich habe eine Frage in der israelischen Facebook-Gruppe für CSS-Entwickler gepostet, und alle wiesen mich auf die border-image-Eigenschaft hin. Sie tut genau das, was sie verspricht: Sie verwendet ein Bild (oder einen CSS-Gradienten) für den Rand eines Elements.

Um mit border-image zu arbeiten, müssen Sie ein Bild bereitstellen, das im Neuner-Schnitt verwendet wird (denken Sie an ein Tic-Tac-Toe-Feld über dem Bild). Jede dieser neun Regionen repräsentiert einen anderen Teil des Randes: oben, rechts, links und unten, jede der vier Ecken und dann die Mitte (die ignoriert wird).

Wenn wir zum Beispiel nur statische Totenköpfe wollten, könnten wir SVG-Muster nutzen, um den Totenkopf neunmal zu wiederholen. Zuerst definieren wir ein 24×24 Muster mit dem Pfad des Totenkopfs und verwenden dann dieses Muster als fill für ein 72×72 rect.

<svg version="1.1" height="72" width="72" xmlns="http://www.w3.org/2000/svg">
 <defs>
  <pattern id="skull-fill" width="24" height="24" 
patternUnits="userSpaceOnUse">
    <path d="..." fill="red"/>
  </pattern>
 </defs>
 <rect fill="url(#skull-fill)" width="72" height="72" />
</svg>

Als nächstes definieren wir einen Rand und setzen das border-image auf das Ziel-Element.

.skulls {
  border: 24px solid transparent;
  border-image: url("https://skullctf.com/images/skull-9.svg") 24 round;
}

Und wir erhalten einen Rand aus Totenköpfen.

SVG-Animationen hinzufügen

Jetzt können wir diese Totenköpfe animieren! Es funktioniert, äh, größtenteils.

Die Idee ist, für jede Region im Randbild eine andere Animation zu erstellen. Zum Beispiel haben wir in der oberen linken Ecke einen Totenkopf, der von rechts nach links geht, während ein zweiter Totenkopf gleichzeitig von oben nach unten geht.

Wir animieren die transform-Eigenschaft für die Bewegung. Wir nutzen auch die <use> von SVG, um die Wiederholung der langwierigen <path>-Definition für jeden Totenkopf zu vermeiden.

<svg version="1.1" height="96" width="96" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <style>
  @keyframes left {to {transform: translate(-32px, 0)}}
  @keyframes down {to {transform: translate(0, 32px)}}
 </style>
 <defs>
  <path id="skull" d="..." fill="red"/>
 </defs>

 <!-- Top-left corner: one skull goes left, another goes down -->
 <use href="#skull" x="0" y="0"  style="animation: down .4s infinite linear"/>
 <use href="#skull" x="32" y="0" style="animation: left .4s infinite linear"/>
</svg>

Die SVG-Animationssyntax mag Ihnen bekannt vorkommen, denn anstelle einer SVG-spezifischen Syntax wie SMIL verwendet sie einfach CSS-Animationen. Cool, oder?

Das ist es, was wir bekommen.

Und wenn wir ein Gitter hinzufügen, können wir sehen, wie diese Animation auch einige der oberen und linken Ränder abdeckt.

Es beginnt beeindruckender auszusehen, nachdem wir die restlichen drei Kanten hinzugefügt haben und somit alle acht Regionen des Randbildes vollständig abgedeckt sind.

<svg version="1.1" height="96" width="96" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <style>
  @keyframes left {to {transform: translate(-32px, 0)}}
  @keyframes down {to {transform: translate(0, 32px)}}
  @keyframes right {to {transform: translate(32px, 0)}}
  @keyframes up {to {transform: translate(0, -32px)}}
 </style>
 <defs>
  <path id="skull" d="..." fill="red"/>
 </defs>

 <!-- Top-left corner: one skull goes left, another goes down -->
 <use href="#skull" x="0" y="0"  style="animation: down .4s infinite linear"/>
 <use href="#skull" x="32" y="0" style="animation: left .4s infinite linear"/>

 <!-- Top-right corner: one skull goes up, another goes left -->
 <use href="#skull" x="64" y="0" style="animation: left .4s infinite linear"/>
 <use href="#skull" x="64" y="32" style="animation: up .4s infinite linear"/>

 <!-- Bottom-left corner: one skull goes down, another goes right -->
 <use href="#skull" x="0" y="32" style="animation: down .4s infinite linear"/>
 <use href="#skull" x="0" y="64" style="animation: right .4s infinite linear"/>

 <!-- Bottom-right corner: one skull goes right, another goes up -->
 <use href="#skull" x="32" y="64" style="animation: right .4s infinite linear"/>
 <use href="#skull" x="64" y="64" style="animation: up .4s infinite linear"/>
</svg>

Und das gibt uns eine vollständige Schaltung.

Dancing skulls, ready to go into your border!

Zusammenfassend verwenden wir das gerade erstellte animierte SVG als border-image und erhalten das gewünschte Ergebnis.

Ich könnte den ganzen Tag damit spielen…

Nachdem ich das zum Laufen gebracht hatte, begann ich, mit den Animationseigenschaften zu experimentieren. Das ist einer der Vorteile der Verwendung von SVGs anstelle von GIFs: Die Art der Animation zu ändern, ist so einfach wie das Ändern einer CSS-Eigenschaft in der SVG-Quelldatei, und Sie sehen das Ergebnis sofort, ganz zu schweigen von den kleineren Dateigrößen (insbesondere bei Gradienten), voller Farbunterschützung und scharfer Skalierung.

Zuerst versuchte ich zu sehen, wie es aussehen würde, wenn ich die Animations-Timing-Funktion auf ease ändere.

Wir können die Totenköpfe auch zwischen Rot und Grün überblenden lassen.

Wir können die Totenköpfe sogar ihre Ausrichtung ändern lassen, während sie um die Highscore-Liste kreisen.

Gehen Sie zum JavaScript-Tab, wo Sie mit dem SVG-Quellcode herumspielen und es selbst ausprobieren können.

Der Elefant 🐘 im Raum (hüstel, Firefox)

Ich war sehr glücklich, als ich das zum ersten Mal zum Laufen brachte. Es gibt jedoch einige Einschränkungen, die Sie beachten sollten. Erstens und vor allem rendert Firefox aus irgendeinem Grund die Animation nicht an den Rändern des Rahmens, sondern nur an den Ecken.

Lustigerweise, wenn ich den SVG in ein GIF mit der gleichen Animation umwandle, funktionierte es perfekt. Aber dann hören die Ränder auf, sich unter Chrome zu animieren! 🤦‍♂️

Auf jeden Fall scheint es ein Browserfehler zu sein, denn wenn wir die Eigenschaft border-image-repeat auf stretch ändern, animiert Firefox die Ränder, aber das Ergebnis ist etwas eigenartig (obwohl es wahrscheinlich zum Thema der Seite passt).

Das Ändern des Werts von border-image-repeat auf space scheint auch zu funktionieren, aber nur, wenn die Breite des Elements kein ganzzahliges Vielfaches der Totenkopfgröße ist, was bedeutet, dass wir einige Lücken in der Animation bekommen.

Ich habe auch ein paar visuelle Probleme festgestellt, wenn die Containergröße kein Vielfaches der Patchgröße (in diesem Fall 32 Pixel) ist, wie z. B. winzige schwarze Linien auf den Totenköpfen. Ich vermute, das hat mit einem Rundungsfehler bei Fließkommazahlen zu tun. Es neigt auch dazu, beim Zoomen zu versagen.


Nicht perfekt, aber definitiv fertig! Wenn Sie die endgültige Version in Aktion sehen möchten, sind Sie eingeladen, sich die High Scores von The Skull-Seite anzusehen. Hoffentlich werden bald einige Ihrer Namen darauf stehen!