CSS-Transitions: Best Practises

von
Kreativ&Söhne

Mit CSS3 sind mit den Transitions sehr praktische Möglichkeiten entstanden, um HTML-Elemente mit einfache Animationen zu versehen. Man kann sie beispielsweise wunderbar für taktiere Rückmeldungen an den Nutzer verwenden, um ihm den Erfolg einer Aktion zu signalisieren.

Allerdings gestaltet sich die Nutzung dieser Animationen hin und wieder recht wenig elegant. Wir stellen unseren Ansatz vor, um mit vielen animierten Elementen und Kontext-abhängigen Animationen besser klar zu kommen.

Basics

Nur ganz kurz zur Theorie: Man muss jedem Element immer erst mittels "transition"-Eigenschaft mitteilen, dass Veränderungen am Element animiert werden sollen:

CSS

.foo {
    width: 128px;
    height: 128px;
    background-color: #333;
    -webkit-transition: all 200ms ease 0s;
    -moz-transition: all 200ms ease 0s;
    -ms-transition: all 200ms ease 0s;
    -o-transition: all 200ms ease 0s;
    transition: all 200ms ease 0s;
}

.bar {
    width: 256px;
    height: 128px;
    background-color: #333;
    -webkit-transition: all 200ms ease 0s;
    -moz-transition: all 200ms ease 0s;
    -ms-transition: all 200ms ease 0s;
    -o-transition: all 200ms ease 0s;
    transition: all 200ms ease 0s;
}

HTML

<div class="foo">lorem ipsum</div>
<div class="bar">lorem ipsum</div>

Die Beispiele besagen, dass die Elemente mit der zugewiesenen Klasse foo und bar animiert werden sollen. Und zwar soll durch "all" jede transition-fähige Änderung über einen Zeitraum von 200 Millisekunden mittels eines Easing-Effekts abgespielt werden. Dabei soll das ohne Verzögerung (0s) stattfinden.

Das Problem: Wartbarkeit und Übersichtlichkeit

Auf den ersten Blick ist das eigentlich ganz nett, aber die Übersicht geht sehr schnell verloren, wenn das Dokument viele dieser Elemente enthält. Die Stylesheets werden damit stark aufgebläht und schon kleine Änderungen fressen viel Zeit.

Der Ansatz: Separate animate-Klassen

Deshalb hat es sich bei uns eingebürgert, dass wir vorgefertigte Animationsklassen nutzen, die den Elementen nur noch zugewiesen werden müssen:

CSS

.foo {
    width: 128px;
    height: 128px;
    background-color: #333;
}

.bar {
    width: 256px;
    height: 128px;
    background-color: #333;
}

.animate200 {
    -webkit-transition: all 200ms ease 0s;
    -moz-transition: all 200ms ease 0s;
    -ms-transition: all 200ms ease 0s;
    -o-transition: all 200ms ease 0s;
    transition: all 200ms ease 0s;
}

HTML

<div class="foo animate200">lorem ipsum</div>
<div class="bar animate200">lorem ipsum</div>

Wunderbar. Jetzt sind wir deutlich flexibler.

Wir können auch noch andere, entsprechend sinnvoll benannte Klassen für Transitions anlegen, die über einen anderen Zeitraum animieren sollen:

CSS

.animate100 {
    -webkit-transition: all 100ms ease 0s;
    -moz-transition: all 100ms ease 0s;
    -ms-transition: all 100ms ease 0s;
    -o-transition: all 100ms ease 0s;
    transition: all 100ms ease 0s;
}

.animate200 {
    -webkit-transition: all 200ms ease 0s;
    -moz-transition: all 200ms ease 0s;
    -ms-transition: all 200ms ease 0s;
    -o-transition: all 200ms ease 0s;
    transition: all 200ms ease 0s;
}

.animate250 {
    -webkit-transition: all 250ms ease 0s;
    -moz-transition: all 250ms ease 0s;
    -ms-transition: all 250ms ease 0s;
    -o-transition: all 250ms ease 0s;
    transition: all 250ms ease 0s;
}

HTML

<div class="foo animate200">lorem ipsum</div>
<div class="bar animate250">lorem ipsum</div>
<div class="baz animate100">lorem ipsum</div>

Allerdings gibt es nun ein weiteres Problem; Jede Änderung am Stylesheet durch JavaScript, genau so wie jede Änderung durch einen Nutzer-ausgelösten CSS-Pseudo-Selektor (:hover) führt immer zu einer Animation. Eventuell ist es aber nicht in jedem Fall gewünscht, dass animiert wird.

Beispiel

Es existiert ein Element, das per JavaScript auf Klick verändert wird. In Falle einer Änderung mittels JavaScript soll eine Animation ausgeführt werden. Im Falle einer Hover-Aktion mittels CSS-Pseudoselektor ":hover" soll aber keine Animation stattfinden.

