Bilder im Internet/Bilder präsentieren

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Meist werden Bilder nicht nur einzeln, sondern zusammen mit anderen Fotos in einer Bildergalerie präsentiert. In einem Museum kannst du an Bildern entlang schlendern und stehen bleiben, wenn dir etwas ins Auge fällt. Im Internet sollte sich das genauso natürlich anfühlen. Die meisten Nutzer surfen heute auf Touch-Geräten, auf denen das Scrollen zur wichtigsten Methode geworden ist, um Inhalte zu erkunden.

In diesem Tutorial erstellen wir einfache, scrollbare Bildergalerien, die auf Smartphones und Tablets flüssig laufen und dennoch genauso gut auf Desktop-Computern funktionieren. Im Gegensatz zu einer Lightbox, bei der ein Bild das andere ersetzt, können Besucher in dieser Galerie frei durch die Bilder scrollen, als würden sie an einer Kunstwand entlanggehen.

Bildergalerie mit Scroll Snap

Ausgangspunkt unserer Bildergalerie ist eine ul-Liste, in deren Listenelementen sich ein img mit einer Referenz auf die Grafik und ein Alternativtext befindet. In Screenreadern werden Elemente einer solchen Liste als „3 von 5“ vorgelesen.

<ul class="gallery">
  <li><img src="Landscape-1.svg" alt="Landschaft in der Toskana"></li>
  <li><img src="Landscape-2.svg" alt="Gebirge - Tagsüber"></li>
  <li><img src="Landscape-2-night.svg" alt="Gebirge - nachts bei Mondschein"></li>
  <li><img src="Landscape-3.svg" alt="Abendstimmung am See"></li>
  <li><img src="Landscape-3-dawn.svg" alt="Morgenstimmung bei Tagesanbruch">
  <li><img src="Seascape.svg" alt="Unterwasser-Landschaft"></li>
  <li><img src="Landscape-4.svg" alt="Karawane in der Wüste"></li>
</ul>

Diese Bildergalerie lässt sich entlang der X-Achse horizontal scrollen. Dafür war früher einiges an JavaScript notwendig - heute benötigt man nur wenige Zeilen CSS:

horizontales Scrollen mit scroll-snap ansehen …
.gallery {
    display: flex;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
}

.gallery img {
    scroll-snap-align: center;
}

Alle Elemente, die sich im Container-Element mit der Klasse gallery befinden, werden nun horizontal angeordnet:

  • display:flex: erzeugt einen flexiblen Container, in dem sich alle Kindelemente (die Bilder) gleichmäßig verteilen
  • overflow-y: hidden: sorgt, dafür, dass außerhalb des Containers befindliche Elemente nicht angezeigt werden.

Für den Scroll Snap benötigen sowohl der Container als auch die scrollenden Elemente jeweils nur eine Festlegung:

  • scroll-snap-type für den Scroll-Container
    • x Das Scrollen soll entlang der X-Achse, also horizontal erfolgen
    • mandatory erzwingt das Einschnappen an der Fangposition
  • scroll-snap-align legt für die Kindelemente fest, wo sich die Fangposition befindet
    • center Die Fangposition befindet sich in der Mitte der Scrollelemente, die Bilder werden nach dem Scroll Snap also zentriert angezeigt.

Scroll-Buttons

In den letzten Monaten gab es immer wieder die Frage, wie man denn anzeigen könnte, dass es verborgene Inhalte außerhalb des Viewports gibt, da die Scrollbalken z.B. im Firefox oder auf Macs standardmäßig ausgeblendet sind.

Könnte man nicht einen Pfeil erzeugen, der auf die Scroll-Richtung hinweist?
Mittlerweile gibt es dafür Pseudoelemente, die allein mit CSS ohne JavaScript erzeugt werden können.

Der ::scroll-button()-Selektor erzeugt ein Pseudoelement als Schaltfläche zur Steuerung des Bildlaufs eines Scroll-Containers. Dieser Button kann mit der Maus, aber auch mit der Tastatur (Tab-Taste) angesteuert und dann mit Leertaste oder Enter ausgelöst werden.

Achtung!

Um dieses Beispiel zu untersuchen, öffnen Sie bitte …
  1. das Beispiel mit einem Klick auf Vorschau in einem neuen Tab!
  2. den Seiteninspektor mit F12!
