Files
Website-Ruckert-Utikal-ITSo…/templatemo-parallax-script.js
2026-04-18 11:13:51 +02:00

193 lines
6.6 KiB
JavaScript

/* ==========================================================================
Parallax Starter - Free HTML CSS Template
TemplateMo 612 Parallax Starter
https://templatemo.com/tm-612-parallax-starter
========================================================================== */
(function () {
'use strict';
// --- Elements ---
var nav = document.getElementById('templatemo-nav');
var navToggle = document.getElementById('navToggle');
var navLinks = document.getElementById('navLinks');
var navItems = document.querySelectorAll('.nav-links a');
var sections = document.querySelectorAll('.parallax-section');
var parallaxBgs = document.querySelectorAll('.parallax-bg');
var revealElements = document.querySelectorAll('.section-content');
// --- Detect mobile ---
var isMobile = /Android|iPhone|iPad|iPod|webOS|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|| window.innerWidth <= 768;
// =============================================
// Smooth Parallax Engine
// =============================================
// How it works:
// - Each .parallax-bg is 200% the height of the viewport
// and offset by -50% so there's plenty of image above
// and below to translate into.
// - As the user scrolls, we calculate how far the section
// midpoint is from the viewport center (a value from -1 to +1).
// - We multiply that by a large pixel range (half the viewport height)
// so the background shifts dramatically relative to the content.
// - data-speed controls intensity: 0.5 = half viewport travel range.
var ticking = false;
function updateParallax() {
if (isMobile) return;
var scrollTop = window.pageYOffset;
var windowHeight = window.innerHeight;
parallaxBgs.forEach(function (bg) {
var section = bg.parentElement;
var rect = section.getBoundingClientRect();
// Skip sections far outside viewport
if (rect.bottom < -300 || rect.top > windowHeight + 300) {
return;
}
var speed = parseFloat(bg.getAttribute('data-speed')) || 0.5;
// How far is the section center from the viewport center?
// sectionCenterY: vertical center of the section in viewport coords
var sectionCenterY = rect.top + rect.height / 2;
var viewportCenterY = windowHeight / 2;
// offset: negative when section center is above viewport center (scrolled past)
// positive when section center is below viewport center (not yet reached)
var offset = sectionCenterY - viewportCenterY;
// Normalize to a -1 to +1 range based on how far through the viewport
// the section has traveled. Using windowHeight + section height as the
// total travel distance ensures full range coverage.
var totalTravel = windowHeight + rect.height;
var normalized = offset / (totalTravel / 2); // -1 to +1
// Clamp
normalized = Math.max(-1, Math.min(1, normalized));
// The maximum pixel displacement — large value for visible effect
// speed=0.5 means the bg can travel up to 50% of the viewport height
var maxShift = windowHeight * speed;
// Apply translation — bg moves in the SAME direction as the offset
// which means it moves SLOWER than the scroll (parallax lag)
var translateY = normalized * maxShift;
bg.style.transform = 'translate3d(0,' + translateY.toFixed(1) + 'px,0)';
});
ticking = false;
}
function onScroll() {
if (!ticking) {
window.requestAnimationFrame(updateParallax);
ticking = true;
}
}
if (!isMobile) {
window.addEventListener('scroll', onScroll, { passive: true });
updateParallax();
}
// Recalculate on resize
window.addEventListener('resize', function () {
isMobile = window.innerWidth <= 768;
if (!isMobile) {
updateParallax();
} else {
parallaxBgs.forEach(function (bg) {
bg.style.transform = 'translate3d(0,0,0)';
});
}
});
// --- Navigation Scroll Effect ---
function handleNavScroll() {
if (window.scrollY > 80) {
nav.classList.add('scrolled');
} else {
nav.classList.remove('scrolled');
}
}
window.addEventListener('scroll', handleNavScroll, { passive: true });
handleNavScroll();
// --- Mobile Toggle ---
navToggle.addEventListener('click', function () {
navToggle.classList.toggle('active');
navLinks.classList.toggle('open');
});
navItems.forEach(function (link) {
link.addEventListener('click', function () {
navToggle.classList.remove('active');
navLinks.classList.remove('open');
});
});
// --- Active Link on Scroll ---
function updateActiveLink() {
var scrollPos = window.scrollY + window.innerHeight / 3;
sections.forEach(function (section) {
var top = section.offsetTop;
var height = section.offsetHeight;
var id = section.getAttribute('id');
if (scrollPos >= top && scrollPos < top + height) {
navItems.forEach(function (link) {
link.classList.remove('active');
if (link.getAttribute('href') === '#' + id) {
link.classList.add('active');
}
});
}
});
}
window.addEventListener('scroll', updateActiveLink, { passive: true });
updateActiveLink();
// --- Scroll Reveal ---
revealElements.forEach(function (el) {
el.classList.add('reveal');
});
function checkReveal() {
var windowHeight = window.innerHeight;
var revealPoint = 120;
revealElements.forEach(function (el) {
var elementTop = el.getBoundingClientRect().top;
if (elementTop < windowHeight - revealPoint) {
el.classList.add('visible');
}
});
}
window.addEventListener('scroll', checkReveal, { passive: true });
checkReveal();
// --- Contact Form ---
var contactForm = document.getElementById('contactForm');
if (contactForm) {
contactForm.addEventListener('submit', function (e) {
e.preventDefault();
alert('Thank you for your message! We will get back to you soon.');
contactForm.reset();
});
}
})();