Es gibt mehrere Wege, Lade-Animationen in Projekte einzubauen. Der Klassiker ist das animierte GIF oder APNG, um eine bessere Transparenz zu ermöglichen. Auch kann man animierte SVG verwenden. Der Nachteil ist bei den Optionen aber, dass es immer Nachteile gibt. Das GIF geht nur auf einfarbigen Hintergründen, das PNG-Sprite animiert nur richtig gut mit JavaScript. Die SVG-Variante ist mit nicht gerade wenig Code verbunden.

Ein weiteres Problem ist, dass durch die oben genannten Methoden meist ein weiteres HTML-Element eingebaut werden muss. Deshalb suche ich schon länger nach einer Lösung, die mit Pseudo-Elementen arbeitet, auf jedem Hintergrund funktioniert und JavaScript nur benötigt, um die Klasse dem Element zuzuweisen. Meine jetzige Methode ist noch nicht perfekt. Aber sie funktioniert in den modernen Browsern so weit ganz gut.

Ein bisschen Sass und kein extra HTML

Ich habe in meinem Beispiel Sass verwendet, welches man gut als Chunk einbinden kann. Es gibt zwei Klassen, die es erlauben, den Loader vor oder nach einem Element einzubinden. Die Animationen sind reines CSS3. Auf Vendor-Prefixes habe ich im Beispiel verzichtet, um den Code übersichtlicher zu gestalten.

Das Sass

Zuerst definiere ich mit einem Attribute-Selektor die globalen loading-Klassen. Das mag etwas auf die Performance drücken, vermeidet aber das Schreiben von doppeltem Code. Außerdem arbeite ich hier mit Variablen, um den Code später leichter zu pflegen:

  $loadingSize: .6em; // size of the dots
$loadingOffset: .6; // margin to element
$loadingColor: #aaa; // dots color
$loadingEase: ease-out; // easing
$loadingDuration: 1s; // duration
$loadingIteration: infinite; // wanna loop?
$loadingDelay: .4s; // delay of the second dot
$loadingScaleStart: .8; // start size of the dots
$loadingScaleEnd: 1; // end size of the dots

// Global loading class
[class*="loading-"] {
  position: relative;
  &::before,
  &::after {
    animation: pulsate $loadingDuration $loadingEase;
    animation-iteration-count: $loadingIteration;
    background: $loadingColor;
    border-radius: 50%
    content: "";
    display: block;
    height: $loadingSize;
    margin-top: -$loadingSize / 2;
    position: absolute;
    opacity: 0;
    top: 50%;
    width: $loadingSize;
  }

  &::after {
    animation-delay: $loadingDelay;
  }
}

Da diese Klasse an ein Element geaddet wird und die Pseudo-Elemente absolute positioniert sind, muss das Element eine position haben. In diesem Fall habe ich relative mitgegeben. Wenn das Element schon eine position hat, kann man das auch rausnehmen.

Als Nächstes kommen die beiden Klassen, mit denen ich die Position der Loader definiere:

  .loading-after {
  &::before {
    right: -$loadingSize - $loadingOffset;
  }

  &::after {
    right: -$loadingSize * 2 - $loadingOffset * 1.2;
  }
}

.loading-before {
  &::before {
    left: -$loadingSize - $loadingOffset;
  }

  &::after {
    left: -$loadingSize * 2 - $loadingOffset * 1.2;
  }
}

Man sieht etwas Mathematik, woran man sieht, dass die Variablen wirklich hilfreich sind. Am Ende passiert nichts anderes, dass ::after neben das ::before wandert. Errechnet aus der Größe der Dots und dem Offset, das den Abstand zwischen den Elementen definiert. Da die Größe in em definiert ist, lässt es sich auch wunderbar skalieren. Es reicht ganz einfach, $loadingSize anzupassen.

Das Resultat sieht dann so aus:

Wie verwende ich das jetzt?

Im Codepen-Beispiel habe ich jetzt einfach im Click-Event ein addClass mitgegeben. Im Ajax-Formular lässt sich das so wunderbar in die Callbacks einbauen. Kein HTML, was per JS geschrieben werden muss, keine ausgeblendeten HTML-Container im DOM, die nur darauf warten, eingeblendet zu werden.

Nicht ganz unwichtig ist, dass die Elemente input und img keine Pseudo-Elemente oder -Klassen unterstützen. Bei Formularen sollte man daher entweder auf button oder Links zurückgreifen.

Verbesserungsvorschläge?

Immer her damit. Aus diesem Quock’n’Dirty-Flittchen könnte noch ‘ne richtige Dame werden.


1,622 0 1