Parallaxe mit / ohne jQuery

Animationen mit jQuery: Parallaxe beim Scrollen

Der Parallax-Effekt reagiert auf Scrollen und erweckt den Eindruck von Tiefe. Die einfachste Variante ohne JavaScript beruht auf CSS mit einem einfachen Hintergrundbild in einem Container fester Höhe. Der Parallax-Effekt entsteht durch background-attachment: fixed.

jQuery Parallax-Effekt

Der Parallax-Effekt nutzt diese Phänomene mit verschiedenen Hintergrundbildern, die beim Scrollen unterschiedlich verschoben werden, um den Eindruck von Bewegung mit räumlicher Tiefe zu verbinden. Wer räumliches Zeichnen mit der Zentralperspektive gelernt hat, kennt den Trick der Zeichner: Was weiter weg ist, erscheint und kleiner. Wer Sport fotografiert, weiß, das eine Bewegung um so langsamer erscheint, je weiter entfernt sie stattfindet.

Ohne Javascript spielen die mobilen Geräte bei der CSS-Lösung mit background-attachment: fixed allerdings nicht mit, so dass der Effekt oft mit jQuery eingesetzt wurde.

Das HTML für den Parallax-Effekt sind schlank und einfach. Jedes div enthält ein Hintergrundbild: Zuerst die Bilder für den am weitesten entfernten Hintergrund, das vorletzte Hintergrundbild (Muscheln und Seesterne) bleibt stehen.

HTML Struktur für Parallaxen-Effekt
<div class="spring">
	<div class="ug"></div>
	<div class="trio" hidden></div>
	<div class="trioFlipped" ></div>
	<div class="duo" hidden></div>
	<div class="duoFlipped" ></div>
</div>

Quelle: Horizontal Parallax on Scroll - jQuery

Das CSS ist minimal – die Hintergrundbilder liegen absolut positioniert im relativ positionierten Container .spring.

CSS für Parallaxe
.spring {
	width: 96%;
	margin: auto;
	height: 300px;
	margin-top: 2rem;
	position: relative;
	background-image: linear-gradient(to top, hsl(190,50%,70%) 0%, hsl(190,50%,96%) 20%);
	
}

.trio {
	position: absolute;
	width: 5000px;
	max-width: 100%;
	overflow:hidden;
	height: 300px;
	background-image: url(trioFlipped.webp);
	background-repeat: repeat-x;
	opacity: 0.6;
}

.trioFlipped {
	position: absolute;
	width: 100%;
	height: 300px;
	background-image: url(trio.webp);
	background-repeat: repeat-x;
	opacity: 0.6;
}

.duo {
	position: absolute;
	width: 100%;
	height: 300px;
	background-image: url(duoFlipped.webp);
	background-repeat: repeat-x;
}

.duoFlipped {
	position: absolute;
	width: 100%;
	height: 300px;
	background-image: url(duo.webp);
	background-repeat: repeat-x;
}

.ug {
	position: absolute;
	width: 100%;
	height: 200px;
	background-image: url(underground.webp);
	background-size: contain;
	bottom: 0;
	opacity: 0.5
}

Das Script muss nur die Hintergrundbilder in unterschiedlichen Geschwindigkeiten verschieben.

$(window).height() ist die Höhe des Browserfensters (aka ViewPort) ohne Maßeinheit. $(document).height() gibt die Höhe des Dokuments zurück (ebenfalls in Pixel und ohne Maßeinheit). Wenn $(document).height() kleiner als die Höhe des Viewports ist, wird die Höhe des Viewports zurückgegeben.

Noch eine Abfrage nach der Scroll-Richtung dazu (if (scrollNum > lastScrollTop)) und dann wechseln die Fische die Richtung, je nachdem, ob der Benutzer nach oben oder nach unten scrollt.

jQuery
$(document).ready(function() {
    var speed = 40;
    var lastScrollTop = 0;

    // higher variation = faster acceleration
    function positon(variation) {
        newPosition = (scrollPercent * (speed * variation)) - (speed * variation);
        return newPosition + "%";
    }

    $(window).scroll(function(e) {
        var scrollNum = $(window).scrollTop();
        scrollPercent = $(window).scrollTop() / ($(document).height() - $(window).height());

        if (scrollNum > lastScrollTop) {
            // downscroll
            $(".nflip").show(); $(".flip").hide();

            $(".fishya").css("backgroundPosition", positon(-1.6) + ' 0');
            $(".fishyb").css("backgroundPosition", positon(-0.8) + ' 10px');
        } else {
            //upscroll
            $(".nflip").hide(); $(".flip").show();

            $(".fishyaFlip").css("backgroundPosition", positon(-1.6) + ' 0');
            $(".fishybFlip").css("backgroundPosition", positon(-0.8) + ' 10px');
        }
        lastScrollTop = scrollNum;
    });
});

jQuery in Javascript übersetzen

Das jQuery-Script in Vanilla Javascript zu übersetzen ist mit den modernen Browsern nicht mehr besonders aufwändig.

Wie weit ist der Benutzer gescrollt?

