Vue.js Fullpage Scroll programmieren

Vue.js Fullpage Scroll programmieren

Beim Fullpage Scroll wird das normale Scrollen deaktiviert und man wird immer exakt in die nächste Section gescrollt. Hier erfährst Du Schritt für Schritt, wie Du dieses Feature selber mit Vue.js programmieren kannst.

Demos

Damit direkt von Anfang an feststeht, wie das Ergebnis aussehen wird, habe ich hier schon mal eine Demo vorbereitet. Diese Version wird von allen gängigen Browsern unterstützt und funktioniert auch auf mobilen Endgeräten.

Ein kleines Problem besteht jedoch im Microsoft Edge Browser (Nachfolger des IE): Das reine Smooth Scroll wird hier nicht unterstützt. Das Scrollen wird somit etwas ruckelig angezeigt, es sind trotzdem aber alle Funktionen nutzbar. Somit haben wir einen schönen Workaround geschaffen.

See the Pen Vue.js Fullpage Scroll by WebDEasy (@WebDEasy) on CodePen.

Wenn Du noch Design-Inspirationen suchst habe ich hier noch zwei Seiten, die den Fullpage Scroll meiner Meinung nach extrem gut in Szene gesetzt haben:

Fullpage Scroll Frameworks

Ich weiß, es gibt bereits einige wirklich gute Vue.js Fullpage Scroll Frameworks, wie dieses. Allerdings ist es doch viel cooler, das ganze selber zu programmieren. Und außerdem hat man dann nicht so viele „unnötige“ Funktionen auf seiner Seite, die man eigentlich gar nicht benötigt.

Wenn Du kein Freund von Vue.js bist, kannst Du das ganze auch mit React, AngularJS oder jQuery umsetzen. Die Vorgehensweise ist meist ähnlich. Einzelne Funktionen können natürlich an die Frameworks angepasst werden.

HTML Gerüst

Zu erst benötigen wir einen Container, in dem unsere Vue.js App läuft. Dazu erstellen wir einen Container #app. Dort kommt das restliche HTML rein.

<div id="app">
    <!-- CONTENT HERE -->
</div>

Als nächstes legen wir unsere einzelnen Sections (Abschnitte) an. Dort kannst Du dann beliebigen Inhalt reinpacken und gestalten.

<section class="fullpage">
    <h1>Section 1</h1>
</section>
<section class="fullpage">
    <h1>Section 2</h1>
</section>
<section class="fullpage">
    <h1>Section 3</h1>
</section>
<section class="fullpage">
    <h1>Section 4</h1>
</section>

Im nächsten Schritt wollen wir das seitlich Menü erstellen. Damit kann man per Klick in eine andere Section navigieren und bekommt immer die aktive Section angezeigt.

<div class="sections-menu">
  <span
     class="menu-point"
     v-bind:class="{active: activeSection == index}"
     v-on:click="scrollToSection(index)"
     v-for="(offset, index) in offsets"
     v-bind:key="index">
  </span>
</div>

Zu diesem Block benötigt es jetzt etwas Erklärung: Um das Menü machen wir ein Wrapper .sections-menu. Darin ist ein span-Tag, welcher einige Attribute für Vue.js enthält. Deshalb hier die genaue Erklärung:

AttributWertBeschreibung
v-bind:class {active: activeSection == index} Der Tag bekommt die Klasse active, wenn der aktuelle Schleifendurchlauf gleich der aktiven Section ist.
v-on:click scrollToSection(index) Beim Klick auf den Link wird die Funktion scrollToSection() aufgerufen. Der Parameter ist der Schleifendurch und somit unsere Section ID.
v-for (offset, index) in offsets Der Link wird nach Anzahl der Elemente im offsets Array wiederholt. Die Variable index ist stellt unsere Section ID dar.
v-bind:key index Um jeden Schleifen Durchlauf eindeutig zu halten, setzen wir unsere Section ID als key für die for-Direktive.

CSS

Damit der Effekt des Fullpage Scrolls richtig zur Geltung kommt, sollten die einzelnen Sections mindestens 100vh (also genau Bildschirmhöhe) betragen. Du musst also sehen, dass der Inhalt einer Section auf genau diesen Platz angepasst ist.

