Dieser Inhalt wurde automatisch aus dem Englischen übersetzt, und kann Fehler enthalten. Erfahre mehr über dieses Experiment.

View in English Always switch to English

Grundlagen zur Gestaltung eines Videoplayers

Im vorherigen Cross Browser Video Player Artikel haben wir beschrieben, wie ein plattformübergreifender HTML-Videoplayer mit den Media- und Vollbild-APIs erstellt wird. Dieser Folgeartikel behandelt, wie dieser benutzerdefinierte Player stilisiert und responsiv gemacht werden kann.

HTML-Markup

Es wurden einige Änderungen am HTML-Markup vorgenommen, das im vorherigen Artikel gezeigt wurde. Die benutzerdefinierten Videosteuerungen und das <progress>-Element befinden sich jetzt in <div>-Elementen, anstatt in Elementen einer ungeordneten Liste.

Das Markup für die benutzerdefinierten Steuerungen sieht nun wie folgt aus:

html
<div id="video-controls" class="controls" data-state="hidden">
  <button id="play-pause" type="button" data-state="play">Play/Pause</button>
  <button id="stop" type="button" data-state="stop">Stop</button>
  <div class="progress">
    <progress id="progress" value="0" min="0">
      <span id="progress-bar"></span>
    </progress>
  </div>
  <button id="mute" type="button" data-state="mute">Mute/Unmute</button>
  <button id="vol-inc" type="button" data-state="vol-up">Vol+</button>
  <button id="vol-dec" type="button" data-state="vol-down">Vol-</button>
  <button id="fs" type="button" data-state="go-fullscreen">Fullscreen</button>
</div>

Ein data-state-Attribut wird an verschiedenen Stellen für Styling-Zwecke verwendet und diese werden mit JavaScript gesetzt. Spezifische Implementierungen werden an geeigneten Stellen unten erwähnt.

Styling

Der resultierende Stil des Videoplayers, der hier verwendet wird, ist eher einfach — das ist beabsichtigt, da es darum geht, zu zeigen, wie ein solcher Videoplayer gestaltet und responsiv gemacht werden kann.

Hinweis: In einigen Fällen wird hier auf einige grundlegende CSS-Beispiele verzichtet, da deren Verwendung entweder offensichtlich oder nicht speziell relevant für das Styling des Videoplayers ist.

Grundlegendes Styling

Der Container der Videosteuerungen benötigt selbst ein gewisses Styling, damit er korrekt eingerichtet ist:

css
.controls {
  display: flex;
  align-items: center;
  overflow: hidden;
  width: 100%;
  height: 2rem;
  position: relative;
}

Die Position wird auf relative gesetzt, was für seine Responsivität erforderlich ist (dazu später mehr).

Wie bereits erwähnt, wird ein data-state-Attribut verwendet, um anzuzeigen, ob die Videosteuerungen sichtbar sind oder nicht, und es benötigt entsprechende CSS-Deklarationen:

css
.controls[data-state="hidden"] {
  display: none;
}

Schaltflächen

Die erste große Styling-Aufgabe besteht darin, die Tasten der Videosteuerung so aussehen zu lassen, als wären sie echte Schaltflächen.

Jede Schaltfläche erhält ein grundlegendes Styling:

