Heim > Web-Frontend > js-Tutorial > So implementieren Sie ein reibungsloses Scrollen in Vanille JavaScript

So implementieren Sie ein reibungsloses Scrollen in Vanille JavaScript

William Shakespeare
Freigeben: 2025-02-18 10:49:09
Original
507 Leute haben es durchsucht

How to Implement Smooth Scrolling in Vanilla JavaScript

Kernpunkte

  • Verwenden Sie die Jump.js -Bibliothek, um native JavaScript -sanfte Scrolling zu implementieren und die Scrollenanimation ohne externe Abhängigkeiten zu vereinfachen.
  • Jump.js ursprünglicher Code ändern, um ihn von ES6 in ES5 umzuwandeln, um eine breitere Kompatibilität mit verschiedenen Browsern zu gewährleisten.
  • Verwenden Sie die Methode requestAnimationFrame, um reibungslose Animationsaktualisierungen durchzuführen, die Leistung zu optimieren und eine reibungslosere Benutzererfahrung zu bieten.
  • Implementieren Sie benutzerdefiniertes JavaScript, um das Standardverhalten des Standard-In-Page-Links abzufangen und plötzliche Sprünge durch reibungslose Bildlaufanimationen zu ersetzen.
  • Integrierte CSS scroll-behavior Eigenschaften zur Unterstützung des nativen reibungslosen Bildlaufs in Browsern, die diese Funktion erkennen, und der JavaScript -Fallback -Mechanismus wird bereitgestellt, wenn der Browser es nicht unterstützt.
  • Sicherstellen Sie die Zugänglichkeit, indem Sie sich nach dem Scrollen auf Zielelemente konzentrieren, potenzielle Probleme mit der Tastaturnavigation behandeln und die Verwendbarkeit für alle Benutzer verbessern.

Dieser Artikel wurde von Adrian Sandu, Chris Perry, Jérémy Heleine und Mallory Van Achterberg überprüft. Vielen Dank an alle Peer -Rezensenten von SitePoint, um den besten Inhalt in SitePoint zu erhalten!

Smooth Scrolling ist ein Benutzeroberflächen-Modus, der das Standarderlebnis für die In-Page-Navigation allmählich verbessert und die Position innerhalb des Scroll-Feld im Clip angegeben.

Dies ist nichts Neues und ist seit Jahren ein bekanntes Muster. Schauen Sie sich beispielsweise diesen SitePoint -Artikel aus dem Jahr 2003 an! Übrigens ist dieser Beitrag historisch wertvoll, da er zeigt, wie sich die kundenseitige JavaScript-Programmierung, insbesondere DOM, im Laufe der Jahre verändert und weiterentwickelt hat, was die Entwicklung einfacher native JavaScript-Lösungen ermöglicht.

Im Jquery-Ökosystem gibt es viele Implementierungen dieses Musters, die direkt mit JQuery oder Plug-Ins implementiert werden können. In diesem Artikel interessieren wir uns jedoch für reine JavaScript-Lösungen. Insbesondere werden wir die Jump.js -Bibliothek erforschen und nutzen.

Nachdem wir einen Überblick über die Bibliothek und ihre Funktionen und Funktionen eingeführt haben, werden wir einige Änderungen am ursprünglichen Code vor unseren Anforderungen vornehmen. Dabei werden wir einige Kern -JavaScript -Sprachkenntnisse wie Funktionen und Schließungen überprüfen. Wir erstellen dann eine HTML -Seite, um das reibungslose Bildlaufverhalten zu testen und es dann als benutzerdefiniertes Skript zu implementieren. Die Unterstützung des nativen reibungslosen Scrollens in CSS wird dann (falls verfügbar) hinzugefügt, und schließlich werden wir einige Beobachtungen zur Historie der Browser -Navigation machen.

Dies ist die letzte Demonstration, die wir erstellen werden:

Smooth Scrolling Stift für SitePoint (@sinepoint) auf CodePen anzeigen.

Der vollständige Quellcode kann auf GitHub gefunden werden.

sprung.js