Pseudoelemente scroll-button() ansehen …
@supports selector(::scroll-button()) {
  .gallery {
    anchor-name: --gallery;
	
  &::scroll-button(*) {
    position-anchor: --gallery;
    position: fixed;
    align-self: anchor-center;
    width: 1.5em;
    background: skyblue;
    border-radius: 1em;
    border: thin solid skyblue;		
  }
  &::scroll-button(*):hover {
    background: gold;	
  }	

  &::scroll-button(left) {
    content: "⬅︎";
    left: calc(anchor(left) + 1em);
  }

  &::scroll-button(right) {
    content: "⮕";
    right: calc(anchor(right) + 1em);
  }		
}

Unsere gallery erhält, wenn mit der @supports-Regel sichergestellt wird, dass der Browser die Pseudoelemente auch wirklich kennt, einen anchor-name.

Alle Scroll-Buttons erhalten nun eine fixe Positionierung mit position-anchor, dessen Wert --gallery auf diesen anchor-name verweist.

Die zwei Buttons erhalten nun über den Parameter die Werte left und right, die aber durch in content notierte Pfeile ersetzt werden. Mit der Maus (oder dem Finger) kann nun weitergeklickt werden.

Beachte: Wenn ein Scroll-Event durch eine Navigation oder durch CSSOM APIs ausgelöst wird, schalten die Fangpositionen sofort um. Dieses Scroll-Verhalten kann mit scroll-behavior: smooth wieder einen weichen Übergang erhalten.

Navigation mit Scroll-Marker

Noch spektakulärer sind die ::scroll-marker. Für jedes Element im Scroll-Container wird ein Pseudo-Element erzeugt, das mit Tastatur und Maus anwählbar ist. Dabei helfen uns weitere CSS-Selektoren, diese Navigation zu gestalten und zu bedienen:

Diese Markierungen können sowohl mit Maus und Touch, als auch mit den Pfeil-Tasten angesteuert werden.
Deshalb sollten die Marker groß genug sein, um bequemes Anklicken mit der Maus zu ermöglichen.

Achtung!

Um dieses Beispiel zu untersuchen, öffnen Sie bitte …
  1. das Beispiel mit einem Klick auf Vorschau in einem neuen Tab!
  2. den Seiteninspektor mit F12!
scroll-marker und scroll-marker-group ansehen …
@supports selector(::scroll-marker) {
  .carousel {
    overflow: hidden;
    anchor-name: --carousel;
    scroll-marker-group: after;

  &::scroll-marker-group {
    display: flex;
    gap: 1em;
    position: fixed;
    position-anchor: --carousel;
    justify-self: anchor-center;
    bottom: calc(anchor(bottom) + 1em);
  }

  li::scroll-marker {
    /* Stylized markers */
    content: "";
    width: 1em;
    aspect-ratio: 1;
    background: white;
    border-radius: 1em;
    border: thin solid skyblue;
  }

  li::scroll-marker:target-current {
    background: skyblue;
  }

Unsere gallery erhält, wenn mit der @supports-Regel sichergestellt wird, dass der Browser die Pseudoelemente auch wirklich erzeugt, eine neue Anordnung:

  • die .gallery wird mit overflow:hidden auf die Größe eines Bildes begrenzt.
  • Für jede Snap Position, also jedes Kind-Element des Scroll-Containers wird ein Marker erstellt, der mit ::scroll-marker gestaltet werden kann.
  • Die gesamte Gruppe kann mit :scroll-marker-group selektiert und positioniert werden.
  • Das aktuell angezeigte Element, bzw. dessen Marker kann über :target-current-selektiert werden.


Beachte: Ich fand es anfangs gewöhnungsbedürftig, dass …
  • das manuelle Scrollen deaktiviert ist, da overflow: hidden den Bereich der Bildlaufleiste ausblendet.
  • eine Steuerung mit den Tab-Tasten nicht möglich ist

::scroll-marker Pseudo-Elemente sind rein visuell und werden vom Browser generiert und verwaltet.
Sie existieren nicht im DOM, sodass JavaScript sie nicht direkt auswählen oder ändern kann – kein querySelector, keine Event-Listener.
Eigentlich sind sie visuelle Alternativen zur Scrollbar.

  • :scroll-marker-group repräsentiert den track;
  • die Marker die snap-positions und
  • :target-current den thumb des Scrollbalkens.

Steuerung mit JavaScript

Achtung!

Um dieses Beispiel zu untersuchen, öffnen Sie bitte …
  1. das Beispiel mit einem Klick auf Vorschau in einem neuen Tab!
  2. den Seiteninspektor mit F12!
Play/Pause und Shuffle ansehen …


Damit die Bilder im Shuffle-Modus nicht von links und rechts chaotisch hereinspringen, wurde scroll-behavior auf auto gesetzt. Die Bilder erscheinen nun plötzlich.

Bildunterschriften

Wenn ein Bild mehr als nur Dekoration ist – also eine Bedeutung hat oder erklärt werden soll – reicht ein einfaches img-Element nicht mehr aus.

Dann hilft das HTML-Element figure. Es gruppiert ein Bild (oder ein Diagramm, eine Tabelle, ein Codebeispiel …) zusammen mit einer passenden Bildunterschrift. Es ist vor allem aus Gründen der Semantik den leider immer noch häufig anzutreffenden Lösungen mit verschachtelten div-Elementen vorzuziehen.

Präsentieren eines Bildes mit figure und figcaption
<figure>
	<img src="lauf-1.jpg" 
         alt="Historisches Stadttor aus Sandstein mit Turm und roten Dächern, umgeben von alten Häusern">
	<figcaption>Lauf an der Pegnitz - Nürnberger Tor<br>
                aufgenommen an einem Sonntagmorgen im Mai
	</figcaption>
</figure>

Als Container für das img-Element und die figcaption dient ein figure-Element.

Das img-Element enthält neben der Referenz auf die Resource einen Alternativtext, der beschreibt, was man auf dem Bild sieht.

In der figcaption gibt es eine zusätzliche Bildunterschrift, die entweder den Titel oder einen Kommentar enthält.


Mehrere Bilder nebeneinander

Während in Fließtexten oft ein einzelnes Bild mittels float integriert ist, will man häufig mehrere Bilder nebeneinander präsentieren.

Das figure-Element kann auch verschachtelt werden, um sowohl der Galerie, als auch den einzelnen Fotos Bildunterschriften zu geben.

Präsentieren mehrerer Bilder mit verschachtelten figure-Elementen ansehen …
<figure id="gallery">
  <figcaption>Ansichten von Lauf an der Pegnitz</figcaption>

  <figure>
    <img src="//wiki.selfhtml.org/images/a/a6/Lauf-1.jpg" 
         alt="Historisches Stadttor aus Sandstein mit Turm und roten Dächern, umgeben von alten Häusern">
    <figcaption>Nürnberger Tor</figcaption>
  </figure>

  <figure>
    <img src="//wiki.selfhtml.org/images/4/4a/Lauf-2.jpg" 
         alt="gedeckte Holz-Brücke über einen Fluss; Gärten am Flussufer">
    <figcaption>Brücke über die Pegnitz</figcaption>
  </figure>

  <figure>
    <img src="//wiki.selfhtml.org/images/2/24/Lauf-3.jpg" 
         alt="leerer Biergarten mit historischen Gebäuden im Hintergrund">
    <figcaption>Mauermühle und Judenturm</figcaption>
  </figure>
</figure>
CSS: Abstände auf 0 setzen
figure, figcaption {
 margin: 0;
 padding: 0;

}

Untersuche das Beispiel im Frickl (oder mit den Entwickler-Werkzeugen) und entferne diese Festlegung für margin.

Um die einzelnen Bilder nebeneinander zu positionieren, gibt es mehrere Möglichkeiten in CSS:

  • Die figure-Elemente sind Block-Elemente, die mit display: inline-block; nebeneinander positioniert werden können
    → dafür müssen sie dann passende Breiten und Abstände erhalten.
  • ein ähnlicher Effekt ist mit flexbox möglich. Hier kann man mit justify-content noch bestimmen, wie ein einzelnes Kindelement in der letzten Zeile positioniert wird.
  • mit Grid Layout wird das Elternelement zum Grid Container. Es kann eine feste Spaltenanzahl festgelegt werden, es ist aber auch möglich Raster implizit zu erzeugen, die durch den auto-placement-Algorithmus Rasterelemente automatisch auf den verfügbaren Platz verteilen.
Bilder nebeneinander platzieren
#gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15em, 1fr));
  gap: 1em;
}

#gallery > figcaption {
  grid-column: 1 / -1;
}

Um im CSS ein figure figure zu vermeiden, geben wir der Bildergalerie eine ID mit Namen gallery.

Folgende Einstellungen sind wichtig:

  • display: grid: erzeugt das Raster
  • grid-template-columns: repeat(auto-fill, minmax(15em, 1fr));: erzeugt Rasterlemente, die zwischen 15em und einem Bruchteil des verfügbaren Platzes breit sind.
  • gap: 1em;: erzeugt einen Abstand zwischen den Rasterelementen, aber nicht außen.

Einziges Problem ist die figcaption der gesamten Galerie. Ohne weitere Festlegungen würde sie das erste Rasterlement links neben dem ersten Bild einnehmen. Mit …

  • grid-column: 1 / -1; erstreckt es sich aber über die gesamte Breite von der ersten bis zur letzten (-1 = von rechts gezählt) Rasterlinie

Zentrierte Bildunterschriften daneben

Häufig sollen die Bildunterschriften neben dem Foto mittig zentriert werden - auch dies ist mit Grid Layout möglich:

figure und figcaption nebeneinander - vertikal zentriert ansehen …
@media (width > 45em) {
#gallery {


  figure {
      display: grid;
      grid-template-columns: 1fr 1fr;
      align-items: center; 
      gap: 1em; 
    }
  }
}

