Skip to content

Commit

Permalink
[twist] Use IntersectionObserver to avoid rendering players until t…
Browse files Browse the repository at this point in the history
…hey'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 jump into the middle of
the animation as soon as the player enters the
viewport.
  • Loading branch information
lgarron committed Jan 20, 2025
1 parent 01cbe72 commit e3c46eb
Showing 1 changed file with 26 additions and 5 deletions.
31 changes: 26 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,16 @@ 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> {
console.log("connectedCallbackCallbakk");
if (this.#alreadyConnected) {
return;
}
this.#alreadyConnected = true;
this.addCSS(twistyPlayerCSS);

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

0 comments on commit e3c46eb

Please sign in to comment.