sprung.js ist in nativem ES6 -JavaScript geschrieben und hat keine externen Abhängigkeiten. Es handelt sich um ein kleines Dienstprogramm mit nur etwa 42 SLOC, aber das Minimierungspaket hat eine Größe von etwa 2,67 kb, wie es übersetzt werden muss. Auf der GitHub -Projektseite wird eine Demo bereitgestellt.

Wie der Name schon sagt, liefert es nur Sprünge: Die Animationsänderungen der Bildlaufleistenposition von ihrem aktuellen Wert zur Zielposition, angegeben durch Bereitstellung von Entfernungen in Form eines DOM -Elements, eines CSS -Selektors oder positiv oder negativ oder negativ Numerischer Wert. Dies bedeutet, dass wir bei der Implementierung des reibungslosen Scroll -Modus Link -Entführungen durchführen müssen. Weitere Informationen finden Sie im Abschnitt unten.

Bitte beachten Sie, dass derzeit nur vertikales Scrollen des Ansichtsfenpfels unterstützt wird.

Wir können Sprünge mit einigen Optionen konfigurieren, z. B. Dauer (dieser Parameter ist erforderlich), die Lockerungsfunktion und Rückrufe, die am Ende der Animation ausgelöst werden. Wir werden ihre praktische Anwendung später in der Demo sehen. In der Dokumentation finden Sie vollständige Details.

Jump.js läuft ohne Probleme mit "modernen" Browsern, einschließlich Internet Explorer Version 10 oder höher. Weitere Informationen finden Sie in der Dokumentation für eine vollständige Liste unterstützter Browser. Mit dem entsprechenden RequestAnimationFrame -Polyfill kann es sogar auf älteren Browsern ausgeführt werden.

verstehen Sie schnell hinter dem Bildschirm

Innen verwendet der Quellcode Jump.js die RequestAnimationFrame -Methode des Fensterobjekts, um die Position der vertikalen Position des Ansichtsfensters zu ordnen, die in jedem Frame der Scroll -Animation aktualisiert wurde. Dieses Update wird erreicht, indem der nächste Positionswert über die Locker -Funktion an das Fenster übergeben wird. Ausführliche Details finden Sie im Quellcode.

Einige Anpassungen

Wir werden einige geringfügige Änderungen am Originalcode vornehmen, bevor wir uns mit der Demo befassen, um zu zeigen, wie sprung.js verwendet wird, aber das wird nicht ändern, wie es intern funktioniert.

Der Quellcode ist in ES6 geschrieben und muss mit JavaScript -Build -Tools verwendet werden, um Module zu übersetzen und zu bündeln. Dies kann für einige Projekte ein bisschen zu viel sein, daher werden wir einige Refactoring anwenden, um den Code in ES5 für die Verwendung überall umzuwandeln.

Entfernen wir zuerst die ES6 -Syntax und die Funktionen. Das Skript definiert eine ES6 -Klasse:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Wir können es mit Konstruktoren und einer Reihe von Prototypmethoden in ES5 -Klassen konvertieren. Beachten Sie jedoch, dass wir nie mehrere Instanzen dieser Klasse benötigen. Ein Singleton, das mit einem normalen Objektliteral implementiert ist

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Zusätzlich zum Löschen der Klasse müssen wir einige andere Änderungen vornehmen. Der Rückruf von

wird verwendet, um die Bildlaufleisteposition in jedem Frame zu aktualisieren. In dem ursprünglichen Code wird er über die ES6-Pfeilfunktion aufgerufen, die bei der Initialisierung vor dem Sprung-Singleton vorgebunden sind. Anschließend bündeln wir die Standard -Locker -Funktion in derselben Quelldatei. Schließlich wickeln wir den Code mit iife (sofort Ausdruck von Aufruffunktion) ein, um die Verschmutzung der Namespace zu vermeiden. requestAnimationFrame

Jetzt können wir einen weiteren Rekonstruktionsschritt anwenden. Beachten Sie, dass wir mit Hilfe verschachtelter Funktionen und Schließungen nur Funktionen anstelle von Objekten verwenden können:

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Singleton ist jetzt eine Sprungfunktion, die auf die Animal Scrolling, Loop- und End -Rückrufe zu verschachtelten Funktionen aufgerufen wird, und die Eigenschaften des Objekts sind jetzt eine lokale Variable (Schließung). Wir brauchen IIfe nicht mehr, denn jetzt ist der gesamte Code sicher in eine Funktion eingewickelt.

