SMIL ist tot! Lang lebe SMIL! Ein Leitfaden zu Alternativen für SMIL Funktionen

Avatar of Sarah Drasner
Sarah Drasner am

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

SMIL, die native Animationsspezifikation von SVG, wurde hoch geschätzt, da sie so viele Extras für eine performante SVG-Animationsdarstellung bietet. Leider schwindet die Unterstützung für SMIL in WebKit und war nie (und wird wahrscheinlich auch nie) für die Browser IE oder Edge von Microsoft existieren. Keine Angst! Wir haben Sie abgedeckt. Dieser Artikel untersucht einige dieser SMIL-spezifischen Funktionen und befasst sich mit den Alternativen, um die gleichen Effekte mit einer längeren Unterstützungsdauer zu erzielen.

SMIL-Funktion: Bewegung entlang eines Pfades

Eines der überzeugendsten Dinge, die SMIL für realistische Bewegungen in SVG bietet, ist die Bewegung entlang eines Pfades. Nur wenige Dinge im wirklichen Leben bewegen sich entlang einer geraden Linie, sodass die Bewegung entlang eines Pfades es uns ermöglicht, das nachzuahmen, was wir im Alltag sehen. Zuvor übergaben Sie einen SVG-Pfad an animateMotion, mit path und definierten die Pfaddaten. Sie wählen das zu animierende Element aus, indem Sie es mit xlink:href=”#thingtoanimate” bezeichnen.

<animateMotion 
  xlink:href="#lil-guy" 
  dur="3s" 
  repeatCount="indefinite" 
  fill="freeze" 
  path="M 25,50 C 37.5,25 37.5,25 50,0 75,50 75,50 100,100 50,100 50,100 0,100 12.5,75 12.5,75 25,50 Z" />

Sehen Sie den Pen SMIL-Bewegungspfad von Sarah Drasner (@sdras) auf CodePen.

Eine Alternative: CSS

Glücklicherweise ist das Motion-along-a-Path-Modul jetzt in CSS integriert. Die Unterstützung ist bisher gering (nur Chrome, Opera und Android):, aber Sara Soueidan hat die Einführung in Edge vorgeschlagen und sie hat bisher starke Unterstützung mit rund 420+ Stimmen zum Zeitpunkt der Veröffentlichung des Artikels. Bitte fügen Sie Ihre Stimme hinzu, um sicherzustellen, dass diese Funktion implementiert wird. Die Abstimmung für Firefox finden Sie hier.
Die Abstimmung für die Unterstützung in Safari ist, soweit ich das beurteilen kann, etwas individueller. Ich habe mich registriert, um einen Fehlerbericht auszufüllen und das Motion-Path-Modul in CSS als Funktion angefordert.

Um Bewegung entlang eines Pfades in CSS zu verwenden, übergeben Sie die Pfaddaten an die Eigenschaft offset-path wie folgt:

.move-me {
  offset-path: path('M3.9,74.8c0,0,0-106.4,75.5-42.6S271.8,184,252.9,106.9s-47.4-130.9-58.2-92s59.8,111.2-32.9,126.1 S5.9,138.6,3.9,74.8z');
}

Sehen Sie den Pen Spielen mit dem Motion Path Module in CSS von CSS-Tricks (@css-tricks) auf CodePen.

Ich erhalte Pfaddaten aus einem SVG, das ich in Illustrator erstelle und dann in SVGOMG optimiere.

In diesem Fall möchte ich sicherstellen, dass der Pfad vom Startpunkt bis zum Ende des Pfades folgt, und Sie können sehen, dass es sich um einen geschlossenen Pfad handelt, durch das kleine z am Ende. Das bedeutet, dass dieser Pfad in einer Schleife verläuft, so dass das seltsame kleine Wesen am selben Ort endet, an dem es begonnen hat. Ich habe diese Parameter in meinen Keyframe-Werten festgelegt, nur den Wert von 100% angegeben, da der Standardwert Null ist.

@keyframes motionpathguy {
  100% {
    motion-offset: 100%;
  }
}

und dann die Animation auf das Element aufrufen

.move-me {
  animation: motionpathguy 10s linear infinite both;
}

Eine Alternative: GreenSock's Bewegung entlang eines Pfades

Wenn Sie die breiteste aktuelle Unterstützung und die flexibelste Implementierung wünschen, sollten Sie GreenSock verwenden. Das Bezier-Plugin von GSAP (standardmäßig in TweenMax enthalten) bietet Unterstützung bis IE7 für Nicht-SVG-Elemente und bis IE9 für SVG (die breiteste verfügbare SVG-Animationsunterstützung). Es funktioniert auf Mobilgeräten hervorragend. Ich habe darüber bereits geschrieben auf dem David Walsh Blog, aber hier ist eine kleine Zusammenfassung und ein paar zusätzliche neue Funktionen, die seitdem herausgekommen sind.