.fullpage {
  height: 100vh;
  width: 100%;
}

In unserem Beispiel haben wir lediglich eine Überschrift und Unterüberschrift. Für Inspirationen schau Dir doch mal die oben verlinkten Demos an.

Navigation

Das Menü zum Navigieren habe ich auch möglichst einfach gehalten. Weiße Punkte, bei denen der aktive Punkt stärker und größer ist. Außerdem sitzt das Menü immer an der rechten Seite des Bildschirmrandes. Das CSS dazu sieht so aus:

.sections-menu {
  position: fixed;
  right: 1rem;
  top: 50%;
  transform: translateY(-50%);
}

.sections-menu .menu-point {
  width: 10px;
  height: 10px;
  background-color: #FFF;
  display: block;
  margin: 1rem 0;
  opacity: .6;
  transition: .4s ease all;
  cursor: pointer;
}

.sections-menu .menu-point.active {
  opacity: 1;
  transform: scale(1.5);
}

CSS zum Positionieren der Schrift, etc. habe ich hier nicht behandelt, da es sich um Basics handelt und nichts mit dem eigentlichen Feature zu tun hat. Den kompletten Code findest Du auf meinem Pen auf Codepen.

Vue.js (JavaScript)

Das JavaScript ist hier die aufwendigste Sache gewesen. Wir müssen sowohl alle modernen Browser, als auch Mobilgeräte abdecken.

Vue.js App initialisieren

Wir beginnen mit dem Initialisieren der Vue.js App. Dazu haben wir bereits im HTML den #app Container angelegt.

var app = new Vue({
  el: '#app',
});

Variablen deklarieren und initialisieren

Nun deklarieren und initialisieren wir die Variablen. Später wird deutlich für das wir welche Variable benötigen, falls der Name das nicht schon verrät.

data: {
    inMove: false,
    activeSection: 0,
    offsets: [],
    touchStartY: 0
}

Offsets berechnen

Nun wollen wir die Offsets (obere Kante der Abschnitte) der einzelnen Sections berechnen. Das bringt uns den Vorteil, dass wir uns Rechenleistung sparen, da wir das nicht bei jedem Scroll neu berechnen müssen.

Wir laufen mit einer Schleife über alle section Element drüber und speichern uns den Offset in unserem globale offsets Array.

calculateSectionOffsets() {
    let sections = document.getElementsByTagName('section');
    let length = sections.length;

    for (let i = 0; i < length; i++) {
        let sectionOffset = sections[i].offsetTop;
        this.offsets.push(sectionOffset);
    }
}

Die Funktion wird einmal beim Erstellen der Applikation in der created() Funktion aufgerufen.

created() {
    this.calculateSectionOffsets();
}

Event Listener

Wir benötigen einige Event Listener, um die Scroll Bewegung für Desktop und den Swipe auf Mobilgeräten abzufangen. Dabei registrieren wir diese Listener in der created() Funktion.

window.addEventListener('DOMMouseScroll', this.handleMouseWheelDOM); // Mozilla Firefox
window.addEventListener('mousewheel', this.handleMouseWheel, {
    passive: false
}); // Other browsers

window.addEventListener('touchstart', this.touchStart, {
    passive: false
}); // mobile devices
window.addEventListener('touchmove', this.touchMove, {
    passive: false
}); // mobile devices

Außerdem entfernen wir die Event Listener beim Beenden der Anwendung in der destroy() Funktion wieder.

destroyed() {
    window.removeEventListener('mousewheel', this.handleMouseWheel, {
        passive: false
    }); // Other browsers
    window.removeEventListener('DOMMouseScroll', this.handleMouseWheelDOM); // Mozilla Firefox

    window.removeEventListener('touchstart', this.touchStart); // mobile devices
    window.removeEventListener('touchmove', this.touchMove); // mobile devices
}

Wie Du schon an den Kommentare sehen kannst, gibt es für verschiedene Browser unterschiedliche Events. Bei manchen wird der Parameter passive: false mitgegeben. Dieser muss unbedingt eingefügt werden.

Ich habe viel rum probiert, bis das Scrollen flüssig funktioniert hat. Das Ergebnis konnte ich nur mit diesem Parameter erzielen. Außerdem wurden sonst Fehlermeldungen in der Browser Console angezeigt.