Wie der endgültige Refactoring -Schritt, um wiederholte Zeitstart -Reset -Überprüfungen jedes Mal, wenn der Loop -Rückruf aufgerufen wird Schleifenfunktion Reset TimerStart Variable:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Beachten Sie erneut, dass sich der Core -Scroll -Animationscode während des Rekonstruktionsprozesses nicht geändert hat.

Testseite

Jetzt, da wir das Skript an unsere Bedürfnisse angepasst haben, sind wir bereit, eine Testdemo zusammenzustellen. In diesem Abschnitt werden wir eine Seite schreiben, die das sanfte Scrollen mit den im nächsten Abschnitt beschriebenen Skripten verbessert.

Diese Seite enthält eine Inhaltsverzeichnis (TOC), die auf Links innerhalb der Seite in den nachfolgenden Abschnitten des Dokuments sowie auf andere Links zum TOC verweist. Wir werden auch einige externe Links zu anderen Seiten mischen. Dies ist die Grundstruktur dieser Seite:

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Am Kopf werden wir einige CSS -Regeln einfügen, um das einfachste Layout festzulegen, und am Ende des Body -Tags werden wir zwei JavaScript -Dateien einbeziehen Ist es das Skript, das wir jetzt diskutieren werden?

Hauptskript

Dies ist ein Skript, das das Scrolling -Erlebnis der Testseite mit animierten Sprüngen aus unserer individuellen Version von Jump.js Library verbessert. Natürlich wird dieser Code auch in ES5 JavaScript geschrieben.

Lassen Sie uns einen kurzen Überblick über das, was es tun sollte Der Link und ersetzen Sie ihn durch einen Anruf auf unsere Sprung () -Funktion. Daher müssen Sie zunächst die Klicks auf die Links auf der Seite überwachen. Wir können dies auf zwei Arten tun, indem wir Ereignisdelegierte verwenden oder Handler an jedem verwandten Link anhängen.

Ereigniskommission

In der ersten Methode fügen wir den Klick -Listener einem Element -Dokument hinzu. Auf diese Weise sprudelt jedes Klickereignis eines Elements auf der Seite die Zweige seines Vorfahren in den Dom -Baum, bis es das Dokument erreicht.

Natürlich müssen wir im registrierten Ereignishörer (Onclick) das Ziel des eingehenden Klick -Ereignisobjekts überprüfen, ob es sich um das Link -Element auf der Seite bezieht. Dies kann auf verschiedene Arten erfolgen, sodass wir sie als Helferfunktion isinpagelink () abstrahieren. Wir werden den Mechanismus dieser Funktion später betrachten.

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Wenn sich der eingehende Klick auf dem In-Page-Link befindet, stoppen wir die Ereignisblase und blockieren die zugehörige Standardaktion. Schließlich nennen wir die Sprungfunktion und stellen sie mit dem Hash -Selektor des Zielelements und den Parametern zur Konfiguration der erforderlichen Animation bereit.

Dies ist der Ereignishandler:

Single -Handler

<code>requestAnimationFrame(function(time) { timeStart = time; loop(time); });