css
.controls button {
  width: 2rem;
  height: 2rem;
  text-align: center;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  border: none;
  cursor: pointer;
  color: transparent;
  background-color: transparent;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

Jede Schaltfläche hat eine Breite und Höhe von 2rem. Standardmäßig haben alle <button>-Elemente einen Rahmen, daher wird dieser entfernt. Da Hintergrundbilder verwendet werden, um geeignete Symbole anzuzeigen, wird die Hintergrundfarbe der Schaltfläche auf transparent gesetzt, nicht wiederholt und das Element soll das Bild vollständig enthalten. Darüber hinaus gibt es etwas Beschriftungstext, der auf dem Bildschirm nicht sichtbar sein soll, daher wird die Textfarbe auf transparent gesetzt.

Die :hover- und :focus-Zustände werden dann für jede Schaltfläche eingestellt, die die Deckkraft der Schaltfläche ändert:

css
.controls button:hover,
.controls button:focus {
  opacity: 0.5;
}

Um geeignete Schaltflächenbilder zu erhalten, wurde ein Satz von kostenlosen, häufig verwendeten Steuerelement-Symbolen aus dem Internet heruntergeladen. Jedes Bild wurde dann in einen Base64-kodierten String konvertiert (mithilfe eines Online-Base64 Image Encoder), da die Bilder recht klein sind, sind die resultierenden kodierten Strings ziemlich kurz.

Da einige Schaltflächen eine doppelte Funktionalität haben, z. B. Abspielen/Pause und Stumm/Unstumm, haben diese Schaltflächen unterschiedliche Zustände, die gestylt werden müssen. Wie bereits erwähnt, wird eine data-state-Variable verwendet, um anzuzeigen, in welchem Zustand sich solche Schaltflächen gerade befinden.

Zum Beispiel hat die Abspielen/Pause-Schaltfläche die folgenden Hintergrundbilddefinitionen (die vollständigen Base64-Strings wurden aus Gründen der Kürze weggelassen):

css
.controls button[data-state="play"] {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAA…");
}

.controls button[data-state="pause"] {
  background-image: url("data:image/png;base64,iVBORw0KGgoAAA…");
}

Wenn sich der data-state der Schaltfläche ändert, ändert sich auch das entsprechende Bild. Alle anderen Schaltflächen werden ähnlich behandelt.

Fortschrittsbalken

Der <div>-Container für das <progress>-Element hat sein flex-grow aktiviert, sodass er den verbleibenden Raum in den Steuerungen ausfüllt. Es zeigt auch einen Zeiger-Cursor an, um anzuzeigen, dass es interaktiv ist.

css
.controls .progress {
  flex-grow: 1;
  cursor: pointer;
  height: 80%;
}

Das <progress>-Element hat die folgende grundlegende Stilkonfiguration:

css
.controls progress {
  display: block;
  width: 100%;
  height: 100%;
  border: none;
  color: #0095dd;
  border-radius: 2px;
  margin: 0 auto;
}

Wie die <button>-Elemente hat auch das <progress>-Element einen Standardrahmen, der hier entfernt wird. Es erhält auch eine leicht abgerundete Ecke aus ästhetischen Gründen.

Es gibt einige browserspezifische Eigenschaften, die festgelegt werden müssen, um sicherzustellen, dass Firefox und Chrome die erforderliche Farbe für den Fortschrittsbalken verwenden:

css
.controls progress::-moz-progress-bar {
  background-color: #0095dd;
}

.controls progress::-webkit-progress-value {
  background-color: #0095dd;
}

Obwohl die gleichen Eigenschaften auf den gleichen Wert eingestellt sind, müssen diese Regeln separat definiert werden, da die gesamte Deklaration möglicherweise nicht gültig wird, wenn ein Selektor nicht erkannt wird.

Vollbild

Nun gestalten wir die Steuerungen für den Vollbildmodus. Da das <figure>-Element das Element ist, das im Vollbildmodus angezeigt wird, können wir es mit der :fullscreen-Pseudoklasse ansprechen. Wir machen Folgendes:

  • Lassen Sie die figure den gesamten Bildschirm mit height: 100% einnehmen
  • Lassen Sie die Steuerleiste unten haften, während das Video zentriert bleibt, verwendet wird Flexbox
  • Machen Sie den Container transparent, um die native Hintergrundfarbe anzuzeigen
  • Verbergen Sie das figcaption
  • Stellen Sie die Hintergrundfarbe für die Steuerungsreihe wieder her, um sicherzustellen, dass unsere schwarzen Schaltflächen weiterhin sichtbar sind, wenn der Hintergrund schwarz ist.
css
figure:fullscreen {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  max-width: 100%;
  height: 100%;
  background-color: transparent;
}
figure:fullscreen video {
  margin-top: auto;
  margin-bottom: auto;
}
figure:fullscreen figcaption {
  display: none;
}
figure:fullscreen .controls {
  background-color: #666666;
}

Responsives Styling

Jetzt, da der Player sein grundlegendes Aussehen und Gefühl bekommen hat, müssen einige andere Stiländerungen — unter Verwendung von Media Queries — vorgenommen werden, um ihn responsiv zu machen.

Wir möchten das Layout der Steuerungen anpassen, wenn es auf einem kleineren Bildschirm (680px/42.5em) angezeigt wird, daher wird hier ein Breakpoint definiert. Wir ändern die Größen- und Positions-Eigenschaften für die Schaltflächen und den Fortschrittsbalken, sodass sie anders angeordnet sind:

css
@media screen and (width <= 42.5em) {
  .controls {
    height: auto;
  }

  .controls button {
    width: calc(100% / 6);
    margin-top: 2.5rem;
  }

  .controls .progress {
    position: absolute;
    top: 0;
    width: 100%;
    margin-top: 0;
    height: 2rem;
  }

  .controls .progress progress {
    width: 98%;
  }

  figcaption {
    text-align: center;
  }
}

Der .progress-Container wird jetzt über position:absolute an die Spitze des Steuerungssatzes verschoben, also müssen er und alle Schaltflächen breiter werden. Darüber hinaus müssen die Schaltflächen unter den Fortschrittscontainer geschoben werden, damit sie sichtbar sind.

JavaScript

Das ist wirklich alles für das direkte Styling; die nächste Aufgabe besteht darin, eine Anzahl von JavaScript-Änderungen vorzunehmen, um sicherzustellen, dass alles wie erwartet funktioniert, hauptsächlich um die Logik der Schaltflächen neu zu gestalten.

Abspielen/Pause und Stummschalten

Nachdem die Schaltflächen nun tatsächlich wie Schaltflächen aussehen und Bilder haben, die anzeigen, was sie tun, müssen einige Änderungen vorgenommen werden, damit die "doppelten Funktionen" (wie z.B. die Abspielen/Pause-Schaltfläche) im richtigen "Zustand" sind und das richtige Bild anzeigen. Um dies zu erleichtern, wird eine neue Funktion namens changeButtonState() definiert, die einen Typ-Parameter akzeptiert, der die Funktion der Schaltfläche angibt:

js
function changeButtonState(type) {
  if (type === "play-pause") {
    // Play/Pause button
    if (video.paused || video.ended) {
      playPause.setAttribute("data-state", "play");
    } else {
      playPause.setAttribute("data-state", "pause");
    }
  } else if (type === "mute") {
    // Mute button
    mute.setAttribute("data-state", video.muted ? "unmute" : "mute");
  }
}

Diese Funktion wird dann von den entsprechenden Ereignishandlern aufgerufen:

js
video.addEventListener("play", () => {
  changeButtonState("play-pause");
});

video.addEventListener("pause", () => {
  changeButtonState("play-pause");
});

stop.addEventListener("click", (e) => {
  video.pause();
  video.currentTime = 0;
  progress.value = 0;

  // Update the play/pause button's 'data-state' which allows the
  // correct button image to be set via CSS
  changeButtonState("play-pause");
});

mute.addEventListener("click", (e) => {
  video.muted = !video.muted;
  changeButtonState("mute");
});

Vielleicht haben Sie bemerkt, dass es neue Handler gibt, bei denen auf die play- und pause-Ereignisse des Videos reagiert wird. Dafür gibt es einen Grund! Auch wenn die standardmäßigen Videosteuerungen des Browsers ausgeschaltet sind, machen viele Browser sie über einen Rechtsklick auf das HTML-Video zugänglich. Dies bedeutet, dass ein Benutzer das Video über diese Steuerungen abspielen/pausieren könnte, was dann dazu führen würde, dass die Schaltflächen des benutzerdefinierten Steuerungssatzes nicht synchron sind. Wenn ein Benutzer die Standardsteuerungen verwendet, werden die definierten Media-API-Ereignisse — wie play und pause — ausgelöst, sodass dies genutzt werden kann, um sicherzustellen, dass die benutzerdefinierten Steuerschaltflächen synchron gehalten werden. Unser Klick löst ebenfalls die play- und pause-Ereignisse aus, wenn die play()- und pause()-Methoden aufgerufen werden, sodass sich hier nichts ändern muss:

js
playPause.addEventListener("click", (e) => {
  if (video.paused || video.ended) {
    video.play();
  } else {
    video.pause();
  }
});

Lautstärke

Die Funktion alterVolume(), die aufgerufen wird, wenn die Lautstärketasten des Players angeklickt werden, ändert sich ebenfalls — sie ruft jetzt eine neue Funktion namens checkVolume() auf:

js
function checkVolume(dir) {
  if (dir) {
    const currentVolume = Math.floor(video.volume * 10) / 10;
    if (dir === "+" && currentVolume < 1) {
      video.volume += 0.1;
    } else if (dir === "-" && currentVolume > 0) {
      video.volume -= 0.1;
    }

    // If the volume has been turned off, also set it as muted
    // Note: can only do this with the custom control set as when the 'volumechange' event is raised,
    // there is no way to know if it was via a volume or a mute change
    video.muted = currentVolume <= 0;
  }
  changeButtonState("mute");
}

function alterVolume(dir) {
  checkVolume(dir);
}
volInc.addEventListener("click", (e) => {
  alterVolume("+");
});
volDec.addEventListener("click", (e) => {
  alterVolume("-");
});

Diese neue Funktion checkVolume() tut dasselbe wie alterVolume(), allerdings setzt sie den Zustand der Stummschalttaste abhängig von der aktuellen Lautstärkeeinstellung des Videos. checkVolume() wird auch aufgerufen, wenn das volumechange-Ereignis ausgelöst wird:

js
video.addEventListener("volumechange", () => {
  checkVolume();
});

Fortschritt und Vollbild

Die Implementierungen des Fortschrittsbalkens und des Vollbildmodus haben sich nicht geändert.

Ergebnis

Warnung: Das Beispielvideo kann laut sein!