diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/features/messages.js | 23 |
1 files changed, 22 insertions, 1 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index 278e66f..bb2d788 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -7098,17 +7098,37 @@ export function messagesFeature(ctx) { if (changed && repaint && c.win && (c.win.top || c.win.bottom)) holdScroll(render); }); } + // A row changes height after it was measured — a picture lands in a row + // that painted before its size was known, a clip learns its shape, a link + // card arrives. The stale number is what the spacer uses when the row + // leaves the window: the page then shrinks or grows by the difference, + // the browser's scroll anchoring pulls the reader back by as much, and a + // coasting wheel scroll stalled or even reversed right as the media + // appeared. Keep the measurements live instead (no layout forced: the + // observer runs after layout). + const rowSizer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver((entries) => { + for (const en of entries) { + const r = en.target, c = r.parentElement && r.parentElement._winC; + if (!c || !r.isConnected) continue; + const hgt = r.getBoundingClientRect().height, key = r.getAttribute('data-key'); + if (hgt > 0 && key) (c.heights || (c.heights = new Map())).set(key, hgt); + } + }); function measureNow(c) { const list = document.querySelector('.notes-feed'); if (!list) return false; c.listTop = list.getBoundingClientRect().top + (window.scrollY || 0); + list._winC = c; const heights = c.heights || (c.heights = new Map()); let changed = false; + const seen = new Set(); for (const r of list.querySelectorAll(':scope > .row[data-key]')) { const hgt = r.getBoundingClientRect().height; const key = r.getAttribute('data-key'); if (hgt > 0 && Math.abs((heights.get(key) || 0) - hgt) > 0.5) { heights.set(key, hgt); changed = true; } + if (rowSizer) { seen.add(r); if (!(list._sized ||= new Set()).has(r)) { list._sized.add(r); rowSizer.observe(r); } } } + if (rowSizer && list._sized) for (const r of list._sized) if (!seen.has(r)) { list._sized.delete(r); rowSizer.unobserve(r); } return changed; } // On scroll: replenish the buffer before the reader reaches its edge, @@ -7897,7 +7917,8 @@ export function messagesFeature(ctx) { const known = size || mediaReady.get(url); return h('img', { src: mediaSrc(url, known), class: 'note-img clickable', loading: feedPaint || known ? 'eager' : 'lazy', alt: label || '', - decoding: known && known.thumb ? 'sync' : undefined, // a few-ms decode: paint it with the page + // a few-ms decode of the copy paints with the page; an original never holds the page for its decode + decoding: known && known.thumb ? 'sync' : 'async', width: size?.width, height: size?.height, style: size ? 'height:auto;aspect-ratio:' + size.width + '/' + size.height + ';--ar:' + (size.width / size.height).toFixed(4) : undefined, onClick: (e) => { if (!onPicture(e)) return; e.stopPropagation(); ctx.openImage && ctx.openImage(url); }, |
