perf: fix UI/scroll jank while browsing channels - #449
Open
jendrikmaass-tech wants to merge 1 commit into
Open
Conversation
The channel grid stuttered (mouse/scroll lag) while browsing, before a stream was selected. Root cause was Angular change detection running far more than necessary during scroll and hover. - home: move window scroll handling out of the Angular zone and throttle it with requestAnimationFrame; only re-enter the zone when the scroll-to-top button flips or another page needs loading. Previously every scroll event forced a layout reflow and an app-wide change detection pass. - channel-tile: use OnPush change detection so the (hundreds of) tiles are skipped during unrelated CD passes; markForCheck() is called at the points the tile mutates its own state. - home: add trackBy to the channel *ngFor so re-search/re-sort/load-more reuses tile DOM instead of rebuilding every tile. - channel-tile: precompute sourceName when the channel input is set instead of calling a method binding every CD cycle. - channel-tile: lazy-load channel logos (loading=lazy, decoding=async).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
While browsing channels (before a stream is selected), the UI stutters — mouse/scroll lag on the channel grid. Once a stream is playing it's fine, because the grid isn't churning anymore. The root cause is Angular change detection running much more often than necessary during scroll and hover.
Changes
1. Move scroll handling out of the Angular zone + throttle with rAF (
home.component.ts)The old
@HostListener("window:scroll")fired inside the Angular zone on every raw scroll event — each one read layout (scrollHeight/innerHeight, forcing a reflow) and triggered an app-wide change-detection pass. It's now registered viangZone.runOutsideAngularwith{ passive: true }, throttled to one check per animation frame withrequestAnimationFrame, and only re-enters the zone when the scroll-to-top button visibility flips or a new page needs loading. This removes the per-scroll-event CD storm that drove most of the stutter.2.
OnPushchange detection on the channel tile (channel-tile.component.ts)The tile is instantiated hundreds of times, so its CD cost is what compounds. With
OnPush, tiles are skipped during unrelated CD passes (their@Inputs don't change while you move the mouse or scroll).markForCheck()is called at the exact points a tile mutates its own state (starting,favorite/fade,hidden, image load error, context-menu open) so those still repaint correctly.HomeComponentis intentionally left on default CD — it's a singleton mutating many fields from many callers, whereOnPushwould be high-risk/low-reward.3.
trackByon the channel grid (home.component.html/home.component.ts)So re-search / re-sort / load-more reuse existing tile DOM instead of destroying and rebuilding every tile.
4. Precompute
sourceName(channel-tile.component.ts)Replaces the
getSourceName()method binding (aMaplookup evaluated every CD cycle) with a field computed once when thechannelinput is set.5. Lazy-load channel logos (
channel-tile.component.html)loading="lazy"+decoding="async"so the WebView doesn't decode hundreds of remote images at once while scrolling.Verification
ng build(AOT) passes — full TypeScript + template typecheck.No behavior changes intended: infinite scroll, the scroll-to-top button, favorite/hide toggles, the context menu, and image error fallback all work as before.