PageSourceSearch

https://www.antesy.io/js/scroll.js?v=101

js antesy.io collected 2026-10-04 07:53:03 UTC 14,122 bytes, 344 lines download raw bytes

1(() => {
2    const SCROLLBAR_GRAB_ZONE = 24; // px depuis le bord droit
3  let scrollbarGrabbed = false;
4
5  function isOnScrollbar(e) {
6    return e.clientX >= window.innerWidth - SCROLLBAR_GRAB_ZONE;
7  }
8
9  window.addEventListener("mousedown", (e) => {
10    if (isOnScrollbar(e)) {
11      scrollbarGrabbed = true;
12      window.__AntesySmoothScroll?.disable();
13    }
14  }, true);
15
16  window.addEventListener("mouseup", () => {
17    if (scrollbarGrabbed) {
18      scrollbarGrabbed = false;
19      // ?? on ne réactive PAS immédiatement
20      requestAnimationFrame(() => {
21        window.__AntesySmoothScroll?.enable();
22      });
23    }
24  }, true);
25    // ===========================
26    //  SMOOTH SCROLL (desktop)
27    // ===========================
28    const IS_COARSE = window.matchMedia?.("(pointer: coarse)")?.matches ?? false;
29    const SCROLLER = document.scrollingElement || document.documentElement;
30
31    // Smooth-scroll custom UNIQUEMENT sur la home (seule page 3D désormais).
32    // Ailleurs : scroll natif ? évite le "scroll tout seul".
33    const _p = (location.pathname || "/").replace(/\/+$/, "").toLowerCase();
34    const IS_HOME = _p === "" || _p === "/home" || _p === "/en" || _p === "/en/home";
35
36    // Desktop only: if coarse pointer, do nothing.
37    if (IS_COARSE) {
38        window.__AntesySmoothScroll = { enable() { }, disable() { }, isEnabled: () => false };
39        return;
40    }
41
42    // Tunables
43    const START_SLOW_FACTOR = 0.38;
44    const EASE = 0.45;
45    const KEY_STEP = 60;
46
47    let active = false;
48    let factor = START_SLOW_FACTOR;
49
50    let target = SCROLLER.scrollTop || 0;
51    let current = target;
52    let rafId = null;
53
54    const clamp = (v, min, max) => Math.min(max, Math.max(min, v));
55    const maxScroll = () => (SCROLLER.scrollHeight - window.innerHeight);
56
57    const loop = () => {
58        // If disabled mid-loop, stop cleanly
59        if (!active) return;
60
61        current += (target - current) * EASE;
62
63        if (Math.abs(target - current) < 0.3) current = target;
64
65        // IMPORTANT: use scrollTo but keep current bounded
66        current = clamp(current, 0, maxScroll());
67        window.scrollTo(0, current);
68
69        rafId = requestAnimationFrame(loop);
70    };
71
72    const onWheel = (e) => {
73        // Compute first, preventDefault ONLY if we will handle it.
74        const delta = e.deltaY || e.wheelDelta || 0;
75
76        const atTop = (current <= 0.5) && (delta < 0);
77        const atBottom = (current >= maxScroll() - 0.5) && (delta > 0);
78
79        // If pushing against bounds, let the browser handle it (no preventDefault)
80        if (atTop || atBottom) {
81            // Keep internal state coherent
82            target = clamp(target, 0, maxScroll());
83            current = clamp(current, 0, maxScroll());
84            return;
85        }
86
87        e.preventDefault();
88        target = clamp(target + delta * factor, 0, maxScroll());
89    };
90
91    const onKeyDown = (e) => {
92        if (e.key === "ArrowDown") {
93            e.preventDefault();
94            target = clamp(target + KEY_STEP * factor, 0, maxScroll());
95        } else if (e.key === "ArrowUp") {
96            e.preventDefault();
97            target = clamp(target - KEY_STEP * factor, 0, maxScroll());
98        }
99    };
100
101    function enableSmooth() {
102        if (active) return;
103        active = true;
104
105        // Align internal values to real scroll position
106        const s = SCROLLER.scrollTop || 0;
107        current = target = clamp(s, 0, maxScroll());
108
109        window.addEventListener("wheel", onWheel, { passive: false });
110        window.addEventListener("keydown", onKeyDown, { passive: false });
111
112        rafId = requestAnimationFrame(loop);
113    }
114
115    function disableSmooth() {
116        if (!active) return;
117        active = false;
118
119        window.removeEventListener("wheel", onWheel);
120        window.removeEventListener("keydown", onKeyDown);
121
122        if (rafId) cancelAnimationFrame(rafId);
123        rafId = null;
124
125        // Snap to a stable position (avoid fractional jitter)
126        current = clamp(current, 0, maxScroll());
127        window.scrollTo(0, Math.round(current));
128        target = current;
129    }
130
131    window.__AntesySmoothScroll = {
132        enable: enableSmooth,
133        disable: disableSmooth,
134        isEnabled: () => active,
135        // Programmatic scroll : met à jour target directement pour éviter l'écrasement du loop
136        scrollTo: (y) => {
137            target = clamp(y, 0, maxScroll());
138            current = target; // snap immédiat, pas d'animation
139            if (!active) window.scrollTo(0, Math.round(target));
140            else window.scrollTo(0, Math.round(target));
141        }
142    };
143
144    // Démarre activé UNIQUEMENT sur la home (sinon scroll natif).
145    if (IS_HOME) enableSmooth();
146})();
147
148// =====================================
149//  SCROLL HANDLER (canvas lock/unlock)
150// =====================================
151let canvasFixed = false;
152window.cameraLocked2 = false;
153
154// HYSTERESIS: avoid ON/OFF around same pixel
155const LOCK_SCROLL_Y = 260;     // lock when going down past this
156const UNLOCK_SCROLL_Y = 180;   // unlock only when going back up above this (lower)
157
158// UI show/hide (hero)
159const HIDE_UI_AFTER_Y = 20;
160const SHOW_UI_BEFORE_Y = 50;   // hysteresis
161
162// Avoid toggling smooth scroll inside the scroll event synchronously
163let pendingSmoothToggle = null;
164function requestSmooth(enabled) {
165    // if on mobile / not defined, ignore
166    const api = window.__AntesySmoothScroll;
167    if (!api) return;
168
169    // cancel previous request in same frame
170    if (pendingSmoothToggle) cancelAnimationFrame(pendingSmoothToggle);
171
172    pendingSmoothToggle = requestAnimationFrame(() => {
173        pendingSmoothToggle = null;
174        if (enabled) api.enable();
175        else api.disable();
176    });
177}
178
179let ticking = false;
180
181window.addEventListener(
182    "scroll",
183    () => {
184        if (ticking) return;
185        ticking = true;
186
187        requestAnimationFrame(() => {
188            ticking = false;
189
190            try {
191                // Dock + console "Parlez-nous de votre projet" : retirés dès qu'on scrolle.
192                // Classe sur <body> (immune aux re-renders Blazor de #antesyDock).
193                const _sY = window.scrollY || document.documentElement.scrollTop || 0;
194                document.body.classList.toggle('is-scrolled', _sY > 20);
195
196                // Pendant le jeu, aucune transition de scroll (canvas-fixed, Animate, hide UI)
197                if (document.body.classList.contains('game-active')) return;
198
199                const canvas = document.querySelector("#babylonCanvas");
200                if (!canvas) return;
201
202                const titleContainer = document.querySelector(".TextAppears");
203                const HeroTexte = document.querySelector(".hero-over-babylon");
204                const dock = document.querySelector(".dock");
205                const consoleWin =
206                    document.getElementById("antesyConsole") ||
207                    document.querySelector(".console-window");
208
209                const scrollY =
210                    window.scrollY || document.documentElement.scrollTop || 0;
211
212                // ---- Lock canvas ----
213                if (!canvasFixed && scrollY >= LOCK_SCROLL_Y) {
214                    canvas.classList.add("canvas-fixed");
215                    if (titleContainer) titleContainer.classList.add("Animate");
216
217                    canvasFixed = true;
218                    window.cameraLocked2 = true;
219
220                    // IMPORTANT: defer to avoid input jank
221                    requestAnimationFrame(() => requestSmooth(false));
222                }
223
224                // ---- Unlock canvas ----
225                if (canvasFixed && scrollY <= UNLOCK_SCROLL_Y) {
226                    canvas.classList.remove("canvas-fixed");
227                    if (titleContainer) titleContainer.classList.remove("Animate");
228
229                    canvasFixed = false;
230                    window.cameraLocked2 = false;
231
232                    requestAnimationFrame(() => requestSmooth(true));
233                }
234
235                // ---- UI hide/show with hysteresis ----
236                if (!canvasFixed && scrollY >= HIDE_UI_AFTER_Y) {
237                    if (dock && !dock.classList.contains("is-scroll-hidden")) {
238                        dock.classList.add("is-scroll-hidden");
239                    }
240                    if (consoleWin && !consoleWin.classList.contains("is-scroll-hidden")) {
241                        consoleWin.classList.add("is-scroll-hidden");
242                    }
243                    if (HeroTexte && !HeroTexte.classList.contains("IsInvisible")) {
244                        HeroTexte.classList.add("IsInvisible");
245                    }
246                }
247
248                if (!canvasFixed && scrollY <= SHOW_UI_BEFORE_Y) {
249                    if (dock && dock.classList.contains("is-scroll-hidden")) {
250                        dock.classList.remove("is-scroll-hidden");
251                    }
252                    if (consoleWin && consoleWin.classList.contains("is-scroll-hidden")) {
253                        consoleWin.classList.remove("is-scroll-hidden");
254                    }
255                    if (HeroTexte && HeroTexte.classList.contains("IsInvisible")) {
256                        HeroTexte.classList.remove("IsInvisible");
257                    }
258                }
259            } catch (e) {
260                console.error("Erreur scroll handler:", e);
261            }
262        });
263    },
264    { passive: true }
265);
266
267// =====================================================
268//  MODE DE PAGE (appelé par MainLayout, CSP-safe : fonction nommée, pas d'eval)
269//  light = page hero léger (barre blanche + --babylon-height:0)
270//  is3d  = page avec scène Babylon (smooth-scroll custom activé)
271// =====================================================
272window.antesyPageMode = function (light, is3d) {
273    try { document.documentElement.classList.toggle('light-hero', !!light); } catch (e) { }
274    try {
275        if (window.__AntesySmoothScroll) {
276            if (is3d) window.__AntesySmoothScroll.enable();
277            else window.__AntesySmoothScroll.disable();
278        }
279    } catch (e) { }
280};
281
282// Recalcule le mode depuis l'URL — indépendant de l'interop Blazor (marche sur les
283// pages rendues en statique). Doit rester synchronisé avec MainLayout.IsLight.
284window.antesyApplyPageMode = function () {
285    try {
286        var p = (location.pathname || '/').replace(/\/+$/, '').toLowerCase();
287        var home = p === '' || p === '/home' || p === '/en' || p === '/en/home';
288        var imm = p.indexOf('/configurateur-voiture') >= 0 || p.indexOf('/car-configurator') >= 0;
289        var lab = p.indexOf('/lab') === 0;
290        var light = !home && !imm && !lab;
291        window.antesyPageMode(light, !light && !imm);
292    } catch (e) { }
293};
294// Navigation "enhanced" (blazor.web.js) = pas de rechargement complet ? le script <head>
295// ne re-tourne pas. On ré-applique donc à chaque navigation enhanced.
296try { document.addEventListener('DOMContentLoaded', window.antesyApplyPageMode); } catch (e) { }
297try { if (window.Blazor && Blazor.addEventListener) Blazor.addEventListener('enhancedload', window.antesyApplyPageMode); } catch (e) { }
298try { window.addEventListener('blazor:enhancedload', window.antesyApplyPageMode); } catch (e) { }
299
300// Init de la bande de triangles (TriLineHero) SANS dépendre de l'interactivité Blazor.
301// Les pages light-hero sont souvent rendues en statique ? le OnAfterRender du composant
302// ne tourne pas ? l'effet de scatter au scroll n'était jamais câblé. On l'initialise ici,
303// au chargement et à chaque navigation enhanced (dispose si la page n'a pas de #tlHero).
304window.antesyInitTriLine = function () {
305    try {
306        var el = document.getElementById('tlHero');
307        import('/js/triLineScroll.js?v=101').then(function (m) {
308            if (el) m.initTriLine('tlHero', { rangeFactor: 0.35, autoPlay: true, clickPreview: false });
309            else if (m.disposeTriLine) m.disposeTriLine('tlHero');
310        }).catch(function () { });
311    } catch (e) { }
312};
313try { document.addEventListener('DOMContentLoaded', window.antesyInitTriLine); } catch (e) { }
314try { if (window.Blazor && Blazor.addEventListener) Blazor.addEventListener('enhancedload', window.antesyInitTriLine); } catch (e) { }
315try { window.addEventListener('blazor:enhancedload', window.antesyInitTriLine); } catch (e) { }
316
317// -----------------------------------------------------------------------------
318// Scroll-to-top a CHAQUE navigation enhanced (blazor.web.js).
319// Sans ca, la position de scroll de la page precedente est conservee : un CTA situe
320// en bas de page (ex: GamePulse "Lancer votre projet avec nous", lien <a href>) ouvre
321// la page cible deja scrollee en bas. On ignore les liens vers une ancre (#section).
322// Enregistrement robuste : window.Blazor peut ne pas etre pret quand ce script (defer)
323// s'execute -> on reessaie jusqu'a ce que Blazor.addEventListener existe.
324// -----------------------------------------------------------------------------
325window.antesyScrollTopOnNav = function () {
326    try {
327        if (location.hash && location.hash.length > 1) return; // lien #ancre : ne pas remonter
328        // Aligne aussi le smooth-scroll custom (home) pour eviter qu'il "restaure" la position.
329        if (window.__AntesySmoothScroll && window.__AntesySmoothScroll.scrollTo) {
330            try { window.__AntesySmoothScroll.scrollTo(0); } catch (e) { }
331        }
332        window.scrollTo(0, 0);
333    } catch (e) { }
334};
335(function registerScrollTopOnNav() {
336    var tries = 0;
337    (function attach() {
338        if (window.Blazor && typeof window.Blazor.addEventListener === 'function') {
339            try { window.Blazor.addEventListener('enhancedload', window.antesyScrollTopOnNav); } catch (e) { }
340        } else if (tries++ < 100) {
341            setTimeout(attach, 50);
342        }
343    })();
344})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.