Ursprünglich würden Sie ein Array von Werten übergeben

bezier: {
  type: "soft",
  values:[{x:10, y:30}, {x:-30, y:20}, {x:-40, y:10}, {x:30, y:20}, {x:10, y:30}],
  autoRotate: true
}

Aber wie Sie hier sehen können, haben Sie auch die Möglichkeit, automatisch zu drehen (oder nicht), genau wie bei SMILs rotate. Wenn Ihnen die SMIL-Fähigkeit fehlt, Auto-Reverse oder den Parameter auto:n für die Anfangsposition der Drehung oder den Drehgrad anzugeben, ermöglicht GSAP die Verwendung von rotation:90, um den Grad zu ändern, oder eine feinere Kontrolle, falls Sie diese benötigen.

autorotate: [
  first position property, like "x",
  second position property, like "y",
  rotation property, typically "rotation" but can be “rotationY”,
  integer for radians/degrees the rotation starts from like 10,
  boolean for radians or degrees- radians is true
]

In SMIL können Sie den Pfad oder die Gruppe transformieren, um diese Ausrichtung des Objekts während der Bewegung zu ändern. In GSAP können Sie dies einfach mit autoRotate: false und der Initialisierung der Drehung mit set erreichen. Sie können das Element auch direkt auf dem SVG-Attribut transformieren, wie Sie es mit SMIL tun würden, obwohl das etwas weniger elegant und während der Arbeit schwieriger zu verfolgen ist.

TweenMax.set("#foo" {
  rotation: 90 // or whatever number
});

Sie können den Typ auch auf thru, soft, quadratic oder cubic setzen. Weitere Dokumentationen zu jedem dieser Punkte finden Sie in den GreenSock API-Dokumenten. Ein schöner Wert von thru ist die Möglichkeit, die Krümmung eines Elements zu beeinflussen. Wenn Sie sich die Punkte als Koordinaten vorstellen, zu denen gesprungen wird, steuert die Krümmung, wie direkt ein Pfad zwischen diesen Punkten genommen wird. 0 ist ein direkter Pfad, 1 ist etwas lockerer, mit 2, das eine schöne Kurve macht, und Werten von 3 und höher, die sich zu verdrehen beginnen.

Sehen Sie den Pen Demo für Krümmung in GreenSock Bezier von Sarah Drasner (@sdras) auf CodePen.

In jüngerer Zeit hat GreenSock auch die Möglichkeit freigegeben, Pfaddaten wie die CSS- und SMIL-Module zu übergeben, wie Sie es mit nativem SMIL tun würden. Dies ist eine Erweiterung ihres MorphSVG-Plugins, sodass Sie das Plugin hinzufügen und es wie folgt verwenden würden:

TweenMax.to("#lil-guy", 3, {
  bezier: {
    MorphSVGPlugin.pathDataToBezier("#path", {align: "#lil-guy" }), 
    type: "cubic"
  },
  ease: Linear.easeNone,
  repeat: -1
});
<path id="path" d="M 25,50 C 37.5,25 37.5,25 50,0 75,50 75,50 100,100 50,100 50,100 0,100 12.5,75 12.5,75 25,50 Z" fill="none" />

Sehen Sie den Pen SMIL-Bewegungspfad von Sarah Drasner (@sdras) auf CodePen.

Standardmäßig wäre die obere linke Ecke der von mir animierten Gruppe, in diesem Fall #lil-guy, auf die Pfad-Trajektorie gesetzt worden. Dies hätte optisch zu einer Fehlausrichtung geführt. Daher habe ich mit TweenLite.set eingestellt, dass #lil-guy stattdessen den Mittelpunkt verwendet.

TweenLite.set("#lil-guy", {xPercent:-50, yPercent:-50}); 

Sie können diese Pfade auch versetzen, indem Sie ein Objekt als zweiten Parameter dieser Methode übergeben und offsetX und offsetY innerhalb von pathDataToBezier definieren – Vorsicht, dass Sie möglicherweise den viewBox erweitern müssen, damit die Gruppe oder das Attribut, das Sie animieren, nicht abgeschnitten wird. Formatierungs-Nerds: Ich platziere die Objekte aus Gründen der Lesbarkeit in neuen Zeilen.