Scroll Funktion

Diese Funktion wird bereits von unseren HTML Links aufgerufen. Hiermit scrollen wir zu der Section. Der Parameter id ist dabei die Section ID. Die Variable inMove sorgt dafür, dass wir einen kleinen Cooldown haben. Das bedeutet, dass wir nur alle 400ms (0,4s) scrollen können. Mit dem Parameter force auf true können wir den Cooldown überspringen.

scrollToSection(id, force = false) {
    if (this.inMove && !force) return false;

    this.activeSection = id;
    this.inMove = true;

    document.getElementsByTagName('section')[id].scrollIntoView({
        behavior: 'smooth'
    });

    setTimeout(() => {
        this.inMove = false;
    }, 400);
},

Scroll Richtung erkennen

Die Funktion handleMouseWheel ist der Event Listener des Scroll Events auf Desktop Geräten (mousewheel & DOMMouseScroll).

Über die Variable wheelDelta des Events können wir erkennen, ob der Nutzer hoch oder runter scrollt. Dementsprechend wird unsere moveUp() oder moveDown() Funktion aufgerufen, die im nächsten Schritt erstellt wird. Am Ende wird durch e.preventDefault() und return false; das Event abgebrochen.

handleMouseWheel: function(e) {

    if (e.wheelDelta < 30 && !this.inMove) {
        this.moveUp();
    } else if (e.wheelDelta > 30 && !this.inMove) {
        this.moveDown();
    }

    e.preventDefault();
    return false;
},

Section hoch- und runter scrollen

Diese beiden Funktionen sind nur dafür zuständig, einmal hoch- oder runter zu scrollen. Wenn wir runter scrollen und die Section kleiner als 0 wäre, scrollen wir zu der letzten Section. Anders herum ist es, wenn die neue Section größer als die Anzahl der Sections wäre, scrollen wir zur ersten Section. 0 ist dabei die ersten Section.

moveDown() {
    this.inMove = true;
    this.activeSection--;

    if (this.activeSection < 0) this.activeSection = this.offsets.length - 1;

    this.scrollToSection(this.activeSection, true);
},
moveUp() {
    this.inMove = true;
    this.activeSection++;

    if (this.activeSection > this.offsets.length - 1) this.activeSection = 0;

    this.scrollToSection(this.activeSection, true);
}

Damit haben wir ein Endlos Scroll geschaffen. Wenn Du das nicht möchtest, kannst Du einfach keinen Swipe ausführen, sodass der Nutzer an den Enden nur in die andere Richtung scrollen kann.

Mobile Swipe erkennen

Mobil haben wir die Events touchstart und touchmove, durch die wir erkennen können, wohin der Nutzer scrollt. Wenn der Nutzer anfängt zu scrollen wird touchStart() aufgerufen. Dort speichern wir uns die Y Position. Wenn der Nutzer dann seinen Finger auf dem Display bewegt, wird touchMove() aufgerufen.

Diese beiden Werte vergleichen wir dann und sehen, ob der Nutzer hoch oder runter scrollt. Dementsprechend wird unsere moveUp() oder moveDown() Funktion aufgerufen, die wir zuvor erstellt haben.

touchStart(e) {
    e.preventDefault();

    this.touchStartY = e.touches[0].clientY;
},
touchMove(e) {
    if (this.inMove) return false;
    e.preventDefault();

    const currentY = e.touches[0].clientY;

    if (this.touchStartY < currentY) {
        this.moveDown();
    } else {
        this.moveUp();
    }

    this.touchStartY = 0;
    return false;
}

Fazit

Wie Du siehst kannst Du Deine eigenen Features hinzufügen oder Teile entfernen, wenn sie Dir nicht gefallen. Fullpage Scroll ist ein sehr schickes Feature und bei richtiger Benutzung kannst Du sehr schöne Ergebnisse zaubern. Einige Inspirationen hast Du bereits bei den Demos kennen gelernt.

Den kompletten Code findest Du auf meinem Pen auf Codepen.

Wenn Du Verbesserungsvorschläge oder Feedback hast, gerne in die Kommentare! 🙂

NEW 🚀

Schreib einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.