Changeset a505f31 in Klonkt for src/assets/js/mod/read.js


Ignore:
Timestamp:
08/20/2026 02:27:56 AM (3 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
88838bc
Parents:
d8f8f07
Message:

Lezen: Lenis voor het snappen op desktop, plus twee kleine dingen

WAAROM LENIS, en dat is niet het vloeiende scrollen: de DUUR van een snap is
met native scroll-snap niet in te stellen -- die zit in de browser. Robin
vroeg om een snellere snap, en dat was declaratief onmogelijk. Lenis' snap-
pakket geeft ons duration, easing, distanceThreshold en debounce.

distanceThreshold 12% de vangzone (native: browsereigen, ~200-300px)
duration 0.4s in totaal onder de halve seconde
easing easeOutQuart -- op de helft van de tijd is 94% van de

weg af, daarna zacht uitdempen. Bewust niet
Lenis' standaard easeOutExpo: die schiet weg
en kruipt zo lang na dat het lijkt of hij niet
afmaakt.

debounce 60ms niet de standaard 500; dat voelt als te laat

ALLEEN OP DESKTOP (hover: hover en pointer: fine). Op touch doet Lenis van
zichzelf niets -- syncTouch staat uit -- en is het systeem-scrollen al
soepel. Daar blijft de native CSS-snap staan die er nu is, inclusief het
anker van nul hoog en de richtingsschakelaar. Robins keuze: "enkel voor
desktop, dat is prima, logisch dat het niet op mobiel kan".

Omhoog niet snappen gaat met Lenis via snap.stop()/start(), een stuk
schoner dan de CSS-eigenschap omzetten. Beide wegen delen dezelfde
richtingsdetectie uit wiel, vinger en toets.

NOG NIET UIT: smoothWheel. Dat kwam er na het snappen bij, en het is de kant
die oppassen vraagt -- een Mac-trackpad heeft zijn EIGEN momentum en Lenis'
demping komt daar bovenop. Vandaar lerp 0.2 in plaats van de standaard 0.1.
Robin vond dat op 20-8 nog steeds te zweverig, dus dat getal moet nog omhoog
of smoothWheel moet terug naar false. Die tweede stand werkte ook; dan doet
Lenis alleen de snap. Dat staat zo in het commentaar bij de instellingen,
zodat de volgende sessie niet hoeft te raden waar de knop zit.

Gevendored in src/assets/js/vendor (Lenis 1.3.26, MIT, 43KB samen), want de
CSP staat geen CDN toe. Dynamisch geimporteerd, dus mobiel haalt het niet
op. VENDOR_V ophogen als die bestanden wijzigen. De CSP hoefde niet aan:
strict-dynamic staat een import vanuit een vertrouwde module toe.

TWEE KLEINE DINGEN uit dezelfde ronde:

  • De terug-naar-boven-knop wijkt voor de mini-speler. Die kwam er anders bovenop te liggen (schermafbeelding). body.has-audio-player bestond al en wordt door audio-player.js gezet, dus dit volgt vanzelf.
  • Vastgepind is nu zichtbaar in de leestitel, met hetzelfde pin-icoon als in de tijdlijn. In de leesstroom staat een gepind bericht bovenaan zonder dat je ziet waarom. Nagekeken met een echte pin op dev.

style.css naar v82, MOD_V naar 19.

Suite 1169/1169.

Co-Authored-By: Claude Opus 5 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/assets/js/mod/read.js

    rd8f8f07 ra505f31  
    117117let rustTimer = null;
    118118
     119/**
     120 * LENIS, en alleen op desktop.
     121 *
     122 * Op touch doet Lenis van zichzelf niets (syncTouch staat standaard uit) en is
     123 * het systeem-scrollen al soepel -- daar blijft de native CSS-snap staan die
     124 * hierboven beschreven is. Robins keuze (20-8): "enkel voor desktop, dat is
     125 * prima, logisch dat het niet op mobiel kan".
     126 *
     127 * WAAROM LENIS HIER STAAT, en dat is niet het vloeiende scrollen: de DUUR van
     128 * een snap is met native scroll-snap niet in te stellen -- die zit in de browser.
     129 * Lenis' snap-pakket wel: duration, easing, distanceThreshold en debounce zijn
     130 * allemaal van ons. Dat was de aanleiding.
     131 *
     132 * Het vloeiende scrollen (smoothWheel) kwam er daarna bij, en dat is de kant die
     133 * OPPASSEN vraagt. Een Mac-trackpad heeft zijn EIGEN momentum, en Lenis'
     134 * demping komt daar bovenop -- dubbel gedempt voelt drijverig. Vandaar lerp 0.2
     135 * in plaats van de standaard 0.1. Robin vond dat op 20-8 nog steeds te zweverig,
     136 * dus dat getal is nog niet uit; zie de opmerking bij de instellingen.
     137 *
     138 * De stand met `smoothWheel: false` werkte ook, en dan doet Lenis alleen de
     139 * snap. Dat is de terugvalpositie als het vloeiende scrollen niet bevalt.
     140 *
     141 * lenis/snap haakt alleen in op lenis.on('scroll') en roept lenis.scrollTo aan
     142 * (nagekeken in de dist), dus die opzet werkt.
     143 */
     144const OP_DESKTOP = window.matchMedia('(hover: hover) and (pointer: fine)');
     145const VENDOR_V = 1;   // ophogen als de bestanden in /assets/js/vendor wijzigen
     146
     147let lenis = null;
     148let snap = null;
     149
     150async function startLenis() {
     151  if (lenis || !OP_DESKTOP.matches) return;
     152  const [L, S] = await Promise.all([
     153    import(`/assets/js/vendor/lenis.mjs?v=${VENDOR_V}`),
     154    import(`/assets/js/vendor/lenis-snap.mjs?v=${VENDOR_V}`),
     155  ]);
     156  lenis = new L.default({
     157    // Lenis tekent de scrollbeweging zelf, maar STEVIG GEDEMPT (lerp 0.2 in
     158    // plaats van de standaard 0.1). Reden: een muiswiel scrollt in schokken en
     159    // heeft die demping nodig; een Mac-trackpad heeft zijn EIGEN momentum en
     160    // krijgt er dan een tweede overheen -- dat is precies het drijverige gevoel
     161    // waar Robin voor waarschuwde. Hoger betekent korter naijlen, dus dit is de
     162    // middenweg: de schokjes weg, de nasleep kort.
     163    // Staat het toch te zweven, dan is lerp omhoog (richting 1) of terug naar
     164    // smoothWheel:false de knop -- die stand werkte ook, met alleen de snap.
     165    smoothWheel: true,
     166    lerp: 0.2,
     167    syncTouch: false,   // op touch blijft alles van het systeem
     168    autoRaf: true,
     169  });
     170  snap = new S.default(lenis, {
     171    type: 'proximity',
     172    distanceThreshold: '12%',   // de vangzone, hier WEL instelbaar
     173    debounce: 60,               // niet de standaard 500: dat voelt als te laat
     174    duration: 0.4,              // in totaal onder de halve seconde
     175    // Vlot weg, dan steeds langzamer aankomen (Robin, 20-8). easeOutQuart: op de
     176    // helft van de tijd is 94% van de weg af, en de rest dempt zacht uit.
     177    // Bewust NIET Lenis' standaard easeOutExpo -- die schiet weg en kruipt dan
     178    // zo lang na dat het lijkt of hij niet afmaakt.
     179    easing: (t) => 1 - Math.pow(1 - t, 4),
     180  });
     181  // Het snappunt is de bovenkant van elk bericht. Het laatste doet niet mee:
     182  // zijn bovenkant is niet te bereiken, er zit te weinig pagina onder.
     183  const berichten = [...document.querySelectorAll('.feed-reader .read-post')];
     184  berichten.slice(0, -1).forEach((a) => snap.addElement(a, { align: 'start' }));
     185  // Native snappen uit: twee mechanismen op dezelfde scroller vechten.
     186  document.documentElement.style.scrollSnapType = 'none';
     187}
     188
     189function stopLenis() {
     190  if (snap) { snap.destroy(); snap = null; }
     191  if (lenis) { lenis.destroy(); lenis = null; }
     192  document.documentElement.style.scrollSnapType = '';
     193}
     194
     195/**
     196 * Omhoog niet snappen. Met Lenis is dat snap.stop()/start(); zonder Lenis (dus
     197 * op touch) zetten we de CSS-eigenschap om, precies zoals hiervoor.
     198 */
    119199function zetSnappen(aan) {
     200  if (snap) { if (aan) snap.start(); else snap.stop(); return; }
    120201  const el = document.documentElement;
    121202  const wil = aan ? '' : 'none';
     
    197278  window.addEventListener('touchmove', opRaakBeweeg, { passive: true });
    198279  window.addEventListener('keydown', opToets);
     280
     281  // Alleen in de leesweergave, en alleen op desktop. Bij elke init() opnieuw
     282  // beoordelen: van Grid naar Lezen schakelen hoort hem aan te zetten, en
     283  // wegnavigeren hoort hem op te ruimen.
     284  stopLenis();
     285  if (document.body.dataset.feedView === 'reader') startLenis();
    199286}
    200287
Note: See TracChangeset for help on using the changeset viewer.