diff --git a/assets/css/style.css b/assets/css/style.css index 8cb5c12..0df3d43 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -1095,12 +1095,14 @@ body.nav-visible .cat-nav.is-stuck ul { top: var(--nav-h); } /* ============ Fliegende Zutaten ============ */ .blast__bits { position: absolute; inset: 0; pointer-events: none; } -.bit { position: absolute; z-index: 3; } +.bit { position: absolute; z-index: 3; transform-origin: 0 0; } /* Mittelpunkt der Zutat liegt auf left/top: so drehen und wachsen sie um sich selbst */ .bit__c { display: block; translate: -50% -50%; } .bit__in, .bit__fl { display: block; } .bit img { display: block; width: 100%; height: auto; filter: drop-shadow(0 12px 10px rgba(40, 0, 0, .35)); } .bit--b { z-index: 1; } .bit--b img { filter: blur(1.2px) brightness(.88) drop-shadow(0 8px 8px rgba(40, 0, 0, .3)); } +.bit--n { z-index: 5; } +.bit--n img { filter: blur(4px) drop-shadow(0 24px 22px rgba(40, 0, 0, .35)); } .hero__burger .blast__bits, .visit__deco .blast__bits, .wings__hero .blast__bits { z-index: auto; } .champ__food .bit--f { z-index: 3; } @media (max-width: 820px) { .bit--b img { filter: brightness(.9) drop-shadow(0 6px 6px rgba(40, 0, 0, .3)); } } diff --git a/assets/js/main.js b/assets/js/main.js index f653e97..d7183d1 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -195,7 +195,7 @@ /* ---------- 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 */ + Ebenen: b = hinter dem Essen (leicht unscharf), f = davor, n = ganz nah an der Kamera (groß, unscharf) */ var BITS = 'assets/img/bits/'; /* Natürliche Streuung statt fester Positionen (fester Seed, damit es bei jedem Laden gleich aussieht). - food: Umriss des Essens im Bild [Mitte x, Mitte y, Halbbreite, Halbhöhe] in % der Box (abgerundetes Rechteck) @@ -204,11 +204,25 @@ - left/right/top/bottom: erlaubter Bereich (% der Box), z. B. damit nichts über Text oder aus der Karte ragt items: [Datei, Grundgröße in % der Box-Breite, auch mobil] */ var SCATTER = { - burger: { seed: 4, food: [39, 49, 38, 47], hw: .72, reach: 34, bursts: 3, behind: .3, top: -26, bottom: 116, items: [ - ['lettuce-2', 22, 1], ['tomato', 17, 1], ['bacon', 24, 1], ['onion-1', 15, 1], ['jalapeno-1', 12, 1], ['lettuce-1', 24, 1], - ['pickle', 11, 1], ['tomato', 13, 0], ['onion-2', 12, 0], ['jalapeno-2', 11, 0], ['bacon', 18, 0], ['lettuce-3', 18, 0], - ['sesame-1', 3.4, 1], ['sesame-2', 3.8, 0], ['sesame-1', 3, 0], ['sesame-1', 2.6, 0] - ] }, + burger: [ + ['lettuce-3', 70, -37, 21, 24, 'b', 0, 0], + ['onion-2', 27, -43, 13, 30, 'b', 0, 0], + ['lettuce-2', 47, -27, 26, 12, 'f', 1, 0], + ['onion-1', 14, -21, 24, -15, 'f', 1, 0], + ['tomato', 81, 6, 27, 20, 'f', 1, 0], + ['lettuce-1', -9, 19, 25, -30, 'f', 1, 0], + ['lettuce-1', 94, 38, 27, 42, 'f', 1, 1], + ['tomato', -20, 50, 20, -34, 'f', 0, 1], + ['onionring', 103, 62, 23, 15, 'f', 0, 0], + ['bacon', -31, 68, 26, -12, 'f', 0, 1], + ['bacon', 110, 84, 24, 12, 'f', 0, 0], + ['jalapeno-1', 6, 107, 16, 20, 'f', 0, 0], + ['pickle', 73, 101, 16, -14, 'f', 0, 0], + ['sesame-1', 33, -6, 6.5, 25, 'f', 1, 0], + ['sesame-1', 39, -15, 6, -35, 'f', 0, 1], + ['sesame-2', 62, -17, 8, -20, 'f', 0, 0], + ['sesame-1', 58, -5, 5.5, 10, 'f', 1, 1] + ], 'burger-visit': { seed: 8, food: [38, 49, 37, 47], hw: .63, reach: 26, bursts: 2, behind: .3, top: 6, bottom: 112, left: -12, items: [ ['lettuce-2', 20, 1], ['tomato', 16, 1], ['bacon', 22, 1], ['onion-1', 14, 1], ['jalapeno-1', 11, 1], ['lettuce-1', 20, 0], ['pickle', 10, 0], ['tomato', 12, 0], ['sesame-1', 3.4, 0], ['sesame-2', 3.8, 0] @@ -294,7 +308,8 @@ var blasts = $$('[data-blast]').map(function (box) { var spec = SCATTER[box.getAttribute('data-blast')]; if (!spec) return null; - var set = scatter(spec); + // Array = von Hand gesetzte Komposition, Objekt = berechnete Streuung + var set = Array.isArray(spec) ? spec : scatter(spec); var lazy = box.getAttribute('data-blast-mode') !== 'hero'; var holder = document.createElement('span'); holder.className = 'blast__bits'; @@ -304,7 +319,8 @@ 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; + // Hero: Handy etwas weniger vergrößert (1,2 statt 1,35), damit die Fontäne den Burger nur berührt und nicht überdeckt + var size = b[3] * (mobileBits && !lazy ? 1.2 : 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 + '%'; @@ -485,7 +501,8 @@ 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 } }); + // CSS zentriert mit translate(-50%, -50%), GSAP liest das als yPercent -50: also -62 = 12 % nach oben + gsap.to('.hero__burger', { yPercent: -62, ease: 'none', scrollTrigger: { trigger: hero, start: 'top top', end: 'bottom top', scrub: true } }); }); } @@ -544,17 +561,35 @@ y: v.y * .3 + (v.y / len) * W() * k * .55 - W() * .45 * k }; }; + // Hero = Fontäne: Der Burger wurde von unten getroffen. Alles schießt aus der Burger-Mitte nach oben, + // fächert zur Seite auf (Spin dreht nach außen) und die Scroll-Explosion setzt diese Bewegung fort. + var FX = .39, FY = .45; // Ausbruchspunkt in Box-Anteilen (Mitte des Burgers) + var spread = function (b) { // Abstand Ausbruchspunkt -> Endposition in px + return { x: (b.x / 100 - FX) * W(), y: (b.y / 100 - FY) * bl.box.offsetHeight }; + }; + var launch = function (b, k) { // Scroll-Ziel: weiter nach oben + nach außen fächern + var d = spread(b), sp = b.layer === 'n' ? 1.5 : b.layer === 'b' ? .7 : 1; + return { x: d.x * 1.1 * sp * k, y: -(W() * .55 + Math.max(0, -d.y) * .4) * sp * k }; + }; var floats = []; var tl = gsap.timeline({ paused: true }); bl.bits.forEach(function (b, i) { - b.side = i % 2 ? 1 : -1; + b.side = bl.hero ? (b.x < FX * 100 ? -1 : 1) : (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; + var spin = (bl.hero ? rand(70, 210) : rand(160, 340)) * b.side; + var grow = b.layer === 'b' ? 1.15 : b.layer === 'n' ? 2.6 : bl.hero ? 1.6 : 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); + // Intro: aus der Burger-Mitte nach oben; x gleitet, y schießt über das Ziel hinaus und fällt zurück + var dist = Math.sqrt(Math.pow(b.x / 100 - FX, 2) + Math.pow((b.y / 100 - FY) * .72, 2)); + var t0 = .5 + (i % 5) * .03, dur = .95 + dist * .8; + gsap.from(b.inner, { x: function () { return -spread(b).x; }, duration: dur, ease: 'power3.out', delay: t0 }); + gsap.from(b.inner, { y: function () { return -spread(b).y; }, duration: dur + .35, ease: 'back.out(2.1)', delay: t0 }); + gsap.from(b.inner, { scale: .2, opacity: 0, rotation: -spin / 2, duration: dur, ease: 'back.out(1.4)', delay: t0 }); + // Scroll: Position schießt zuerst schnell hoch und läuft dann aus (Fontäne), Spin/Größe wachsen gleichmäßig mit + var st = b.layer === 'n' ? 0 : b.layer === 'f' ? .04 + (i % 4) * .02 : .12; + tl.to(b.el, { x: function () { return launch(b, 1).x; }, y: function () { return launch(b, 1).y; }, duration: 1 - st, ease: 'power1.out' }, st) + .to(b.el, { rotation: spin, scale: grow, duration: 1 - st, ease: 'none' }, st) + .to(b.el, { opacity: 0, duration: .3, ease: 'none' }, .6); } 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)