// offset the path coordinates by 125px on the x-axis, and 50px on the y-axis:
TweenMax.to("#lil-guy", 3, {
  bezier: {
    values: MorphSVGPlugin.pathDataToBezier("#path", {
      offsetX: 125, 
      offsetY: 50, 
      align: "#lil-guy"
    }),
    type: "cubic"
  },
  ease: Linear.easeNone,
  repeat: -1
});

Sehen Sie den Pen SMIL-Bewegungspfad von Sarah Drasner (@sdras) auf CodePen.

Sie können sogar eine Matrixkoordinate für diese Positionierung definieren.

// scale the path coordinates up by 1.25 
// and shift it over 120px on the x-axis 
// and up 30px on the y-axis:
TweenMax.to("#lil-guy", 3, {
  bezier: {
    values: MorphSVGPlugin.pathDataToBezier("#path", {
      matrix:[1.5,0,0,1.5,120,-30], 
      align:"lil-guy"}),
    type: "cubic"
  },
  ease: Linear.easeNone,
  repeat: -1
});

Sehen Sie den Pen SMIL-Bewegungspfad von Sarah Drasner (@sdras) auf CodePen.

Eine weitere Option ist, das Mitglied align auf "relative" zu setzen. Dies verhindert ein Springen, indem die Position jeder Koordinate relativ zu x:0, y:0 gehalten wird. In den vorherigen Pens habe ich align verwendet, um die Bewegung mit der Gruppe #lil-guy selbst zu koppeln.

Weitere Informationen zu dieser neuen (im Sinne von neu am Tag dieses Beitrags!) Funktion in GSAP's Bezier Plugin API finden Sie in den Dokumenten und diesem großartigen Erklärvideo.

SMIL-Funktion: Formen-Morphing

Zuvor konnten Sie Pfaddaten als Werte in animate attribute übergeben, um eine Form zu morphen. Noah Blon hat ein großartiges Beispiel

Sehen Sie den Pen Sitepoint Challenge #1 in SVG und SMIL von Noah Blon (@noahblon) auf CodePen.

Eine Alternative: Snap.svg oder SVG Morpheus

Einige Bibliotheken bieten morphing Pfad- oder Formenwerte, wie z.B. Snap.svg und SVG Morpheus, aber die Einschränkung (auch in SMIL) ist, dass die Form die gleiche Anzahl von Punkten haben muss, sonst sieht das Morphing schrecklich aus oder schlägt komplett fehl. Dies ist auf der Pre-Processing-Seite enttäuschend, da es bedeutet, dass Sie genau verfolgen müssen, was Sie erstellen, oder gut mit Ihrem Designer zusammenarbeiten müssen, um sicherzustellen, dass Sie mit diesen (manchmal willkürlichen) Zwischenpunktdaten versorgt werden. Die zusätzlichen Punkte blähen auch unnötig Ihren Code auf.

Eine Alternative: GreenSock MorphSVG

Ich kann das MorphSVG-Plugin von GSAP sehr empfehlen, da es Formen und Pfade mit unterschiedlichen Punktzahlen wunderschön morpht. Sehen Sie die Umschaltung auf dem Logo dieser Website für eine Demo des Morphs in Aktion. Hier ist ein weiteres Beispiel

Sehen Sie den Pen Austauschbarer Hipster von Sarah Drasner (@sdras) auf CodePen.

Da das MorphSVG-Plugin Pfaddaten tweent, wenn Sie eine Form konvertieren müssen, würden Sie die Option convertToPath verwenden

MorphSVGPlugin.convertToPath("ellipse"); 
// or circle, rect, etc

Dies ermöglicht uns, wirklich komplexe Formen zu tweenen, und ist ein Game-Changer für die gesamte Bewegung im Web.

Es gibt ein paar zusätzliche Funktionen, die dieses Plugin bietet und die es wirklich auszeichnen. Das erste ist das Utility-Plugin findShapeIndex. Nehmen wir an, Sie sind unzufrieden mit der Art und Weise, wie die Form morpht (obwohl in 9 von 10 Fällen die Auto-Voreinstellung gut funktioniert). Sie laden das Plugin (keine Sorge, Sie fügen kein zusätzliches Gewicht hinzu, da es in der Produktion nicht benötigt wird) und übergeben zwei Werte: die ID der ersten zu tweenenden Form und die ID der zweiten. Ein GUI wird angezeigt, in dem Sie zwischen Werten wechseln können, und es wird auch automatisch repeat: -1 verwendet, sodass es kontinuierlich zwischen den Formen wechselt.

findShapeIndex("#hex", "#star");
// you can comment out above line to automatically disable findShapeIndex() UI

Sehen Sie den Pen SMIL-Bewegungspfad von Sarah Drasner (@sdras) auf CodePen.

