CSS-Deep-Dive – matrix3d() für eine benutzerdefinierte Bildlaufleiste mit perfekten Frame-Optionen

Benutzerdefinierte Bildlaufleisten kommen selten vor. Das liegt vor allem daran, sind Bildlaufleisten eine der verbleibenden Bits im Web, die so gut wie nicht lesbar (Ich sehe Sie, Datumsauswahl) Sie können JavaScript verwenden, um Ihre eigenen zu erstellen, aber das ist teuer und kostengünstig. und kann verzögert wirken. In diesem Artikel nutzen wir einige unkonventionelle CSS-Matrizen zum Erstellen eines benutzerdefinierten Scrollers, der keine JavaScript beim Scrollen, nur ein Teil des Einrichtungscodes.

Kurzfassung

Die kleinen Dinge sind dir nicht wichtig? Sie sollten sich nur die Nyan Katzen-Demo und die Bibliothek holen? Den Code der Demo finden Sie in der GitHub-Repository.

LAM;WRA (Lang und mathematisch; wird trotzdem lesen)

Vor einiger Zeit haben wir einen Parallaxe-Scroller entwickelt (Haben Sie diesen Artikel? Es ist wirklich lecker und die Zeit wert!). Indem sie Elemente mithilfe von CSS 3D zurückschieben Transformationen sind Elemente langsamer als unsere tatsächliche Scrollgeschwindigkeit.

Zusammenfassung

Wir beginnen mit einer Zusammenfassung der Funktionsweise des Parallaxe-Scrollers.

Wie die Animation zeigt, wurde der Parallaxe-Effekt erzielt, indem Elemente im 3D-Raum entlang der Z-Achse "nach hinten". Durch das Scrollen in einem Dokument Verschiebung entlang der Y-Achse. Wenn wir also um 100 Pixel nach unten scrollen, wird um 100 Pixel nach oben verschoben. Dies gilt für alle Elemente, auch solche, die weiter zurückliegen. Aber weil sie weiter weg von der Kamera ist ihre beobachtete Bewegung auf dem Bildschirm weniger als 100 Pixel, den gewünschten Parallaxe-Effekt erzielen.

Wenn Sie ein Element zurück in den Raum verschieben, erscheint es natürlich auch kleiner, die wir korrigieren, indem wir das Element nach oben skalieren. Wir haben die genaue Mathematik herausgefunden, als wir die Parallaxe Scroller, Deshalb werde ich nicht alle Details wiederholen.

Schritt 0: Was möchten wir tun?

Bildlaufleisten Und genau das werden wir entwickeln. Aber haben Sie jemals wirklich darüber nachgedacht, was sie tun? Habe ich auf jeden Fall nicht. Bildlaufleisten sind ein Indikator für wie viel der verfügbaren Inhalte derzeit sichtbar ist und wie viel Fortschritt die Sie als Leser gemacht haben. Wenn Sie nach unten scrollen, ändert sich auch die Bildlaufleiste zur zeigen Sie an, dass Sie Fortschritte machen. Wenn alle Inhalte im Darstellungsbereich ist die Bildlaufleiste normalerweise ausgeblendet. Hat der Inhalt die doppelte Höhe des Darstellungsbereichs, füllt die Bildlaufleiste die Hälfte der Höhe des Darstellungsbereichs aus. Inhalte mit einer dreifach höheren Höhe skaliert der Darstellungsbereich die Bildlaufleiste auf 1⁄3 des Darstellungsbereichs usw. Sie sehen das Muster. Anstatt zu scrollen, können Sie auch auf die Bildlaufleiste klicken und diese ziehen, um Website schneller zu finden. Das ist überraschend für einen unauffälligen ein solches Element. Kämpfe gegeneinander.

Schritt 1: Umgekehrte Reihenfolge

Mit CSS 3D können wir Elemente langsamer bewegen als die Scrollgeschwindigkeit. wie im Artikel zum Parallaxe-Scrollen beschrieben. Können wir auch die Richtung? Und das ist unser Weg, um ein eine benutzerdefinierte Bildlaufleiste. Um die Funktionsweise zu verstehen, CSS-3D-Grundlagen ein.

Um eine perspektivische Projektion im mathematischen Sinn zu erhalten, müssen Sie werden wahrscheinlich homogenen Koordinaten. Ich werde nicht ins Detail gehen, was sie sind und warum sie funktionieren, aber Sie können sich wie 3D-Koordinaten mit einer zusätzlichen, vierten Koordinate namens w. Dieses -Koordinate sollte 1 sein, es sei denn, Sie möchten eine perspektivische Verzerrung haben. Mi. müssen wir uns nicht um die Details von w kümmern, da wir hier keine einen anderen Wert als 1. Daher sind alle Punkte ab jetzt auf vierdimensionale Vektoren. [x, y, z, w=1], und folglich müssen Matrizen ebenfalls 4 x 4.