Lazy Loading

Lazy Loading bedeutet, dass Bilder (oder andere Ressourcen) nur dann geladen werden, wenn sie tatsächlich benötigt werden – beispielsweise wenn sie beim Scrollen der Seite sichtbar werden. [1]

Normalerweise lädt ein Browser alle Bilder, sobald die Seite geöffnet wird. Bei Lazy Loading werden Bilder außerhalb des Viewports durch Platzhalter ersetzt und später geladen. Moderne Browser unterstützen dies mit dem loading-Attribut – es ist kein zusätzliches JavaScript erforderlich.

Lazy Loading
<img src="img/cabinet.svg" alt="Kommode" loading="lazy" width="200" height="200">
  ...

Mit loading="lazy" wird festgelegt, dass Medien außerhalb des sichtbaren Bereichs erst geladen werden, wenn der Benutzer scrollt. Zusätzlich sollte mit einer Breitenangabe der Patz für das Bild reserviert werden, damit die Seite nicht immer wieder neu gerendert werden muss.


Das alles passiert ohne weiteres Zutun von CSS und JavaScript im Browser.

Trotzdem sollen die Bilder im nächsten Beispiel erst beim Scrollen erscheinen.[2] [3] (Wenn du die beiden letzten Beispiele ausprobiert hast, liegen die Grafiken ja bereits im Cache.)