Sobald Sie diesen zusätzlichen Wert haben, würden Sie shapeIndex innerhalb des morphSVG-Objekts übergeben.

TweenLite.to("#hex", 1, {morphSVG: { shape: "#star", shapeIndex: 1 }});

Das zweite dieser zusätzlichen Features ist die Fähigkeit des Plugins, ausgeschnittene Pfade zu parsen, was keine andere Bibliothek bietet. Schließlich können Sie auch die erste Start-ID wiederverwenden (anstatt die Pfaddaten für die Wiederverwendung speichern zu müssen). Es ist erwähnenswert, dass diese Funktionen bei der ersten Veröffentlichung des Plugins nicht verfügbar waren, aber GreenSock erkannte den Bedarf an Unterstützung und hat sie daher integriert.

Da wir nicht mehr an eine bestimmte Anzahl von Punkten gebunden sind, haben wir die Möglichkeit für verschiedene Arten von Effekten erweitert. Unten habe ich etwas Rauch gemacht

Sehen Sie den Pen Wo Rauch ist von Sarah Drasner (@sdras) auf CodePen.

SMIL-Funktion: DOM-Events

Dinge wie Hover und Klick waren gut in SMIL integriert. Um loszulegen, war es möglich, begin="click" oder begin="hover" anzugeben.

<animate 
    xlink:href="#rectblue"
    attributeName="x"
    from="0"
    to="300" 
    dur="1s"
    begin="click"
    values="20; 50"
    keyTimes="0; 1"
    fill="freeze" />

Sehen Sie den Pen SMIL-Bewegungspfad von Sarah Drasner (@sdras) auf CodePen.

Eine Alternative: JavaScript

Es gibt native DOM-Ereignisse wie onmouseenter und onmouseleave für Hover und click für, wissen Sie, Klicks. Sie könnten sie verwenden, um Ihre JavaScript-basierten Animationen auszulösen.

Eine Alternative: JavaScript + CSS

Sie könnten JavaScript verwenden, um einen Klassennamen zu ändern oder CSS-Stile direkt zu ändern. Hier ist eine Möglichkeit: Ändern Sie den animation-play-state, um die Animation von einem Ereignisauslöser aus zu starten.

.st0 {
  animation: moveAcross 1s linear both;
  animation-play-state: paused;
}
@keyframes moveAcross {
  to {
    transform: translateX(100px);
  }
}
document.getElementById("rectblue").addEventListener("click", function() {
  event.target.style.animationPlayState = "running";
});

oder in jQuery

$(".st0").on("click", function() {
  $(this).css("animation-play-state", "running");
});

Sehen Sie den Pen SMIL-Bewegungspfad von Sarah Drasner (@sdras) auf CodePen.

Diese Implementierung würde diese Animation nicht sofort wieder auf Anfang setzen, wie im SMIL-Beispiel. Wenn Sie dies erreichen möchten, beschreibt ein früherer Artikel auf CSS-Tricks einige gute Möglichkeiten, dies zu tun.

Eine Alternative: Greensock

In GSAP ist der Neustart einfacher. Wir könnten die Animationen zu einer Timeline hinzufügen, sie auf pausiert setzen und dann bei Klick neu starten. Diese Implementierung ist dem, was Sie von SMIL erwarten würden, etwas näher, da wir nichts Hacky tun müssen, wie das Klonen/Wiedereinfügen eines DOM-Knotens oder das Ändern von Eigenschaften, die auf dem Element gesetzt sind.

// instantiate a TimelineLite    
var tl = new TimelineLite();

// add a tween to the timeline
tl.to(foo, 0.5, { left: 100 });

$(".st0").on("click", function() {
  tl.restart();
});

SMIL-Funktion: „X“ nach Abschluss von „Y“ ausführen

SMIL erlaubte auch kompliziertere zeitgesteuerte Ereignisse wie begin="circ-anim.begin + 1s". Dies ist besonders nützlich beim Verketten von Animationen.

Eine Alternative: CSS

In CSS könnten wir die Animationen verketten, indem wir eine Verzögerung auf den zweiten Wert setzen

.foo {
  animation: foo-move 2s ease both;
}
.bar {
  animation: bar-move 4s 2s ease both; 
  /* the 2 second value corresponds with the length of the iteration of the first. */
}

Diese Arbeitsweise ist ein kleiner Dämpfer, da Sie darauf achten müssen, sowohl das erste Intervall als auch die Verzögerung zu ändern.

Eine Alternative: CSS-Präprozessierung

Die Wartung und Verwaltung dieser Intervalle kann etwas einfacher werden, wenn wir eine Variable in (z. B.) Sass verwenden