Wie Sie sehen, verwendet CSS homogene Koordinaten unter dem definieren Sie Ihre eigenen 4x4-Matrizen in einer Transformationseigenschaft mithilfe des matrix3d(). Für matrix3d sind 16 Argumente erforderlich (da die Matrix 4 x 4), wobei eine Spalte nach der anderen angegeben wird. Mit dieser Funktion können wir Drehungen, Übersetzungen usw. manuell festlegen. macht mit dieser W-Koordinate verunsichert!

Bevor wir matrix3d() verwenden können, benötigen wir einen 3D-Kontext – denn ohne Im 3D-Kontext gibt es keine perspektivischen Verzerrungen. homogenen Koordinaten. Um einen 3D-Kontext zu erstellen, benötigen wir einen Container mit einer perspective und einige Elemente darin, die wir in der neuen einen 3D-Raum erstellt. Für Beispiel:

CSS-Code, der ein div-Element mithilfe der CSS-Eigenschaft
    Perspektive ein.

Die Elemente in einem perspektivischen Container werden von der CSS-Engine verarbeitet wie folgt:

  • Jede Ecke (Scheitelpunkt) eines Elements in homogene Koordinaten umwandeln [x,y,z,w] relativ zum Perspektivcontainer.
  • Alle Transformationen des Elements als Matrizen von rechts nach links anwenden.
  • Wenn das Perspective-Element scrollbar ist, wenden Sie eine Scroll-Matrix an.
  • Die Perspektivenmatrix anwenden.

Die Scroll-Matrix ist eine Verschiebung entlang der y-Achse. Wenn wir zum Beispiel nach unten scrollen, 400 Pixel müssen alle Elemente um 400 Pixel nach oben verschoben werden. Die Perspektivenmatrix ist eine Matrix, die „Pull“ in 3D näher an den Fluchtpunkt zeigt, je weiter zurück die sie sind. Dadurch wird beides verkleinert, wenn sind sie weiter zurückliegend und sorgen außerdem dafür, dass sie beim Übersetzen „langsamer werden“. Wird ein Element also zurückgedrückt, führt eine Verschiebung von 400 Pixel dazu, dass das Element sich nur um 300 Pixel auf dem Bildschirm bewegen.

Wenn du alle Einzelheiten erfahren möchtest, solltest du die Spezifikation der Transformations-Renderingmodell, aber für diesen Artikel habe ich Algorithmus oben.

Die Box befindet sich in einem perspektivischen Container mit dem Wert p für perspective und der Container scrollbar ist und nach unten n Pixel.

Perspektivische Matrix × Scroll-Matrix × Element-Transformationsmatrix
  ergibt 4:4 Identitätsmatrix mit minus eins über p in der vierten Zeile
  in der dritten Spalte multipliziert mit einer Einheitsmatrix 4:4 mit minus n in der zweiten Spalte
  Zeile vierte Spalte multipliziert mit der Elementtransformationsmatrix.

Die erste Matrix ist die Perspektivmatrix, die zweite Matrix die Schriftrolle. Matrixstruktur. Zusammenfassung: Die Aufgabe der Scroll-Matrix besteht darin, ein Element nach oben zu bewegen, wenn wir nach unten scrollen, daher das Minuszeichen.

Bei der Bildlaufleiste soll jedoch das Gegenteil angezeigt werden. nach unten, wenn wir nach unten scrollen. Hier ist ein Trick: Umkehren der w-Koordinate der Ecken der Rechtecke Wenn die w-Koordinate gleich -1 aktivieren, werden alle Verschiebungen in die entgegengesetzte Richtung wirksam. Wie können wir also das? Die CSS-Engine sorgt dafür, dass die Ecken des Felds homogenen Koordinaten und setzt w auf 1. Es ist Zeit, zu matrix3d() zu glänzen!

.box {
  transform:
    matrix3d(
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, -1
    );
}

Diese Matrix führt nichts anderes aus, als w zu negieren. Wenn die CSS-Engine jede Ecke in einen Vektor der Form [x,y,z,1] umgewandelt hat, wird die Matrix konvertieren Sie ihn in [x,y,z,-1].

Vier nach vier Einheitsmatrix mit minus eins über p in der vierten Zeile
  in der dritten Spalte multipliziert mit einer Einheitsmatrix 4:4 mit minus n in der zweiten Spalte
  Zeile vierte Spalte multipliziert mit vier mal vier Identitätsmatrix mit minus eins in der
  vierte Zeile, vierte Spalte multipliziert mit einem vierdimensionalen Vektor x, y, z, 1 gleich vier
  durch vier Identitätsmatrix mit minus eins über p in der vierten Zeile,
  minus n in der zweiten Zeile der vierten Spalte und minus eins in der vierten Zeile
  vierte Spalte ist gleich vierdimensionaler Vektor x, y plus n, z, minus z
  p minus 1.