einschwebende Bilder mit view() ansehen …
		@keyframes reveal {
			from {
				opacity: 0;
				clip-path: inset(45% 20% 45% 20%);
			}
			to {
				opacity: 1;
				clip-path: inset(0% 0% 0% 0%);
			}
		}

		.image-reveal {
			animation: linear reveal both;
			animation-timeline: view();
			animation-range: entry 25% cover 50%;
		}

Wenn ein Bild in den sichtbaren Bereich scrollt, wird es allmählich eingeblendet und aus der Mitte heraus freigegeben – als würde es durch ein sich vergrößerndes Fenster sichtbar werden.

  • animation reveal verändert zwei Eigenschaften:
    • die opacity 0 → 1 blendet das Bild ein.
    • clip-path beschneidet das Bild anfangs, sodass es beim Scorllen zur vollen Größe wächst
  • animation-timeline ruft die view()-Funktion auf, die überwacht, wenn das Element in den sichtbaren Bereich gescrollt wird.
  • animation-range legt den Bereich fest, ab dem die Animation ausgeführt werden soll.


Siehe auch

Zoom bei img:hover?

Ich möchte gerne, dass Bilder durch Überfahren der Maus in Großdarstellung angezeigt werden.

Diese Aufgabenstellung vernachlässigt zwei Aspekte bei Bildergalerien:

  • Auf mobilen Geräten ist der Viewport so klein, dass auf eine Vorschauansicht verzichtet werden kann.
  • Ein Bild sollte sowohl auf mobilen Touchgeräten ohne Tastatur als auch mit der Tastatur, z.B mit der Tab-Taste ( ), ausgewählt werden können.