$secs: 2s;
.foo {
  animation: foo-move $secs ease both;
}
.bar {
  animation: bar-move 4s $secs ease both; 
}

Jetzt wissen wir, dass sie synchron bleiben, wenn wir den einen Wert aktualisieren.

Aber wenn wir immer erkennen wollen, wann die Animation abgeschlossen ist, bietet JavaScript dazu einige nette native Funktionalität mit animationEnd.

$("#rectblue").on("animationend", function() {   
  $(this).closest("svg").find("#rectblue2").css("animation-play-state", "running");     
});
#rectblue2 {
  animation: moveAcross 2s 1s ease both;
  animation-play-state: paused;
}

Sie müssen möglicherweise auf Wiedergabe klicken, um die unten stehende Animation zu sehen.

Sehen Sie den Pen SMIL-Bewegungspfad von Sarah Drasner (@sdras) auf CodePen.

Für die Verzögerung würden wir sie in die CSS-Eigenschaft animation-delay des Elements selbst einbacken, wie oben, oder wir könnten dies mit einem setTimeout ausdrücken.

setTimeout(function timeoutHandler() {
  // animation goes here, with any language
}, 1000); // wait for a second

Eine Alternative: Greensock

Meine bevorzugte Option ist es, die Animation-Timeline endlich zu kontrollieren. Wir können GreenSocks TimelineLite dafür verwenden, die auf verschiedene Arten ausgedrückt werden kann.
Einfache Timeline

// instantiate a TimelineLite    
var tl = new TimelineLite();

// add a tween at the beginning of the timeline
tl.to(foo, 0.5, { left: 100 });

// use the position parameter "+=1" to schedule next tween 1 second after the previous tweens end
tl.to(foo, 0.5, { left: 200 }, "+=1");

Timeline mit relativer Beschriftung

// add a label 0.5 seconds later to mark the placement of the next tween
tl.add("myRelativeLabel")
// use the label to specify an animation a second after the 
tl.to(foo, 0.5, { scale: 0 }, "myRelativeLabel+=1");

// or to use to this label for things like interaction 
tl.play("myRelativeLabel");

Ich bevorzuge relative Labels, da Sie einen Zeitpunkt wählen können, zu dem viele Dinge ausgelöst oder verzögert werden, und selbst wenn sich dieser Zeitpunkt ändert, müssen Sie keine Neuberechnungen durchführen, wie Sie es in CSS tun müssten.

Das Schöne an der Timeline ist, dass Sie dann die volle Kontrolle über viele verschiedene Objekte an einem Ort haben und Dinge wie repeatDelay (Verzögerungen zwischen mehreren Wiederholungen) anbieten können.

SMIL bietet repeatDur, womit Sie angeben können, wie lange die Wiederholung dauern soll, wenn Sie nicht die Standardeinstellung wie repeatDur="01:30" wünschen. In GreenSock können Sie die Timeline beschleunigen oder verlangsamen und dadurch die Wiederholungsdauer mit timeScale(n) anpassen, oder repeat: -1 für Zeiten setzen, die Sie sonst mit repeatDur="indefinite" setzen würden.

Praktische Ersatz-Referenztabelle

Nachdem wir uns nun eingehend mit einigen der nützlichsten SMIL-spezifischen Funktionen befasst haben, ist es erwähnenswert, dass es viele weitere Ersatzmöglichkeiten für SMIL-Funktionen gibt, die Sie vielleicht bereits kennen. Wir haben die kleine Tabelle unten zur schnellen Referenz dieser einfacheren Implementierungen erstellt.

SMIL-Code Ersatzcode Ersatztechnologie
keyTimes @keyframes CSS
keySplines cubic-bezier CSS
restart restart(); GSAP
calcMode=”discrete” steps() CSS
remove kill();
clear();
clearProps: “all”
GSAP
freeze animation-play-state: paused CSS
freeze pause(); GSAP
fill animation-fill-mode CSS
repeatCount=”indefinite” animation-iteration-count: infinite; CSS
repeatCount=”indefinite” repeat: -1 GSAP
whenNotActive detect animation-play-state in JS CSS, Vanilla JavaScript oder jQuery
animateMotion path motion-path CSS
animateMotion path bezier GSAP
Werte animieren (Pfad-Morphing) MorphSVG GSAP
begin=”hover” mouseover, mouseenter/
mouseout, mouseleave
jQuery, Vanilla JS
begin=”click” click jQuery, Vanilla JS
begin=”circ-anim.begin + 1s” animation-delay: $vars; SASS
begin=”circ-anim.begin + 1s” timeline, position parameter z.B. „+=1“ GSAP