- Körnung neutral und echte Überblendung (Abschnitte isoliert), keine harten Kanten mehr an Flammen/Wellen - Hero: Burger kleiner, Wortmarke raus, KNOCKOUT wieder lesbar - Fotoband durch Plakat-Abschnitt Crispy Tenders ersetzt, Wings-Freisteller nachgeschnitten - Zutaten: korrekt zentriert, Schweben relativ (kein Pendeln), kräftigere Explosion, pausiert außerhalb des Bildes - Speisekarte: Kategorie-Leiste klebt wieder (body overflow), Bänder in Rot/Gelb/Dunkel mit Flammen - Kontrast auf Orange, Kopfleiste mit Hintergrund beim Scrollen, Menü sperrt das Scrollen, leeres lenis.min.js entfernt - Flammen und Funken laufen nur, solange sie sichtbar sind Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
561 lines
27 KiB
JavaScript
561 lines
27 KiB
JavaScript
/* Chickago Boys – Interaktion & Animation */
|
||
(function () {
|
||
'use strict';
|
||
|
||
var root = document.documentElement;
|
||
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||
var hasGsap = !!(window.gsap && window.ScrollTrigger);
|
||
var $ = function (s, r) { return (r || document).querySelector(s); };
|
||
var $$ = function (s, r) { return Array.prototype.slice.call((r || document).querySelectorAll(s)); };
|
||
|
||
if (!hasGsap) root.classList.remove('anim');
|
||
|
||
/* ---------- Jahr ---------- */
|
||
$$('[data-year]').forEach(function (el) { el.textContent = new Date().getFullYear(); });
|
||
|
||
/* ---------- Öffnungszeiten (täglich 12–22 Uhr, Zeitzone Berlin) ---------- */
|
||
var OPEN = 12 * 60, CLOSE = 22 * 60;
|
||
function berlinNow() {
|
||
var parts = new Intl.DateTimeFormat('en-US', {
|
||
timeZone: 'Europe/Berlin', weekday: 'short', hour: '2-digit', minute: '2-digit', hourCycle: 'h23'
|
||
}).formatToParts(new Date());
|
||
var map = {};
|
||
parts.forEach(function (p) { map[p.type] = p.value; });
|
||
var days = { Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6 };
|
||
return { day: days[map.weekday], minutes: parseInt(map.hour, 10) * 60 + parseInt(map.minute, 10) };
|
||
}
|
||
function updateOpenState() {
|
||
var now = berlinNow();
|
||
var isOpen = now.minutes >= OPEN && now.minutes < CLOSE;
|
||
var text = isOpen ? 'Jetzt geöffnet · bis 22 Uhr'
|
||
: now.minutes < OPEN ? 'Geschlossen · öffnet heute um 12 Uhr'
|
||
: 'Geschlossen · öffnet morgen um 12 Uhr';
|
||
$$('[data-open-badge]').forEach(function (el) {
|
||
el.textContent = text;
|
||
el.classList.toggle('is-open', isOpen);
|
||
el.classList.toggle('is-closed', !isOpen);
|
||
});
|
||
$$('[data-hours] tr').forEach(function (tr) {
|
||
tr.classList.toggle('is-today', parseInt(tr.getAttribute('data-day'), 10) === now.day);
|
||
});
|
||
}
|
||
updateOpenState();
|
||
setInterval(updateOpenState, 60000);
|
||
|
||
/* ---------- Smooth Scroll (Lenis) ---------- */
|
||
var lenis = null;
|
||
if (!reduce && window.Lenis) {
|
||
lenis = new window.Lenis({ duration: 1.1, smoothWheel: true });
|
||
if (hasGsap) {
|
||
lenis.on('scroll', window.ScrollTrigger.update);
|
||
window.gsap.ticker.add(function (t) { lenis.raf(t * 1000); });
|
||
window.gsap.ticker.lagSmoothing(0);
|
||
} else {
|
||
var raf = function (t) { lenis.raf(t); requestAnimationFrame(raf); };
|
||
requestAnimationFrame(raf);
|
||
}
|
||
}
|
||
function navOffset() { return -(($('#nav') || {}).offsetHeight || 0) - 12; }
|
||
function scrollToTarget(target) {
|
||
if (lenis) lenis.scrollTo(target, { offset: navOffset(), duration: 1.1 });
|
||
else target.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block: 'start' });
|
||
}
|
||
document.addEventListener('click', function (e) {
|
||
var a = e.target.closest && e.target.closest('a[href*="#"]');
|
||
if (!a) return;
|
||
var url = new URL(a.href, location.href);
|
||
if (url.pathname !== location.pathname || !url.hash) return;
|
||
var target = url.hash === '#top' ? document.body : document.getElementById(decodeURIComponent(url.hash.slice(1)));
|
||
if (!target) return;
|
||
e.preventDefault();
|
||
closeMenu(false);
|
||
if (url.hash === '#top') { lenis ? lenis.scrollTo(0) : window.scrollTo({ top: 0, behavior: reduce ? 'auto' : 'smooth' }); }
|
||
else scrollToTarget(target);
|
||
history.replaceState(null, '', url.hash);
|
||
});
|
||
|
||
/* ---------- Navigation ein-/ausblenden ---------- */
|
||
var nav = $('#nav');
|
||
var lastY = window.scrollY;
|
||
function onScroll() {
|
||
var y = window.scrollY;
|
||
if (!nav) return;
|
||
if (document.body.classList.contains('menu-open')) { lastY = y; return; }
|
||
if (y > 140 && y - lastY > 6) nav.classList.add('is-hidden');
|
||
else if (lastY - y > 6 || y < 140) nav.classList.remove('is-hidden');
|
||
document.body.classList.toggle('nav-visible', !nav.classList.contains('is-hidden') && y > 10);
|
||
nav.classList.toggle('is-scrolled', y > 60);
|
||
lastY = y;
|
||
}
|
||
window.addEventListener('scroll', onScroll, { passive: true });
|
||
|
||
/* ---------- Menü ---------- */
|
||
var btn = $('#menu-btn'), panel = $('#menu-panel'), overlay = $('#menu-overlay');
|
||
function setLabel(txt) { $$('.menu-label', btn).forEach(function (s) { s.textContent = txt; }); }
|
||
function openMenu() {
|
||
document.body.classList.add('menu-open');
|
||
nav.classList.add('is-open');
|
||
btn.setAttribute('aria-expanded', 'true');
|
||
setLabel('Schließen');
|
||
if (lenis) lenis.stop();
|
||
if (hasGsap && !reduce) {
|
||
window.gsap.fromTo($$('li', panel), { y: 18, opacity: 0 }, { y: 0, opacity: 1, duration: .38, stagger: .06, ease: 'power3.out', delay: .14 });
|
||
}
|
||
var first = $('a', panel);
|
||
if (first) setTimeout(function () { first.focus({ preventScroll: true }); }, 60);
|
||
}
|
||
function closeMenu(returnFocus) {
|
||
if (!document.body.classList.contains('menu-open')) return;
|
||
document.body.classList.remove('menu-open');
|
||
nav.classList.remove('is-open');
|
||
btn.setAttribute('aria-expanded', 'false');
|
||
setLabel('Menü');
|
||
if (lenis) lenis.start();
|
||
if (returnFocus !== false) btn.focus({ preventScroll: true });
|
||
}
|
||
if (btn && panel) {
|
||
btn.addEventListener('click', function () {
|
||
document.body.classList.contains('menu-open') ? closeMenu() : openMenu();
|
||
});
|
||
overlay && overlay.addEventListener('click', function () { closeMenu(); });
|
||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeMenu(); });
|
||
$$('a', panel).forEach(function (a) { a.addEventListener('click', function () { closeMenu(false); }); });
|
||
}
|
||
|
||
/* ---------- Navigation auf dunklen Flächen hell einfärben ---------- */
|
||
var darkEls = $$('[data-nav-dark]');
|
||
if (nav && darkEls.length) {
|
||
var checkDark = function () {
|
||
var probe = (nav.offsetHeight || 70) / 2;
|
||
nav.classList.toggle('is-dark', darkEls.some(function (el) {
|
||
var r = el.getBoundingClientRect(); return r.top <= probe && r.bottom >= probe;
|
||
}));
|
||
};
|
||
window.addEventListener('scroll', checkDark, { passive: true });
|
||
window.addEventListener('resize', checkDark);
|
||
checkDark();
|
||
}
|
||
|
||
/* ---------- Mobile Bestell-Leiste ---------- */
|
||
var orderbar = $('#orderbar');
|
||
var heroEl = $('.hero__cta .btn') || $('.page-hero');
|
||
if (orderbar && heroEl && 'IntersectionObserver' in window) {
|
||
new IntersectionObserver(function (entries) {
|
||
orderbar.classList.toggle('is-visible', !entries[0].isIntersecting);
|
||
}, { threshold: 0 }).observe(heroEl);
|
||
}
|
||
|
||
/* ---------- Speisekarte: aktive Kategorie ---------- */
|
||
var catNav = $('.cat-nav');
|
||
if (catNav && 'IntersectionObserver' in window) {
|
||
var links = $$('a', catNav);
|
||
var list = $('ul', catNav);
|
||
var setActive = function (id) {
|
||
links.forEach(function (l) {
|
||
var on = l.getAttribute('href') === '#' + id;
|
||
l.classList.toggle('is-active', on);
|
||
if (on) {
|
||
l.setAttribute('aria-current', 'true');
|
||
list.scrollTo({ left: l.offsetLeft - list.clientWidth / 2 + l.offsetWidth / 2, behavior: reduce ? 'auto' : 'smooth' });
|
||
} else l.removeAttribute('aria-current');
|
||
});
|
||
};
|
||
var io = new IntersectionObserver(function (entries) {
|
||
entries.forEach(function (en) { if (en.isIntersecting) setActive(en.target.id); });
|
||
}, { rootMargin: '-35% 0px -60% 0px' });
|
||
var cats = $$('.menu-cat');
|
||
cats.forEach(function (s) { io.observe(s); });
|
||
window.addEventListener('scroll', function () {
|
||
if (cats[0] && cats[0].getBoundingClientRect().top > window.innerHeight * .4) {
|
||
links.forEach(function (l) { l.classList.remove('is-active'); l.removeAttribute('aria-current'); });
|
||
}
|
||
}, { passive: true });
|
||
}
|
||
|
||
/* ---------- Zutaten rund ums Essen (Explosion beim Scrollen) ----------
|
||
Jede Zutat: [Datei, x %, y % (relativ zur Essens-Box), Größe % der Box, Drehung, Ebene, auch mobil, gespiegelt]
|
||
Ebenen: b = hinter dem Essen (leicht unscharf), f = davor */
|
||
var BITS = 'assets/img/bits/';
|
||
var PRESETS = {
|
||
burger: [
|
||
['lettuce-2', 114, 30, 20, 40, 'b', 0], ['tomato', -10, 40, 16, -12, 'b', 1], ['onion-2', 106, 88, 14, 0, 'b', 0],
|
||
['jalapeno-1', 8, -8, 11, 30, 'b', 1], ['bacon', 24, -16, 20, -30, 'b', 0, 1], ['sesame-2', 64, -14, 4, 40, 'b', 0],
|
||
['lettuce-1', -14, 70, 24, -25, 'f', 1], ['tomato', 110, 60, 20, 18, 'f', 1, 1], ['bacon', 100, 8, 24, 22, 'f', 1],
|
||
['onion-1', -12, 12, 18, -16, 'f', 1], ['pickle', 22, 104, 13, 8, 'f', 0], ['jalapeno-2', 82, 106, 12, -24, 'f', 1],
|
||
['sesame-1', 36, -8, 3.5, 0, 'f', 0], ['sesame-1', 50, -20, 3, 80, 'f', 0, 1]
|
||
],
|
||
'burger-sm': [
|
||
['onion-2', 6, 10, 15, -14, 'b', 1], ['bacon', 94, 6, 20, 24, 'b', 1],
|
||
['lettuce-1', -4, 70, 20, -20, 'f', 1], ['tomato', 102, 64, 17, 18, 'f', 1], ['sesame-1', 30, -4, 3, 0, 'f', 0]
|
||
],
|
||
'chicken-sm': [
|
||
['bacon', 98, 10, 20, 24, 'b', 1, 1], ['crumb-1', 6, 4, 10, 0, 'b', 0], ['crumb-2', 88, -4, 9, 30, 'b', 0],
|
||
['lettuce-2', -6, 70, 20, -24, 'f', 1], ['tomato', 104, 64, 15, 18, 'f', 1]
|
||
],
|
||
'wrap-sm': [
|
||
['jalapeno-2', 6, 6, 11, -20, 'b', 1], ['onion-2', 96, 6, 13, 20, 'b', 1],
|
||
['lettuce-3', -8, 70, 20, -18, 'f', 1], ['tomato', 104, 64, 15, 22, 'f', 1]
|
||
],
|
||
tenders: [
|
||
['crumb-6', 2, 20, 11, 0, 'b', 1], ['crumb-7', 98, 14, 10, 40, 'b', 0], ['chili', 104, 78, 20, 20, 'b', 1],
|
||
['crumb-1', 14, -6, 12, 10, 'f', 1], ['crumb-3', 64, -14, 11, 90, 'f', 1], ['crumb-4', 90, 8, 11, 120, 'f', 0],
|
||
['crumb-8', -4, 84, 12, 70, 'f', 1], ['jalapeno-1', 40, 108, 11, -10, 'f', 0], ['crumb-5', 78, 106, 10, 30, 'f', 0]
|
||
],
|
||
wings: [
|
||
['crumb-6', 2, 34, 11, 0, 'b', 0], ['crumb-7', 98, 22, 10, 40, 'b', 1], ['chili', 102, 74, 20, 20, 'b', 0], ['jalapeno-1', -6, 66, 12, -30, 'b', 1],
|
||
['crumb-1', 14, 10, 12, 10, 'f', 1], ['crumb-3', 60, 8, 11, 90, 'f', 1], ['crumb-4', 88, 12, 11, 120, 'f', 1],
|
||
['crumb-5', 104, 46, 10, 0, 'f', 0], ['crumb-8', -2, 92, 11, 70, 'f', 0], ['jalapeno-2', 86, 104, 12, -10, 'f', 1], ['crumb-2', 48, 106, 9, 30, 'f', 0, 1]
|
||
]
|
||
};
|
||
var mobileBits = window.matchMedia('(max-width: 820px)').matches;
|
||
var bitScale = mobileBits ? 1.35 : 1;
|
||
var blasts = $$('[data-blast]').map(function (box) {
|
||
var set = PRESETS[box.getAttribute('data-blast')];
|
||
if (!set) return null;
|
||
var lazy = box.getAttribute('data-blast-mode') !== 'hero';
|
||
var holder = document.createElement('span');
|
||
holder.className = 'blast__bits';
|
||
holder.setAttribute('aria-hidden', 'true');
|
||
var bits = [];
|
||
set.forEach(function (b) {
|
||
if (mobileBits && !b[6]) return;
|
||
// Verschachtelt, damit sich nichts in die Quere kommt:
|
||
// .bit (Scroll-Explosion) > .bit__c (CSS-Zentrierung) > .bit__in (Intro) > .bit__fl (Schweben) > img (feste Drehung)
|
||
var size = b[3] * bitScale;
|
||
var el = document.createElement('span');
|
||
el.className = 'bit bit--' + b[5];
|
||
el.style.left = b[1] + '%'; el.style.top = b[2] + '%'; el.style.width = size + '%';
|
||
var c = document.createElement('span'); c.className = 'bit__c';
|
||
var inner = document.createElement('span'); inner.className = 'bit__in';
|
||
var fl = document.createElement('span'); fl.className = 'bit__fl';
|
||
var img = document.createElement('img');
|
||
img.alt = ''; img.decoding = 'async'; img.draggable = false;
|
||
if (lazy) img.loading = 'lazy';
|
||
img.style.rotate = b[4] + 'deg';
|
||
if (b[7]) img.style.scale = '-1 1';
|
||
img.onerror = function () { el.remove(); };
|
||
// Größe als Fläche: lange, schmale Zutaten (Salatblatt, Bacon) wirken so gleich groß wie runde
|
||
img.onload = function () {
|
||
var ratio = img.naturalWidth / img.naturalHeight;
|
||
el.style.width = (size * Math.sqrt(ratio)).toFixed(2) + '%';
|
||
};
|
||
img.src = BITS + b[0] + '.webp';
|
||
fl.appendChild(img); inner.appendChild(fl); c.appendChild(inner); el.appendChild(c); holder.appendChild(el);
|
||
bits.push({ el: el, inner: inner, fl: fl, x: b[1], y: b[2], layer: b[5] });
|
||
});
|
||
box.appendChild(holder);
|
||
return { box: box, bits: bits, hero: !lazy };
|
||
}).filter(Boolean);
|
||
|
||
/* ---------- Flammen nur animieren, solange sie sichtbar sind ---------- */
|
||
if ('IntersectionObserver' in window) {
|
||
var flameIO = new IntersectionObserver(function (entries) {
|
||
entries.forEach(function (en) { en.target.classList.toggle('is-live', en.isIntersecting); });
|
||
});
|
||
$$('.flames, .cta__fire, .fx__sparks').forEach(function (el) { flameIO.observe(el); });
|
||
} else {
|
||
$$('.flames, .cta__fire, .fx__sparks').forEach(function (el) { el.classList.add('is-live'); });
|
||
}
|
||
|
||
/* ---------- Funken (Buckets) ---------- */
|
||
if (!reduce) {
|
||
$$('[data-sparks]').forEach(function (wrap) {
|
||
for (var i = 0; i < 22; i++) {
|
||
var sp = document.createElement('i');
|
||
sp.style.left = (Math.random() * 100).toFixed(1) + '%';
|
||
sp.style.setProperty('--sz', (2 + Math.random() * 3.5).toFixed(1) + 'px');
|
||
sp.style.setProperty('--d', (4 + Math.random() * 5).toFixed(2) + 's');
|
||
sp.style.setProperty('--dl', (-Math.random() * 9).toFixed(2) + 's');
|
||
sp.style.setProperty('--dx', ((Math.random() - .5) * 140).toFixed(0) + 'px');
|
||
sp.style.setProperty('--h', (300 + Math.random() * 600).toFixed(0) + 'px');
|
||
wrap.appendChild(sp);
|
||
}
|
||
});
|
||
}
|
||
|
||
/* ---------- 3D-Neigung der Karten (nur Maus) ---------- */
|
||
if (!reduce && window.matchMedia('(hover: hover) and (pointer: fine)').matches) {
|
||
$$('[data-tilt]').forEach(function (card) {
|
||
var r = null;
|
||
card.addEventListener('pointerenter', function () { r = card.getBoundingClientRect(); });
|
||
card.addEventListener('pointermove', function (e) {
|
||
if (!r) r = card.getBoundingClientRect();
|
||
var nx = (e.clientX - r.left) / r.width - .5, ny = (e.clientY - r.top) / r.height - .5;
|
||
card.classList.add('is-tilting');
|
||
card.style.setProperty('--ty', (nx * 14).toFixed(2) + 'deg');
|
||
card.style.setProperty('--tx', (-ny * 12).toFixed(2) + 'deg');
|
||
});
|
||
card.addEventListener('pointerleave', function () {
|
||
r = null;
|
||
card.classList.remove('is-tilting');
|
||
card.style.setProperty('--tx', '0deg'); card.style.setProperty('--ty', '0deg');
|
||
});
|
||
});
|
||
}
|
||
|
||
/* ---------- Route (Take away) ---------- */
|
||
var route = $('[data-route]');
|
||
var routeState = null;
|
||
function buildRoute() {
|
||
if (!route) return null;
|
||
var svg = $('.route__svg', route), path = $('path', svg), stops = $$('.stop', route);
|
||
if (!stops.length || window.getComputedStyle(svg).display === 'none') return null;
|
||
var box = route.getBoundingClientRect();
|
||
var pts = stops.map(function (s) {
|
||
var r = s.getBoundingClientRect();
|
||
return { x: r.left - box.left + r.width * .5, y: r.top - box.top - 34 };
|
||
});
|
||
var start = { x: Math.max(40, pts[0].x - box.width * .2), y: pts[0].y + 6 };
|
||
var d = 'M' + start.x + ' ' + start.y;
|
||
var prev = start;
|
||
pts.forEach(function (p, i) {
|
||
var dx = p.x - prev.x;
|
||
var lift = i % 2 ? 90 : -70;
|
||
d += ' C' + (prev.x + dx * .45) + ' ' + (prev.y + lift) + ' ' + (p.x - dx * .45) + ' ' + (p.y - lift) + ' ' + p.x + ' ' + p.y;
|
||
prev = p;
|
||
});
|
||
path.setAttribute('d', d);
|
||
return { path: path, len: path.getTotalLength(), rider: $('.route__rider', route) };
|
||
}
|
||
function placeRider(progress) {
|
||
if (!routeState) return;
|
||
var pt = routeState.path.getPointAtLength(routeState.len * progress);
|
||
var ahead = routeState.path.getPointAtLength(Math.min(routeState.len, routeState.len * progress + 4));
|
||
var angle = Math.atan2(ahead.y - pt.y, ahead.x - pt.x) * 180 / Math.PI;
|
||
var w = routeState.rider.offsetWidth, h = routeState.rider.offsetHeight;
|
||
routeState.rider.style.transform = 'translate(' + (pt.x - w / 2) + 'px,' + (pt.y - h / 2) + 'px) rotate(' + (angle * .25) + 'deg)';
|
||
}
|
||
|
||
/* ---------- Ohne GSAP: fertig ---------- */
|
||
if (!hasGsap) {
|
||
routeState = buildRoute();
|
||
placeRider(1);
|
||
return;
|
||
}
|
||
|
||
var gsap = window.gsap, ST = window.ScrollTrigger;
|
||
gsap.registerPlugin(ST);
|
||
var rand = gsap.utils.random;
|
||
|
||
function splitWords(el) {
|
||
(function walk(node) {
|
||
Array.prototype.slice.call(node.childNodes).forEach(function (ch) {
|
||
if (ch.nodeType === 3) {
|
||
var frag = document.createDocumentFragment();
|
||
ch.textContent.split(/(\s+)/).forEach(function (p) {
|
||
if (!p) return;
|
||
if (/^\s+$/.test(p)) frag.appendChild(document.createTextNode(p));
|
||
else { var s = document.createElement('span'); s.className = 'pw'; s.textContent = p; frag.appendChild(s); }
|
||
});
|
||
ch.parentNode.replaceChild(frag, ch);
|
||
} else if (ch.nodeType === 1 && ch.tagName !== 'BR') walk(ch);
|
||
});
|
||
})(el);
|
||
return $$('.pw', el);
|
||
}
|
||
function splitChars(el) {
|
||
var txt = el.textContent; el.textContent = '';
|
||
var sr = document.createElement('span'); sr.className = 'sr-only'; sr.textContent = txt; el.appendChild(sr);
|
||
return txt.split('').map(function (c) {
|
||
var s = document.createElement('span'); s.className = 'pl'; s.textContent = c === ' ' ? '\u00a0' : c;
|
||
s.setAttribute('aria-hidden', 'true'); s.style.display = 'inline-block';
|
||
el.appendChild(s); return s;
|
||
});
|
||
}
|
||
|
||
if (reduce) {
|
||
routeState = buildRoute();
|
||
placeRider(1);
|
||
return;
|
||
}
|
||
|
||
/* ---- Hero-Intro ---- */
|
||
var hero = $('.hero');
|
||
if (hero) {
|
||
var chars = [];
|
||
$$('[data-hero-big] .part', hero).forEach(function (p) { chars = chars.concat(splitChars(p)); });
|
||
var burgerImg = $('.hero__burger .blast__food', hero);
|
||
var tl = gsap.timeline({ delay: .05 });
|
||
tl.set($$('[data-intro]', hero), { opacity: 1 })
|
||
.fromTo('.hero__eyebrow', { y: 24, opacity: 0 }, { y: 0, opacity: 1, duration: .5, ease: 'power3.out' }, 0)
|
||
.fromTo(chars, { scale: 0, opacity: 0, y: function () { return rand(40, 100); }, rotation: function () { return rand(-18, 18); }, transformOrigin: '50% 90%' },
|
||
{ scale: 1, opacity: 1, y: 0, rotation: 0, duration: .75, stagger: .045, ease: 'back.out(2.2)' }, .05)
|
||
.fromTo(burgerImg, { scale: .45, y: 140, rotation: -12, opacity: 0 }, { scale: 1, y: 0, rotation: 0, opacity: 1, duration: 1.1, ease: 'back.out(1.5)' }, .3)
|
||
.fromTo($$('[data-sticker]', hero), { '--s': 0 }, { '--s': 1, duration: .6, stagger: .12, ease: 'back.out(3)' }, .9)
|
||
.fromTo(['.hero__bottom .lead', '.hero__cta'], { y: 30, opacity: 0 }, { y: 0, opacity: 1, duration: .7, stagger: .1, ease: 'power3.out' }, .8)
|
||
.add(function () {
|
||
gsap.to(burgerImg, { y: -14, duration: 2.4, yoyo: true, repeat: -1, ease: 'sine.inOut' });
|
||
});
|
||
|
||
if (window.matchMedia('(pointer: fine)').matches) {
|
||
var qx = gsap.quickTo(burgerImg, 'x', { duration: .8, ease: 'power3.out' });
|
||
var qr = gsap.quickTo(burgerImg, 'rotation', { duration: .8, ease: 'power3.out' });
|
||
hero.addEventListener('mousemove', function (e) {
|
||
var r = hero.getBoundingClientRect();
|
||
var nx = (e.clientX - r.left) / r.width - .5;
|
||
qx(nx * 40); qr(nx * 6);
|
||
});
|
||
}
|
||
|
||
var mm = gsap.matchMedia();
|
||
mm.add('(min-width: 821px)', function () {
|
||
var parts = $$('[data-hero-big] .part', hero);
|
||
gsap.to(parts[0], { xPercent: -10, ease: 'none', scrollTrigger: { trigger: hero, start: 'top top', end: 'bottom top', scrub: true } });
|
||
gsap.to(parts[1], { xPercent: 12, ease: 'none', scrollTrigger: { trigger: hero, start: 'top top', end: 'bottom top', scrub: true } });
|
||
gsap.to('.hero__burger', { yPercent: -12, ease: 'none', scrollTrigger: { trigger: hero, start: 'top top', end: 'bottom top', scrub: true } });
|
||
});
|
||
}
|
||
|
||
/* ---- Seiten-Hero (Unterseiten) ---- */
|
||
var pageHero = $('.page-hero');
|
||
if (pageHero) {
|
||
var ph = $('[data-hero-big]', pageHero);
|
||
if (ph) {
|
||
var pchars = splitChars(ph);
|
||
gsap.fromTo(pchars, { scale: 0, opacity: 0, y: function () { return rand(40, 90); }, rotation: function () { return rand(-16, 16); }, transformOrigin: '50% 90%' },
|
||
{ scale: 1, opacity: 1, y: 0, rotation: 0, duration: .75, stagger: .05, ease: 'back.out(2.2)', delay: .05 });
|
||
}
|
||
var phImg = $('.page-hero__img img', pageHero);
|
||
if (phImg) {
|
||
gsap.fromTo(phImg, { scale: .5, y: 120, rotation: 10, opacity: 0 }, { scale: 1, y: 0, rotation: 0, opacity: 1, duration: 1.1, ease: 'back.out(1.5)', delay: .25 });
|
||
gsap.to(phImg, { y: -12, duration: 2.4, yoyo: true, repeat: -1, ease: 'sine.inOut', delay: 1.4 });
|
||
}
|
||
}
|
||
|
||
/* ---- Wort-Pop für Headlines ---- */
|
||
$$('[data-pop]').forEach(function (el) {
|
||
var words = splitWords(el);
|
||
gsap.fromTo(words,
|
||
{ opacity: 0, scale: 0, y: function () { return rand(18, 40); }, rotation: function () { return rand(-14, 14); }, transformOrigin: '50% 90%' },
|
||
{ opacity: 1, scale: 1, y: 0, rotation: 0, duration: .72, stagger: .06, ease: 'back.out(2.35)',
|
||
scrollTrigger: { trigger: el, start: 'top 88%', once: true } });
|
||
});
|
||
|
||
/* ---- Reveal (nutzt translate/opacity, damit CSS-Rotationen bleiben) ---- */
|
||
var reveals = $$('[data-reveal]').filter(function (el) { return !el.closest('.hero'); });
|
||
ST.batch(reveals, {
|
||
start: 'top 90%', once: true,
|
||
onEnter: function (batch) {
|
||
gsap.fromTo(batch, { opacity: 0, '--ry': '40px' }, { opacity: 1, '--ry': '0px', duration: .9, stagger: .1, ease: 'power3.out' });
|
||
}
|
||
});
|
||
|
||
/* ---- Sticker "aufkleben" ---- */
|
||
$$('[data-sticker]').filter(function (el) { return !el.closest('.hero'); }).forEach(function (el) {
|
||
gsap.fromTo(el, { '--s': 0 }, { '--s': 1, duration: .7, ease: 'back.out(3)', scrollTrigger: { trigger: el, start: 'top 88%', once: true } });
|
||
});
|
||
|
||
/* ---- Zutaten: schweben, beim Laden aus dem Essen platzen, beim Scrollen wegfliegen ---- */
|
||
blasts.forEach(function (bl) {
|
||
var W = function () { return bl.box.offsetWidth; };
|
||
var vec = function (b) {
|
||
return { x: (b.x - 50) / 100 * W(), y: (b.y - 50) / 100 * bl.box.offsetHeight };
|
||
};
|
||
// Explosionsziel: kräftig nach außen, mit Drall zur Seite und nach oben
|
||
var away = function (b, k) {
|
||
var v = vec(b), len = Math.sqrt(v.x * v.x + v.y * v.y) || 1;
|
||
var side = b.side;
|
||
return {
|
||
x: v.x * .5 + (v.x / len) * W() * k + (-v.y / len) * W() * .18 * side,
|
||
y: v.y * .3 + (v.y / len) * W() * k * .55 - W() * .45 * k
|
||
};
|
||
};
|
||
var floats = [];
|
||
var tl = gsap.timeline({ paused: true });
|
||
bl.bits.forEach(function (b, i) {
|
||
b.side = i % 2 ? 1 : -1;
|
||
floats.push(gsap.to(b.fl, { y: '+=' + rand(-16, -8), rotation: '+=' + rand(-8, 8), duration: rand(1.8, 3.2), yoyo: true, repeat: -1, ease: 'sine.inOut', delay: rand(0, 1.5), paused: true }));
|
||
var spin = rand(160, 340) * b.side;
|
||
var grow = b.layer === 'b' ? 1.15 : 1.9;
|
||
if (bl.hero) {
|
||
gsap.from(b.inner, { x: function () { return -vec(b).x; }, y: function () { return -vec(b).y; }, scale: .2, opacity: 0, rotation: -spin / 3, duration: 1.2, ease: 'back.out(1.3)', delay: .55 + i * .035 });
|
||
tl.to(b.el, { x: function () { return away(b, 1).x; }, y: function () { return away(b, 1).y; }, rotation: spin, scale: grow, duration: 1, ease: 'power2.in' }, 0)
|
||
.to(b.el, { opacity: 0, duration: .25, ease: 'none' }, .75);
|
||
} else {
|
||
tl.fromTo(b.el, { x: function () { return -vec(b).x * .6; }, y: function () { return -vec(b).y * .6; }, scale: .4, opacity: 0, rotation: -spin / 3 },
|
||
{ x: 0, y: 0, scale: 1, opacity: 1, rotation: 0, duration: .4, ease: 'power2.out' }, 0)
|
||
.to(b.el, { x: function () { return away(b, .8).x; }, y: function () { return away(b, .8).y; }, scale: grow, rotation: spin, duration: .45, ease: 'power2.in' }, .55)
|
||
.to(b.el, { opacity: 0, duration: .12, ease: 'none' }, .88);
|
||
}
|
||
});
|
||
var sec = bl.box.closest('section') || bl.box;
|
||
ST.create({
|
||
animation: tl, invalidateOnRefresh: true, scrub: .6,
|
||
trigger: bl.hero ? sec : bl.box,
|
||
start: bl.hero ? 'top top' : 'top 92%',
|
||
end: bl.hero ? '65% top' : 'bottom 12%'
|
||
});
|
||
// Schweben nur, solange die Zutaten im Bild sind (spart Akku und Rechenzeit)
|
||
ST.create({
|
||
trigger: bl.box, start: 'top bottom', end: 'bottom top',
|
||
onToggle: function (self) { floats.forEach(function (t) { self.isActive ? t.play() : t.pause(); }); }
|
||
});
|
||
});
|
||
|
||
/* ---- Feuerwirbel dreht sich langsam mit ---- */
|
||
$$('[data-swirl]').forEach(function (sw) {
|
||
var sec = sw.closest('section') || sw.parentNode;
|
||
gsap.fromTo(sw, { rotation: -12 }, { rotation: 28, ease: 'none', scrollTrigger: { trigger: sec, start: 'top bottom', end: 'bottom top', scrub: true } });
|
||
});
|
||
|
||
/* ---- Parallax Fotoband ---- */
|
||
$$('[data-parallax]').forEach(function (img) {
|
||
gsap.fromTo(img, { yPercent: -7 }, { yPercent: 7, ease: 'none', scrollTrigger: { trigger: img.parentNode, start: 'top bottom', end: 'bottom top', scrub: true } });
|
||
});
|
||
|
||
/* ---- Route ---- */
|
||
if (route) {
|
||
routeState = buildRoute();
|
||
ST.create({
|
||
trigger: route, start: 'top 75%', end: 'bottom 55%', scrub: .6,
|
||
onUpdate: function (self) { placeRider(self.progress); },
|
||
onRefresh: function (self) { routeState = buildRoute(); placeRider(self.progress); }
|
||
});
|
||
placeRider(0);
|
||
}
|
||
|
||
/* ---- Footer: Zutaten fliegen von unten ins Bild und fallen wieder raus ---- */
|
||
var jug = $('[data-juggle]');
|
||
if (jug) {
|
||
var foot = jug.closest('footer') || jug;
|
||
var G = 1100; // Schwerkraft in px/s², hält Flugkurve und -zeit realistisch
|
||
var running = false;
|
||
// Versatz, ab dem eine Zutat (auch gedreht) komplett unter dem Footer-Rand steckt
|
||
var below = function (el) {
|
||
return foot.getBoundingClientRect().bottom - jug.getBoundingClientRect().top - el.offsetTop + el.offsetHeight * .3 + 2;
|
||
};
|
||
var toss = function (item, wait) {
|
||
var el = item.el;
|
||
var low = below(el);
|
||
var apex = -Math.min(window.innerHeight * .55, Math.max(jug.offsetHeight, 110) * rand(.8, 1.25) + 20);
|
||
var dir = Math.random() < .5 ? -1 : 1;
|
||
var drift = window.innerWidth * rand(.03, .08) * dir;
|
||
var rot = rand(-30, 30);
|
||
var up = Math.sqrt(2 * (low - apex) / G);
|
||
item.tl = gsap.timeline({ delay: wait, paused: !running, onComplete: function () { toss(item, rand(.15, .8)); } })
|
||
.fromTo(el, { y: low }, { y: apex, duration: up, ease: 'power2.out' })
|
||
.to(el, { y: low, duration: up, ease: 'power2.in' })
|
||
.fromTo(el, { x: -drift / 2, rotation: rot }, { x: drift / 2, rotation: rot + dir * rand(200, 480), duration: up * 2, ease: 'none' }, 0);
|
||
};
|
||
var items = $$('.juggle', jug).map(function (el, i) {
|
||
var item = { el: el, tl: null };
|
||
gsap.set(el, { y: below(el), visibility: 'visible' });
|
||
toss(item, .1 + i * .3);
|
||
return item;
|
||
});
|
||
var setRunning = function (on) {
|
||
running = on;
|
||
items.forEach(function (it) { on ? it.tl.play() : it.tl.pause(); });
|
||
};
|
||
ST.create({
|
||
trigger: jug, start: 'top bottom', end: 'bottom top',
|
||
onToggle: function (self) { setRunning(self.isActive); }
|
||
});
|
||
document.addEventListener('visibilitychange', function () {
|
||
setRunning(!document.hidden && ST.isInViewport(jug));
|
||
});
|
||
}
|
||
|
||
window.addEventListener('load', function () { ST.refresh(); });
|
||
})();
|