Ich habe einen Zwischenschritt aufgelistet, um die Auswirkungen der Elementtransformation zu zeigen. Matrixstruktur. Wenn Sie sich nicht mit Matrixberechnungen auskennen, ist das in Ordnung. Die Eureka In der letzten Zeile fügen wir unserem Y- -Koordinate, anstatt sie zu subtrahieren. Das Element wird nach unten verschoben wenn wir nach unten scrollen.

Wenn wir diese Matrix jedoch einfach in unsere Beispiel wird das Element nicht angezeigt. Das liegt daran, dass gemäß der CSS-Spezifikation Vertex mit w < Bei 0 wird das Rendern des Elements blockiert. Und da unsere z -Koordinate ist aktuell 0, p ist 1, w ist aktuell -1.

Glücklicherweise können wir den Wert von z wählen! Um am Ende w=1 zu erhalten, müssen wir um z = -2 festzulegen.

.box {
  transform:
    matrix3d(
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, -1
    )
    translateZ(-2px);
}

Sieh dir an, unsere Box ist zurück!

Schritt 2: Verschieben

Jetzt ist unsere Verpackung da und sieht genauso aus, wie sie ohne Transformationen. Im Moment ist der Perspektivencontainer nicht scrollbar, sehen, aber wir wissen, dass unser Element die andere Richtung einschlagen wird, wenn gescrollt haben. Lasst uns also den Container scrollen, oder? Wir können einfach eine Abstandselement, das Platz belegt:

<div class="container">
    <div class="box"></div>
    <span class="spacer"></span>
</div>

<style>
/* … all the styles from the previous example … */
.container {
    overflow: scroll;
}
.spacer {
    display: block;
    height: 500px;
}
</style>

Und jetzt im Feld scrollen! Das rote Feld befindet sich weiter nach unten.

Schritt 3: Größe festlegen

Wir haben ein Element, das nach unten bewegt wird, wenn die Seite nach unten scrollt. Das ist das Schwierige aus dem Weg räumen. Jetzt müssen wir es so gestalten, dass es wie eine Bildlaufleiste aussieht, um sie interaktiver zu gestalten.

Eine Bildlaufleiste besteht normalerweise aus einem Daumen und einem Titel, während der Track nicht immer sichtbar sind. Die Höhe des Daumens ist direkt proportional dazu, wie viel Inhalte sichtbar sind.

<script>
    const scroller = document.querySelector('.container');
    const thumb = document.querySelector('.box');
    const scrollerHeight = scroller.getBoundingClientRect().height;
    thumb.style.height = /* ??? */;
</script>

scrollerHeight ist die Höhe des scrollbaren Elements, während scroller.scrollHeight ist die Gesamthöhe des scrollbaren Inhalts. scrollerHeight/scroller.scrollHeight ist der Bruchteil des Inhalts, der sichtbar sind. Das Verhältnis des vertikalen Abstands auf die Daumenabdeckungen sollte dem des Verhältnis des sichtbaren Inhalts:

<ph type="x-smartling-placeholder">
</ph> Daumenpunktstil Punkthöhe über scrollerHöhe entspricht Scroller-Höhe
  Über Scroller-Punkt-Scrollhöhe wenn und nur bei Daumenpunkt-Stil Punkthöhe
  gleich Scroller-Höhe mal Scroller-Höhe über Scroller-Punkt-Scrollen
  Höhe.
<script>
    // …
    thumb.style.height =
    scrollerHeight * scrollerHeight / scroller.scrollHeight + 'px';
    // Accommodate for native scrollbars
    thumb.style.right =
    (scroller.clientWidth - scroller.getBoundingClientRect().width) + 'px';
</script>

Die Größe des Daumens beträgt sieht gut aus, aber es bewegt sich viel zu schnell. Hier können wir unsere Technik Parallaxe Scroller. Wird das Element weiter nach hinten verschoben, verlangsamt es sich, während es durch Scrollen. Wir können die Größe korrigieren, indem wir sie vergrößern. Aber wie stark sollten wir genau zurück? Lass uns ein bisschen rechnen – du hast es erraten! Das ist das letzte Mal, dass ich versprochen.

Wichtig ist, dass der untere Rand des Daumens beim vollständigen Scrollen am unteren Rand des scrollbaren Elements liegen Mit anderen Worten: Wenn wir nach links scroller.scrollHeight - scroller.height Pixel, wir möchten, dass unser Daumen so übersetzt von scroller.height - thumb.height. Für jedes Scroller-Pixel Sie möchten, dass sich unser Daumen einen Bruchteil eines Pixels bewegt:

Gleiche Scroller-Punkthöhe minus Daumenpunkthöhe über Scroller
  Scroll-Höhe des Punkts minus Scroll-Punkthöhe.

Das ist unser Skalierungsfaktor. Nun müssen wir den Skalierungsfaktor in einen die wir bereits beim Scrollen der Parallaxe entlang der z-Achse Artikel. Gemäß den entsprechenden Abschnitt in der Spezifikation: Der Skalierungsfaktor ist gleich p/(p − z). Wir können diese Gleichung lösen, wie viel wir unseren Daumen entlang der z-Achse verschieben müssen. Behalten Sie Beachten Sie, dass aufgrund unserer Falschigkeiten mit der Koordinaten-Koordinaten zusätzliche -2px entlang z. Die Transformationen eines Elements werden d. h. alle Übersetzungen vor unserer speziellen Matrix werden invertiert werden, werden jedoch alle Übersetzungen nach unserer speziellen Matrix! Lassen Sie uns Kodifiziere das!

<script>
    // ... code from above...
    const factor =
    (scrollerHeight - thumbHeight)/(scroller.scrollHeight - scrollerHeight);
    thumb.style.transform = `
    matrix3d(
        1, 0, 0, 0,
        0, 1, 0, 0,
        0, 0, 1, 0,
        0, 0, 0, -1
    )
    scale(${1/factor})
    translateZ(${1 - 1/factor}px)
    translateZ(-2px)
    `;
</script>

Wir haben eine Bildlaufleiste ein. Und es ist nur ein DOM-Element, das wir nach Belieben gestalten können. Eine Sache, im Hinblick auf die Barrierefreiheit ist es wichtig, den Daumen so zu gestalten, und ziehen, da viele Nutzende es gewohnt sind, mit einer Bildlaufleiste zu interagieren. Damit der Blogpost nicht noch länger wird, werde ich die Details zu diesem Teil. Werfen Sie einen Blick auf die Bibliothekscode .

Und was ist mit iOS?

Ah, mein alter Freund iOS Safari. Wie beim Parallaxe-Scrollen stoßen wir auch hier auf Problem. Da wir auf einem Element scrollen, müssen wir -webkit-overflow-scrolling: touch, aber das führt zu einer 3D-Abflachung und der gesamten funktioniert nicht mehr. Dieses Problem haben wir mit dem Parallaxe-Scroller gelöst. durch die Erkennung von iOS Safari und die Verwendung von position: sticky als Workaround und machen wir hier genau das Gleiche. Werfen Sie einen Blick auf die Artikel zum Entspannen um Ihr Gedächtnis aufzufrischen.

Was ist mit der Bildlaufleiste im Browser?

Auf einigen Systemen haben wir eine permanente, native Bildlaufleiste. Bisher konnte die Bildlaufleiste nicht ausgeblendet werden (außer kein Standard-Pseudoselektor). Um es zu verstecken, müssen wir uns also auf ein paar (Mathematik-kostenlose) Hackerangriffe umsehen. Wir schließen unsere in einem Container mit overflow-x: hidden freigeben und das Element Scrollelement, das breiter als der Container ist. Die native Bildlaufleiste des Browsers ist jetzt nicht mehr sichtbar.

Fin

Aus dem Gesamtbild können wir nun einen perfekten personalisierten Frame erstellen. Bildlaufleiste wie die in der Nyan Katzen-Demo

Wenn Sie Nyan Cat nicht sehen können, haben Sie einen Fehler, den wir gefunden und protokolliert haben, während Sie diese Demo erstellen (klicken Sie auf den Daumen, damit Nyan Katze angezeigt wird). Chrome ist wirklich gut darin, unnötige Arbeit zu vermeiden. wie z. B. das Malen oder Animieren von Dingen, die sich außerhalb des Bildschirms befinden. Die schlechte Nachricht ist, Aufgrund von Matrixfehlern denkt Chrome, dass das Nyan-Katzen-GIF nicht auf dem Bildschirm zu finden ist. Hoffentlich lässt sich das Problem bald beheben.

Das war es schon. Das war eine Menge Arbeit. Danke, dass Sie die gesamte Ding. Dies sind einige ist das ein echter Trick, und der Aufwand lohnt sich nicht. außer wenn eine benutzerdefinierte Bildlaufleiste wesentlicher Bestandteil der Nutzererfahrung ist. Aber gut zu wissen, dass es möglich ist, oder? Die Tatsache, dass es so schwierig ist, die benutzerdefinierte Bildlaufleiste zeigt, dass auf der Seite des Preisvergleichsportals noch etwas erledigt werden muss. Aber keine Sorge! Künftig werden Sie Houdini AnimationWorklet wird Effekte wie diese, die per Scrollen verlinkt sind, viel einfacher zu gestalten.