Skip to content

Commit

Permalink
[twisty] Use IntersectionObserver to avoid rendering players until …
Browse files Browse the repository at this point in the history
…they're on the screen.

Since we set `contain: size` on the `.wrapper` inside the player, this should not affect page layout.

Since the `TwistyProp` hierarchy can be
initialized and updated independent of the DOM,
this means that you can even call `.play()` on a
player before it starts rendering, and the
rendering will smoothly pick up fromq the middle of
the animation as soon as the player enters the
viewport.

(Note that there is no way to enforce any of this,
though, and it is entirely possible for future
code to make bad assumptions that breaks this. A
browser test would be very useful for this.)
  • Loading branch information
lgarron committed Jan 20, 2025
1 parent 01cbe72 commit 34836d6
Showing 1 changed file with 25 additions and 5 deletions.
30 changes: 25 additions & 5 deletions src/cubing/twisty/views/TwistyPlayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ import type { SetupToLocation } from "../model/props/puzzle/state/SetupAnchorPro
import type { PuzzleID } from "../model/props/puzzle/structure/PuzzleIDRequestProp";
import type { BackgroundThemeWithAuto } from "../model/props/viewer/BackgroundProp";
import type { BackViewLayoutWithAuto } from "../model/props/viewer/BackViewProp";
import {
type ControlPanelThemeWithAuto,
controlsLocations,
} from "../model/props/viewer/ControlPanelProp";
import type {
ColorScheme,
ColorSchemeWithAuto,
} from "../model/props/viewer/ColorSchemeRequestProp";
import {
type ControlPanelThemeWithAuto,
controlsLocations,
} from "../model/props/viewer/ControlPanelProp";
import type { ViewerLinkPageWithAuto } from "../model/props/viewer/ViewerLinkProp";
import type { VisualizationFormatWithAuto } from "../model/props/viewer/VisualizationProp";
import type { VisualizationStrategy } from "../model/props/viewer/VisualizationStrategyProp";
Expand Down Expand Up @@ -169,6 +169,22 @@ const propOnly: Record<string, boolean> = {
experimentalMovePressCancelOptions: true,
};

let cachedSharedIntersectionObserver: IntersectionObserver | undefined;
const intersectedCallback = Symbol("intersectedCallback");
function waitForIntersection(player: TwistyPlayer) {
cachedSharedIntersectionObserver ??= new IntersectionObserver(
(entries, observer) => {
for (const entry of entries) {
if (entry.isIntersecting && entry.intersectionRect.height > 0) {
(entry.target as TwistyPlayer)[intersectedCallback]();
observer.unobserve(entry.target);
}
}
},
);
cachedSharedIntersectionObserver.observe(player);
}

/**
* TwistyPlayer is the heart of `cubing.js`. It can be used to display a puzzle on a web page like this:
*
Expand Down Expand Up @@ -232,11 +248,15 @@ export class TwistyPlayer
#errorElem = document.createElement("div"); // TODO: Better pattern.
#alreadyConnected = false; // TODO: support resetting
async connectedCallback(): Promise<void> {
this.addCSS(twistyPlayerCSS);
waitForIntersection(this);
}

async [intersectedCallback](): Promise<void> {
if (this.#alreadyConnected) {
return;
}
this.#alreadyConnected = true;
this.addCSS(twistyPlayerCSS);

this.addElement(this.#visualizationWrapperElem).classList.add(
"visualization-wrapper",
Expand Down

0 comments on commit 34836d6

Please sign in to comment.