function loop(time) {
    timeElapsed = time - timeStart;

    window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

    if (timeElapsed         requestAnimationFrame(loop)
    else
        end();
}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Verwenden Sie die zweite Methode, um Linkklicks zu überwachen und die leicht modifizierte Version des oben beschriebenen Ereignishandlers an das Link -Element auf jeder Seite zu beenden. Es gibt also keine Ereignisblase:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Wir fragen alle Elemente ab und verwenden den Trick [] .slice (), um den zurückgegebenen Dom NodeList in ein JavaScript -Array umzuwandeln (eine bessere Alternative, wenn der Zielbrowser es unterstützt, ES6 -Array.from () Verfahren). Wir können dann die Array -Methode verwenden, um die Links auf der Seite zu filtern, die oben definierte Helferfunktion wiederzuverwenden und den Hörer schließlich an die verbleibenden Link -Elemente hinzufügen.

Ereignishandler ist fast der gleiche wie zuvor, aber natürlich müssen wir das Klickziel nicht überprüfen:

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Welche Methode ist am besten vom Gebrauchskontext abhängt. Wenn beispielsweise neue Link -Elemente dynamisch nach dem Laden der Anfangsseite hinzugefügt werden können, müssen wir Ereignisdelegierte verwenden.

Jetzt wenden wir uns der Implementierung von isinpagelink () zu, die wir diese Helferfunktion in unserem vorherigen Ereignis-Handler für abstrakte Tests von einseitigen Links verwendeten. Wie wir sehen können, nimmt diese Funktion einen DOM-Knoten als Parameter ein und gibt einen booleschen Wert zurück, um anzuzeigen, ob der Knoten ein In-Page-Verbindungselement darstellt. Es reicht nicht aus, nur zu überprüfen, ob der übergebene Knoten ein A -Tag ist und dass Hash -Fragment festgelegt ist, da der Link möglicherweise auf eine andere Seite zeigt. In diesem Fall darf die Standard -Browser -Aktion nicht deaktiviert werden. Daher prüfen wir, ob der in der Eigenschaft gespeicherte Wert "minus" Hash -Fragment href gleich der Seiten -URL ist:

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

striphash () ist eine weitere Helferfunktion, mit der wir auch den Wert des variablen Pageurl festlegen, wenn das Skript initialisiert wird:

<code>requestAnimationFrame(function(time) { timeStart = time; loop(time); });

function loop(time) {
    timeElapsed = time - timeStart;

    window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

    if (timeElapsed         requestAnimationFrame(loop)
    else
        end();
}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Diese String-basierte Lösung und das Beschneiden von Hash-Fragmenten funktionieren auch bei URLs mit Abfragebrägern einwandfrei, da die Hash-Teile in der allgemeinen Struktur der URL hinter ihnen stehen.

Wie ich bereits sagte, ist dies nur eine mögliche Möglichkeit, diesen Test zu implementieren. Der zu Beginn dieses Tutorials zitierte Artikel verwendet beispielsweise eine andere Lösung, um Vergleiche auf Komponentenebene mit Link-HREFs mit Standortobjekten durchzuführen.

Es ist zu beachten, dass wir diese Funktion in beiden Ereignisabonnementmethoden verwenden, aber in der zweiten Methode verwenden wir sie als Filter für Elemente, von denen wir bereits wissen überflüssig. Dies bleibt dem Leser als Übung überlassen.

Überlegungen zur Zugänglichkeit

Für den Moment ist unser Code anfällig für bekannte Fehler (eigentlich ein Paar nicht verwandter Fehler, die sich auf Blink/Webkit/KHTML auswirken, und eine, die IE betrifft), die Tastaturbenutzer betreffen. Beim Durchsuchen der TOC -Link über die Registerkarte Taste scrollen Sie eine Link reibungslos zum ausgewählten Abschnitt nach unten, aber der Fokus bleibt auf dem Link. Dies bedeutet, dass beim Drücken der nächsten Registerkarte der Benutzer anstelle des ersten Links im Abschnitt seiner Wahl an den TOC zurückgesandt wird.

Um dieses Problem zu lösen, werden wir dem Hauptskript eine weitere Funktion hinzufügen:

<code>>
    <h1>></h1>Title>
    <nav> id="toc"></nav>
        <ul>></ul>
            <li>></li>
<a> href="https://www.php.cn/link/db8229562f80fbcc7d780f571e5974ec"></a>Section 1>>
            <li>></li>
<a> href="https://www.php.cn/link/ba2cf4148007ed8a8b041f8abd9bbf96"></a>Section 2>>
            ...
        >
    >
     id="sect-1">
        <h2>></h2>Section 1>
        <p>></p>Pellentesque habitant morbi tristique senectus et netus et <a> href="https://www.php.cn/link/e1b97c787a5677efa5eba575c41e8688"></a>a link to another page> ac turpis egestas. <a> href="https://www.php.cn/link/e1b97c787a5677efa5eba575c41e8688index.html#foo"></a>A link to another page, with an anchor> quam, feugiat vitae, ...>
        <a> href="https://www.php.cn/link/7421d74f57142680e679057ddc98edf5"></a>Back to TOC>
    >
     id="sect-2">
        <h2>></h2>Section 2>
        ...
    >
    ...
     src="jump.js">>
     src="script.js">>
>
</code>
Nach dem Login kopieren
Es wird in dem Rückruf ausgeführt, in dem wir an die Sprungfunktion übergeben und den Hash des Elements übergeben werden, das wir in der Vergangenheit scrollen möchten:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Die Funktion dieser Funktion besteht darin, das DOM -Element zu erhalten, das dem Hash -Wert entspricht, und zu testen, ob es sich bereits um ein Element handelt, das Fokus empfangen kann (z. B. ein Anker- oder Schaltflächenelement). Wenn das Element standardmäßig keinen Fokus empfangen kann (z. B. unserem Container), legt es seine Tabindex -Eigenschaft auf -1 fest (ermöglicht es programmgesteuert, fokussierte Fokus, jedoch nicht über die Tastatur). Der Fokus wird dann auf dieses Element eingestellt, was bedeutet, dass der nächste -Ristentaste des Benutzers den Fokus auf den nächsten verfügbaren Link verschiebt. Sie können den vollständigen Quellcode des Hauptskripts hier anzeigen, wobei alle Änderungen zuvor diskutiert wurden.

Unterstützen Sie den nativen glatten Scrollen mit CSS

Die Spezifikation des CSS -Objektmodell -Ansichtsmoduls führt eine neue Eigenschaft ein, um reibungsloses Scrollen nativ zu implementieren:

.

scroll-behavior Es kann zwei Werte annehmen,

die Standard -Instantane -Scroll repräsentiert und

die Animationsrolle repräsentiert. Diese Spezifikation bietet keine Möglichkeit, Scroll -Animationen wie die Dauer- und Zeitfunktionen (Lockerung) zu konfigurieren. auto smooth Kann ich CSS-Scroll-Verhalten verwenden? Daten von caniuse.com zeigen die Unterstützung von CSS-S-CROLL-Verhaltensfunktionen durch große Browser.

zum Zeitpunkt des Schreibens ist die Unterstützung leider sehr begrenzt. In Chrome befindet sich diese Funktion in der Entwicklung und kann teilweise implementiert werden, indem sie im Bildschirm von Chrome: // Flags aktiviert werden. Die CSS -Eigenschaft wurde noch nicht implementiert, daher funktioniert das reibungslose Scrollen auf Linkklicks nicht.

Wie auch immer, indem wir kleine Änderungen am Hauptskript vornehmen, können wir feststellen, ob diese Funktion im Benutzeragenten verfügbar ist, und vermeiden Sie es, den Rest unseres Codes auszuführen. Um das sanfte Scrollen im Ansichtsfenster zu verwenden, wenden wir das CSS -Attribut auf das HTML -Element des Stammelements an (aber auf unserer Testseite können wir es sogar auf das Körperelement anwenden):

Dann fügen wir zu Beginn des Skripts einen einfachen funktionalen Erkennungstest hinzu:
<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Wenn der Browser native Scrolling unterstützt, wird das Skript nichts bewirken und beendet sich ansonsten weiter wie zuvor und der Browser ignoriert nicht unterstützte CSS -Eigenschaften.
<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Schlussfolgerung

Abgesehen von der Einfachheit und Leistung implementiert, ist ein weiterer Vorteil der CSS -Lösung, die gerade diskutiert wurde, dass das Verhalten des Browser -Verlaufs mit dem Verhalten übereinstimmt, das bei der Verwendung der Standard -Scrolling des Browsers erlebt wird. Jeder auf dem Seite stehende Sprung wird zum Browser-Verlaufsstapel gedrückt, und wir können diese Sprung mit den entsprechenden Tasten hin und her durchsuchen (aber zumindest gibt es kein glattes Scrollen in Firefox).

In dem von uns geschriebenen Code (wir können es jetzt als Fallback -Schema betrachten, wenn CSS -Unterstützung nicht verfügbar ist), haben wir das Verhalten von Skripten in Bezug auf die Browsergeschichte nicht in Betracht gezogen. Abhängig vom Kontext und des Anwendungsfalles kann dies etwas von Interesse sein oder nicht. Wenn wir jedoch der Meinung sind, dass Skripte die Standard -Scroll -Erfahrung verbessern sollten, sollten wir wie CSS genau wie CSS erwarten.

FAQs (FAQs) beim glatten Scrollen mit nativem JavaScript

Wie kann man reibungsloses Scrollen mit nativem JavaScript erreichen, ohne Bibliotheken zu verwenden?

Verwenden Sie natives JavaScript, um ein reibungsloses Scrollen zu erreichen, ohne dass Bibliotheken verwendet werden. Sie können die Methode window.scrollTo verwenden und die Option behavior auf smooth festlegen. Diese Methode dokumentiert im Fenster um eine bestimmte Anzahl von Male. Hier ist ein einfaches Beispiel:

<code>import easeInOutQuad from './easing'

export default class Jump {
  jump(target, options = {}) {
    this.start = window.pageYOffset

    this.options = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    }

    this.distance = typeof target === 'string'
      ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
      : target

    this.duration = typeof this.options.duration === 'function'
      ? this.options.duration(this.distance)
      : this.options.duration

    requestAnimationFrame(time => this._loop(time))
  }

  _loop(time) {
    if(!this.timeStart) {
      this.timeStart = time
    }

    this.timeElapsed = time - this.timeStart
    this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

    window.scrollTo(0, this.next)

    this.timeElapsed       ? requestAnimationFrame(time => this._loop(time))
      : this._end()
  }

  _end() {
    window.scrollTo(0, this.start + this.distance)

    typeof this.options.callback === 'function' && this.options.callback()
    this.timeStart = false
  }
}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

In diesem Beispiel klicken Sie in diesem Beispiel auf ein Element mit der Klasse your-element, die Seite reibungslos nach oben scrollt.

Warum funktioniert meine glatte Schriftrolle in Safari nicht?

glatte Scrolling -Funktion mit der Methode scrollTo und das Festlegen der Option behavior in smooth wird in Safari nicht unterstützt. Damit Sie es funktionieren können, können Sie Polyfill verwenden, z. B. smoothscroll-polyfill. Dies ermöglicht eine reibungslose Scrollen in Browsern, die es nicht nativ unterstützen.

Wie kann man reibungslos zu einem bestimmten Element scrollen?

Um reibungslos zu einem bestimmten Element zu scrollen, können Sie die Methode Element.scrollIntoView verwenden und die Option behavior auf smooth festlegen. Hier ist ein Beispiel:

<code>var jump = (function() {

    var o = {

        jump: function(target, options) {
            this.start = window.pageYOffset

            this.options = {
              duration: options.duration,
              offset: options.offset || 0,
              callback: options.callback,
              easing: options.easing || easeInOutQuad
            }

            this.distance = typeof target === 'string'
              ? this.options.offset + document.querySelector(target).getBoundingClientRect().top
              : target

            this.duration = typeof this.options.duration === 'function'
              ? this.options.duration(this.distance)
              : this.options.duration

            requestAnimationFrame(_loop)
        },

        _loop: function(time) {
            if(!this.timeStart) {
              this.timeStart = time
            }

            this.timeElapsed = time - this.timeStart
            this.next = this.options.easing(this.timeElapsed, this.start, this.distance, this.duration)

            window.scrollTo(0, this.next)

            this.timeElapsed               ? requestAnimationFrame(_loop)
              : this._end()
        },

        _end: function() {
            window.scrollTo(0, this.start + this.distance)

            typeof this.options.callback === 'function' && this.options.callback()
            this.timeStart = false
        }

    };

    var _loop = o._loop.bind(o);

    // Robert Penner's easeInOutQuad - http://robertpenner.com/easing/
    function easeInOutQuad(t, b, c, d)  {
        t /= d / 2
        if(t         t--
        return -c / 2 * (t * (t - 2) - 1) + b
    }

    return o;

})();
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

In diesem Beispiel klicken Sie in diesem Beispiel auf ein Element mit der Klasse your-element, die Seite sanft zu einem Element mit der Klasse target-element scrollen.

Kann ich die Geschwindigkeit des reibungslosen Scrollens steuern?

Die Geschwindigkeit des glatten Scrollens kann nicht direkt gesteuert werden, da sie vom Browser behandelt wird. Sie können jedoch window.requestAnimationFrame verwenden, um eine benutzerdefinierte reibungslose Scroll -Funktion zu erstellen, um eine bessere Kontrolle über die Scrolling -Animation einschließlich ihrer Geschwindigkeit zu erhalten.

Wie kann man horizontales glattes Bildlauf erreichen?

Sie können horizontales glattes Scrollen in ähnlicher Weise wie vertikales glattes Scrollen erzielen. Die Methoden window.scrollTo und Element.scrollIntoView akzeptieren auch die left -Optionen, um die horizontale Position anzugeben, in die sie scrollen sollen. Hier ist ein Beispiel:

<code>function jump(target, options) {
    var start = window.pageYOffset;

    var opt = {
      duration: options.duration,
      offset: options.offset || 0,
      callback: options.callback,
      easing: options.easing || easeInOutQuad
    };

    var distance = typeof target === 'string' ? 
        opt.offset + document.querySelector(target).getBoundingClientRect().top : 
        target
    ;

    var duration = typeof opt.duration === 'function'
          ? opt.duration(distance)
          : opt.duration
    ;

    var 
        timeStart = null,
        timeElapsed
    ;

    requestAnimationFrame(loop);

    function loop(time) {
        if (timeStart === null)
            timeStart = time;

        timeElapsed = time - timeStart;

        window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

        if (timeElapsed         requestAnimationFrame(loop)
        else
            end();
    }

    function end() {
        window.scrollTo(0, start + distance);

        typeof opt.callback === 'function' && opt.callback();
        timeStart = null;
    }

    // ...

}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

scrollt das Dokument nach rechts reibungslos.

Wie kann man glatte Scroll -Animation stoppen?

Die glatte Scrolling -Animation kann nicht direkt gestoppt werden, da sie vom Browser behandelt wird. Wenn Sie jedoch eine benutzerdefinierte reibungslose Scroll -Funktion verwenden, können Sie window.cancelAnimationFrame den Animationsrahmen abbrechen, um die Animation zu stoppen.

Wie kann man ein reibungsloses Scrollen mit festen Headern erzielen?

Um ein reibungsloses Scrollen mit festen Headern zu erzielen, müssen Sie die Bildlaufposition einstellen, um die Höhe des Headers zu berücksichtigen. Sie können dies tun, indem Sie die Höhe des Headers von der Ziel -Scroll -Position subtrahieren.

Wie kann man ein reibungsloses Scrollen für Ankerlinks erreichen?

Um ein reibungsloses Scrollen für Ankerlinks zu erzielen, können Sie Ereignishörer zum Klickereignis des Links hinzufügen und die Element.scrollIntoView -Methode verwenden, um reibungslos zum Zielelement zu scrollen. Hier ist ein Beispiel:

<code>requestAnimationFrame(function(time) { timeStart = time; loop(time); });

function loop(time) {
    timeElapsed = time - timeStart;

    window.scrollTo(0, opt.easing(timeElapsed, start, distance, duration));

    if (timeElapsed         requestAnimationFrame(loop)
    else
        end();
}
</code>
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

scrollen Sie alle Ankerlinks auf der Seite sanft zu seinem Zielelement.

Wie kann ich die Tastaturnavigation verwenden, um reibungslos zu scrollen?

Die Verwendung der Tastaturnavigation zur Erzielung eines reibungslosen Scrollens ist komplizierter, da die Abfangen von Tastaturereignissen und das manuelle Scrollen -Dokumente erforderlich sind. Sie können dies tun, indem Sie das Ereignisereignis zum keydown -Event hinzufügen und das Dokument mit der window.scrollTo -Methode reibungslos scrollen.

Wie testet ich die Kompatibilität meiner sanften Bildlaufimplementierung?

Sie können Online -Tools wie BrowsStack verwenden, um die Kompatibilität eines reibungslosen Scrolls zu testen. Mit diesen Tools können Sie Ihre Website auf verschiedenen Browsern und auf verschiedenen Geräten testen, um sicherzustellen, dass Ihre Implementierung in allen Umgebungen ordnungsgemäß funktioniert.

Das obige ist der detaillierte Inhalt vonSo implementieren Sie ein reibungsloses Scrollen in Vanille JavaScript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage