BlogGIF-Like Animationen mit Sanity und Gatsby

GIF-Like Animationen mit Sanity und Gatsby

HTML5 Video ersetzt GIF-Animationen

Topics:

    Sanity

    Gatsby

    HTML

Read Time: 10 min

Published: 2020-08-31

In meinen Blog-Posts benutze ich kurze, sich wiederholende Animationen um die besprochenen Konzepte zu verdeutlichen, z.B:

Für diesen Zweck kommen auf vielen Websites GIF-Animationen zum Einsatz, diese sind heute jedoch nicht mehr unbedingt der Stand der Technik.
Es empfiehlt sich stattdessen HTML5-Video für diesen Zweck zu verwenden.
Die Vorteile sind unter anderem eine geringere Dateigröße und eine größere Farbpalette(GIF unterstützt nur 256 Farben).

In diesem Tutorial werde ich zeigen, wie man ein Video entsprechend vorbereitet, es in seinen Sanity-Workflow einbindet und es schließlich in Gatsby verfügbar macht.

Video vorbereiten

Als Video-Container für das HTML5-Video empfehle ich MP4/H.264, weil es das am meisten verbreitete Format ist.
Zu diesem Zeitpunkt wird es von 97,81% aller Browser unterstützt:

Je nachdem wie das Video für die Animation erstellt wurde, ist dieses vermutlich für die Verwendung auf einer Website noch nicht optimiert.
Typische Aufzeichnungsformate sind 1920x1080 Pixel (HD) oder 3840x2160 Pixel (UHD) mit 25 oder 30 Bildern pro Sekunde (Framerate).
Für die meisten Animationen ist eine so hohe Video-Qualität nicht notwendig. Viel wichtiger ist es die Dateigröße möglichst gering zu halten, damit die Website schnell lädt.
Für diesen Blog verwende ich meistens eine horiziontale Auflösung von 700px und eine Framerate von 15.

Als Video-Konverter empfehle ich: HandBrake
Die Verwendung ist kostenlos.

Handbrake - Summary

Starten wir das Programm werden wir von folgendem Menü begrüßt:

Open Source - Das Ausgangs-Video auswählen.
Range: Seconds - Kann verwendet werden um dass Video vorne und hinten zu beschneiden.
Format: MPEG-4 (Das steht für MP4)
Web Optimized - aktivieren
Save As - Gewünschten Dateinamen festlegen

Handbrake - Dimensions

Im Dimensions-Tab können wir das Video auf die gewünschte Größe bringen.

Anamorphic - Off
Storage Geometry - Die gewünschte Größe des Videos einstellen. Wie bereits erwähnt nutze ich für diesen Blog meistens 700px.

Handbrake - Video

Video Encoder - H.264
Das ist der Video-Codec der für das MP4 Container-Format verwendet wird.
Framerate - 15
Desto höher die Bildrate, desto flüssiger läuft die Animation, aber desto größer ist auch die Datei. Für meine Zwecke reichen meist 15 Frames pro Sekunde aus.
Constant Framerate
Garantiert eine konstante Framerate
Constant Quality: RF: 38
Desto höher die Qualität, desto größer auch die Datei.
Wie sich die hier eingestellte Qualität auf die wahrgenommene Qualität des Videos auswirkt, ist stark von dem Inhalt des Videos abhängig.
Hat ein Video z.B. viel Bewegung und viele kleine Details, so wird es unter einer niedrig eingestellten Qualität schneller leiden.
Hier muss also jeder für sich die entsprechende Einstellung finden.

Handbrake - Audio

Falls das Ausgangs-Video mit einer Tonspur aufgezeichnet wurde, kann diese hier gelöscht werden.

Mein Beispiel-Video hat mit diesen Einstellungen 65,8kB bei einer Länge von 7 Sekunden.

Sanity

Es gibt in Sanity keinen Schema-Type für Video-Files, wir erstellen also einen Custom-Type "gifLikeVideo" basierend auf dem Schema-Type "file"

JAVASCRIPT

export default {
  name: "gifLikeVideo",
  type: "file",
  options: {
    storeOriginalFilename: false,
    accept: ".mp4"
  }
};

storeOriginalFilename: false
Sanity überschreibt beim Upload den Original-Dateinamen um mögliche sensible Informationen zu verschleiern.
accept: ".mp4"
Nur .mp4-Files werden für den Upload akzeptiert um Fehler zu vermeiden.

Dieser Custom-Type kann jetzt überall in Sanity verwendet werden.
(Nicht vergessen diesen noch in createSchema() dem entsprechenden Schema zuzuweisen)
z.B. in einem Schema-Type "block", um die Animationen in einen Text-Block einzubauen:

JAVASCRIPT

    ...
    {
      name: "body",
      type: "array",
      title: "Body",
      of: [
        {
          title: "Block",
          type: "block",
          styles: [
            { title: "Normal", value: "normal" },
            { title: "H1", value: "h2" },
            { title: "H2", value: "h3" }
          ],
          marks: {
            decorators: [
              { title: "Strong", value: "strong" },
              { title: "Emphasis", value: "em" },
            ],
          }
        },
        {
          title: "GIFlikeVideo",
          type: "gifLikeVideo"
        },
      ]
    }
    ...

Gatsby

Um den Schema-Type "block" mit den darin enthaltenen Animationen darzustellen benutzen wir block-content-to-react.

JSX

import React from "react";
import { graphql } from "gatsby";
import PortableText from "@sanity/block-content-to-react";
import clientConfig from "../../client-config";

const BlogTemplate = (props) => {
  const blog = props.data.sanityBlog;

  const gifLikeVideo = (props) => {
    const targetFile = allSanityFileAsset.nodes.find((node) => {
      return node._id === props.node.asset._ref;
    });

    return (
      <div>
        <video autoPlay controls={false} loop muted preload={"auto"}>
          <source src={targetFile.url} type="video/mp4" />
        </video>
      </div>
    );
  };

  return (
    <div>
      <PortableText
        blocks={blog._rawBody}
        serializers={{
          types: { gifLikeVideo },
        }}
        {...clientConfig.sanity}
      />
    </div>
  );
};

export const query = graphql`
  query($id: String!) {
    sanityBlog(_id: { eq: $id }) {
      _rawBody
    }

    allSanityFileAsset {
      nodes {
        _id
        url
      }
    }
  }
`;

export default BlogTemplate;

Mittels GraphQL lesen wir den _rawBody aus, welcher die Daten des vorher definierten Schema-Type "block" enthält. Unter anderem unseren Custom-Type "gifLikeVideo" mit den MP4-Animationen.
Die URL zu unserem Video-File ist darin jedoch nicht gespeichert, deshalb müssen wir ebenfalls allSanityFileAsset laden.

Innerhalb von <PortableText /> definieren wir einen Serializer für unseren Custom-Type "gifLikeVideo".
Anschließend erstellen wir eine Funktion(den Serializer) mit dem selben Namen:

JSX

...
  const gifLikeVideo = (props) => {
    const targetFile = allSanityFileAsset.nodes.find((node) => {
      return node._id === props.node.asset._ref;
    });

    return (
      <div>
        <video autoPlay controls={false} loop muted preload={"auto"}>
          <source src={targetFile.url} type="video/mp4" />
        </video>
      </div>
    );
  };
...


Mittels find() suchen wir aus allen File-Assets aus Sanity das, wo die _id mit der _ref des im Schema-Type "block" gespeicherten Asset übereinstimmt.
Dadurch erhält man die URL für unser Video-File.

Schließlich werden im <video> Tag folgende Attribute definiert:
autoPlay - Das Video beginnt automatisch abzuspielen, wenn es fertig geladen ist.
controls={false} - Kontroll-Elemente (Wie z.B. Play/Pause-Button) werden nicht angezeigt.
loop - Spielt das Video in einer Dauerschleife ab
muted - Schaltet die Audio-Ausgabe stumm. Muss auch gesetzt werden, wenn das Video keine Audiospuren hat. Chrome verhindert sonst das Autoplay.
preload={"auto"} - Kontrolliert das Lade-Verhalten. Das Video soll gemeinsam mit der Website geladen werden. Nähere Informationen dazu hier.

Mit diesen Einstellungen verhält sich das Video so, wie man es von GIF-Animationen gewöhnt ist.

WebM

WebM ist ein neueres Video-Format als MP4 und bietet oft die gleiche Video-Qualität bei noch geringerer Datei-Größe.
Das Format wird aber derzeit noch nicht von so vielen Browsern unterstützt, im Moment von 94,52%.

<video> bietet aber die Möglichkeit mehr als ein <source> zu definieren. Der Browser des Users lädt dann das File, dass er unterstützt:

JSX

  ...
  <div>
    <video autoPlay controls={false} loop muted preload={"auto"}>
      <source src={targetFileWebM.url} type="video/webm" />
      <source src={targetFile.url} type="video/mp4" />
    </video>
  </div>
  ...

Um mit Handbrake ein Video in WebM zu konvertieren wählt man im Summary-Tab als Format: Matroska.

Die Endung der Ausgabedatei ändert sich auf .mkv
(WebM ist ein Subset von MKV)
Wir können die Dateiendung einfach überschreiben und nennen unsere Ausgabedatei my_animation.webm

Im Video-Tab muss noch der entsprechende Video-Codec eingestellt werden:

Für WebM verwenden wir VP9
Mit den folgenden Einstellungen erreiche ich im Beispiel eine ähnliche Video-Qualität wie die des MP4-Videos:

Bitrate (kbps): 30
2-Pass Encoding
Preset: veryslow

Bei einer Dateigröße von nur 26,7kB statt 65,8kB.