In diesem Fall funktioniert die folgende Konstellation nicht wie gewünscht:

CSS

.foo {
    width: 128px;
    height: 128px;
    background-color: #999;
}

.foo:hover {
    background-color: #aaa;
}

.animate200 {
    -webkit-transition: all 200ms ease 0s;
    -moz-transition: all 200ms ease 0s;
    -ms-transition: all 200ms ease 0s;
    -o-transition: all 200ms ease 0s;
    transition: all 200ms ease 0s;
}

HTML

<div class="foo animate200">lorem ipsum</div>

JavaScript

$('.foo').click(function () {
    // manipuliere Stylesheet des Elements mit Animation
    $(this).css({
        "background-color": "#090"
    })
});

Das Resultat ist eine Animation sowohl bei Klick, als auch bei Hover.

Nun könnte man die Transition für den Hover-Fall auch einfach hart abschalten:

CSS

.foo {
    width: 128px;
    height: 128px;
    background-color: #999;
}

.foo:hover {
    background-color: #aaa;
    -webkit-transition: none;
    -moz-transition: none;
    -ms-transition: none;
    -o-transition: none;
    transition: none;
}

.animate200 {
    -webkit-transition: all 200ms ease 0s;
    -moz-transition: all 200ms ease 0s;
    -ms-transition: all 200ms ease 0s;
    -o-transition: all 200ms ease 0s;
    transition: all 200ms ease 0s;
}

HTML

<div class="foo animate200">lorem ipsum</div>

JavaScript

$('.foo').click(function () {
    // manipuliere Stylesheet des Elements mit Animation
    $(this).css({
        "background-color": "#090"
    })
});

Aber hier gibt es gleich drei Probleme.

  1. Man verschandelt sich schon wieder seine Stylesheets
  2. Die Abschaltung der Animation funktioniert nur bei Mouse-In, aber nicht bei Mouse-Out, da bei Mouse-Out bereits der Pseudo-Selektor :hover nicht mehr greift und damit die alten Regeln wieder aktiv sind. Die besagen aber, dass die Änderung der Hintergrundfarbe über 200 Millisekunden animiert werden soll.
  3. Die gewünschte Animation beim Klick auf das Element wird ebenfalls nicht mehr ausgeführt, da in dem Fall :hover aktiv ist und Animationen abschaltet.

Die Lösung: Dynamische Nutzung von animate-Klassen

Das kann also keine Lösung sein. Deshalb verfolgen wir nun schon seit einiger Zeit einen alternativen Ansatz für Animationen; Wir nutzen weiterhin die praktischen .animateXXX-Klassen, weisen diese aber den gewünschten Elementen nicht schon pauschal zu, sondern erst zu dem Zeitpunkt, wenn die Animation wirklich benötigt wird:

JavaScript

$('.foo').click(function () {
    // weise Animationsklasse zu
    $(this).addClass('animate200');
    
    // Animiere zu Änderungen in Klasse "selected", oder animiere zum Standard
    if ($(this).hasClass('selected')) {      
        $(this).removeClass('selected');
    } else {
        $(this).addClass('selected');
    }
    
    // Warte bis die Animation vorbei ist
    window.setTimeout(function($el){$el.removeClass('animate200')},250, $(this));
});

CSS

.foo {
    width: 128px;
    height: 128px;
    background-color: #999;
}

.foo:hover {
    background-color: #888;
}

.foo.selected {
    background-color: #090;
}

.animate200 {
    -webkit-transition: all 200ms ease 0s;
    -moz-transition: all 200ms ease 0s;
    -ms-transition: all 200ms ease 0s;
    -o-transition: all 200ms ease 0s;
    transition: all 200ms ease 0s;
}

HTML

<div class="foo">lorem ipsum</div>

Der Timeout auf der letzten Zeile ist notwendig, um die Animation nicht bereits frühzeitig abbrechen zu lassen. Durch den Timeout wird das Entfernen der animate-Klasse um 250 Millisekunden verzögert. In dieser Zeit ist die Animation durchgelaufen. Würde das Entfernen der animate-Klasse nicht verzögert werden, dann gäbe es keine Animation. Denn JavaScript hätte die animate-Klasse bereits wieder entfernt, bevor die Animation überhaupt angefangen hätte.

Sicherlich lässt sich diese Timeout-Problematik eleganter lösen - Zum Beispiel mit einem jQuery-Plugin, das auf einen Callback hört. Aber darum sollte es in dieser kleinen Abhandlung gar nicht gehen.

Wir hoffen, dem einen oder anderen einen neuen Ansatz für die Handhabung von CSS-Animationen gegeben zu haben.

Zurück