Während solche Großansichten früher nur mit JavaScript oder jQuery verwirklicht wurden, kamen mit CSS3 und CSS-animation scheinbar einfache CSS-Lösungen auf. Diese stießen aber immer wieder auf Probleme:

  • Bilder können mit :hover auf Mausberührungen reagieren, aber nicht auf ein „Durchtabben“ mit der Tastatur.
  • Bilder, die durch :hover oder Klick in einer Großansicht dargestellt werden, ragen teilweise aus dem Viewport hinaus und sind so nicht sichtbar. Bei heute verpönten, pixelgenauen Layouts mag man mit absoluter Positionierung eine Feinjustierung vornehmen können, auf Mobilgeräten wird dies schnell zur Fummelei.
    Mit JavaScript und der Intersection Observer API könnte man überprüfen, ob sich ein HTML-Element im Viewport befindet und wenn nicht - es entsprechend verschieben.)


Screenshot von mit :hover vergrößerten Bildern, die sich außerhalb des Viewports befinden
Screenshot von mit :hover vergrößerten Bildern, die sich teilweise außerhalb des Viewports befinden

Bilderzoom mit Grid Layout

Dieses Beispiel von Temani Afif[4] ordnet 9 Bilder in einem 3x3-Grid an. Jedes figure-Element erhält ein tabindex-Attribut, damit es antabbar wird.

Mit :hover oder beim Durchtabben wird das aktive Bild gezoomt und die Bildunterschrift wird sichtbar:

Schritt 1: ein Grid ansehen …
.gallery {
  --size: 10em; /* control the size */
  --gap: 1em;  /* control the gap */
  --zoom: 2;   /* control the scale factor */
  
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(3,auto);
  width: calc(3*var(--size) + 2*var(--gap));
  aspect-ratio: 1;

}

Das Element mit der Klasse gallery erhält mit grid-template-columns drei Spalten ohne Breitenangabe. Diese wird über die width festgelegt, deren Wert mit der calc()-Funktion aus den custom properties für --size und --gap berechnet wird. Mittels der aspect-ratio-Eigenschaft wird diese Breite auch für die Höhe der Galerie übernommen.

Das gezoomte Bild ist nie breiter als die Galerie und bleibt somit immer im Viewport!


Schritt 2: Bilderzoom ansehen …
.gallery figure {
  margin: 0;
  padding: 0;
  position: relative;
}
.gallery > figure img {
  width: 0;
  height: 0;
  min-height: 100%;
  min-width: 100%;
  object-fit: cover;
  cursor: pointer;
  filter: grayscale(80%);
  transition: .35s linear;
}

.gallery > figure:hover img,
.gallery > figure:focus img{
  filter: grayscale(0);
  width:  calc(var(--size)*var(--zoom));
  height: calc(var(--size)*var(--zoom));
}

.gallery figcaption {
	position: absolute;
  	bottom: 0;
	left: 0;
	text-align: center;
	width: 100%;
  	opacity: 0;
	color: white;
	background: rgb(0 0 0 / 0.3);	
}

.gallery figure:hover figcaption,
.gallery figure:focus figcaption {
  opacity: 1;
}

Im Originalzustand füllen alle Bilder ihr jeweiliges figure-Element aus. Mit der CSS filter-Eigenschaft wird ein Grauschleier grayscale(.8) über die Bilder gelegt. Die Figcaption erhält einen dunklen Hintergrund und weiße Schrift, bleibt durch opacity: 0 aber unsichtbar.

Bei :hover oder dem Antabben wird nun das Bild mit grayscale(0) wieder farbig; die Breite und Höhe werden über den --zoom-Wert vergrößert. Die Änderung der Eigenschaftswerte erfolgt mit transition als weicher Übergang mit einer Dauer von 0.35s.

Auch die Figcaption wird nun eingeblendet.

Weblinks

  1. MDN: Lazy loading - Web Performance
  2. Animate elements on scroll with Scroll-driven animations Bramus, 05.05.2023 (developer.chrome.com)
  3. Enhancing HTML 5 Lazy Loading With CSS and Minimal JavaScript von Jason Knight, Oct 6, 2020
  4. Zooming Images in a Grid Layout von Temani Afif, 8. Aug 2022

CSS-Grid-and-Custom-Shapes.jpg

Temani Afif zeigt auf css-tricks, wie man interessante Formen mit Grid Layout zu geometrischen Mustern anordnet:

CSS Grid and Custom Shapes, Part 1 von Temani Afif, 15.08.2022

Slanted-images-by-eric-meyer.png

Eric Meyer zeigt, wie man mit clip-path Bilder anschrägt und nebeneinander positionieren kann:

Flexible Captioned Slanted Images von Eric Meyer (24ways.org)