Ohne Rücksicht auf alte Browser gibt es heute eine zuverlässige Abfrage für die Größe des Browserfensters und die gerenderte Höhe des Dokuments.

  • $(document).ready(function() {}) wird nicht gebraucht wenn das Skript am Ende vor dem body-Tag oder im head-Element mit defer geladen wird.
  • $(window).scroll(function(e) {}) wird zu window.addEventListener ("scroll", function (e) {})
  • Aus $(window).scrollTop() wird document.documentElement.scrollTop
  • Aus $(document).height() wird document.body.scrollHeight
  • Aus $(window).height() wird window.innerHeight
  • $(".nflip").show(); $(".flip").hide(); da heißt es »aufpassen«, denn $(".class") erfasst alle Element der Klasse, querySelector(".class") nur das erste Element.
  • jQuery .show() und .hide() werden durch setAttribut("hidden", "true") / removeAttribut("hidden") ersetzt.
  • Die Berechnung des Versatz beim Scrollen übernimmt setAttribute.

Sonst ist der Aufwand für den Parallaxen-Effekt ohne jQuery gering, nur die effizienten kleinen jQuery Effekte hide() und show() brauchen mit Vanilla Javascript ein paar Zeilen zusätzlich.

const speed = 50;
let lastScrollTop = 0;
// higher variation = faster acceleration

function position(variation) {
	newPosition = (scrollPercent * (speed * variation)) - (speed * variation);
	return newPosition.toFixed(1) + "%";
}

window.onscroll = function (e) {
	let scrollNum = window.scrollY;
	
	let documentHeight = document.body.offsetHeight-window.innerHeight;
	scrollPercent = window.scrollY / (documentHeight - window.innerHeight);
	
	if (scrollNum > lastScrollTop) {
		// downscroll
		document.querySelector(".trioFlipped").setAttribute ("hidden", "true");
		document.querySelector(".trio").removeAttribute ("hidden");
		document.querySelector(".duoFlipped").setAttribute ("hidden", "true");
		document.querySelector(".duo").removeAttribute ("hidden");
		
		document.querySelector(".trio").setAttribute ("style", "background-position:" + position(3) + " 0");   
		document.querySelector(".duo").setAttribute ("style", "background-position:" + position(-3) + " 10px");         
	} else {
	// upscroll
		document.querySelector(".trio").setAttribute ("hidden", "true");
		document.querySelector(".trioFlipped").removeAttribute ("hidden");
		document.querySelector(".duo").setAttribute ("hidden", "true");
		document.querySelector(".duoFlipped").removeAttribute ("hidden");
		
		document.querySelector(".trioFlipped").setAttribute ("style", "background-position:" + position(3) + " 0");
		document.querySelector(".duoFlipped").setAttribute ("style", "background-position:" + position(-3) + " 10px");
	}
	lastScrollTop = scrollNum;
}

Accordion ohne jQuery

CSS transition und ein paar Zeilen Vanilla Javascript setzen heute den Accordion-Effekt ebenso gut in Gang, wenn die ganz alten Browser keine Rolle mehr spielen (CSS transition: ab IE10).

Es gibt nur wenige CSS-Events, die eine CSS-Transition ohne Javascript und aufwändige Tricks anstoßen können. Ein paar Zeilen Javascript setzen die Transition bei einem Mausklick durch eine zusätzliche CSS-Klasse in Gang.

Suspendisse potenti

acco-books-3

Nullam risus dui, consectetur quis ullamcorper suscipit, facilisis a quam. Suspendisse potenti.

Cras ut elit

acco-paper-3

Etiam tincidunt dui est. Nam quis nulla quis neque efficitur rhoncus. Cras ut elit mattis, hendrerit orci a, varius lorem. Nullam finibus consequat dignissim. Suspendisse potenti. Suspendisse nec odio accumsan, vehicula nulla et, varius erat.

Donec ultricies

acco-spices-3

Donec ultricies sem sit amet ultricies venenatis.

<h4>Donec ultricies</h4>
<div class="row">
   <img src="spices.jpg" width="980" height="511" alt="spices">
   <p>Donec ultricies sem sit amet ultricies venenatis.</p>
</div>

Der Platz für die Elemente des Accordions darf unterschiedlich hoch sein. CSS transition agiert hier nicht auf height, sondern auf max-height. Wenn der Inhalt kürzer als die Höhe des Blocks bleibt, verhalten sich Blöcke mit min-height und height gleich.

.row { 
   max-height: 0;
   overflow:hidden;
   transition: max-height 1s; 
}
.acctoggle { 
   max-height: 800px;
   height: auto;
}

Der Trick mit max-height und height:auto bringt die drei Zeilen der Animation auf die passende Höhe. Zu beachten ins allenfalls, dass sich die Dauer der Animation nicht nach height, sondern nach max-height richtet.

<script>
(function(){
   let row = document.querySelectorAll (".row");
   let h4 = document.querySelectorAll (".acc h4");
   let len = h4.length;
   for (let i=0; i<len; i++) {
      h4[i].onclick = function () {
         for (let j=0; j<len; j++) {
            row[j].classList.remove("acctoggle"); 
         }
         this.nextElementSibling.classList.add("acctoggle");
      }
   }
})();
</script>

Accordion und Parallaxe – Responsive?

Auf kleinen Monitoren sind Effekte nicht immer benutzerfreundlich und steuern wenig zur Benutzererfahrung bei. Javascript matchMedia ist das Äquivalent zu CSS Media Queries. matchMedia kann den Effekt auf größere ViewPorts beschränken.

matchMedia wird von den modernen Browsern und von IE ab IE10 unterstützt.

/** Javascript Media Query **/
let mql = window.matchMedia("screen and (min-width:600px)");

/** Beim Laden der Seite initialisieren **/
tabbedGallery(mql);

/** Ein spezieller Event Listener für matchMedia **/
mql.addListener(tabbedGallery);

function tabbedGallery (mql) {
   if (mql.matches) {
      … 
   }
}