Skip to content

Commit d3d64e0

Browse files
committed
[twist] Use IntersectionObserver to avoid rendering players until 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.)
1 parent 01cbe72 commit d3d64e0

File tree

1 file changed

+25
-5
lines changed

1 file changed

+25
-5
lines changed

src/cubing/twisty/views/TwistyPlayer.ts

+25-5
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import type { SetupToLocation } from "../model/props/puzzle/state/SetupAnchorPro
1616
import type { PuzzleID } from "../model/props/puzzle/structure/PuzzleIDRequestProp";
1717
import type { BackgroundThemeWithAuto } from "../model/props/viewer/BackgroundProp";
1818
import type { BackViewLayoutWithAuto } from "../model/props/viewer/BackViewProp";
19-
import {
20-
type ControlPanelThemeWithAuto,
21-
controlsLocations,
22-
} from "../model/props/viewer/ControlPanelProp";
2319
import type {
2420
ColorScheme,
2521
ColorSchemeWithAuto,
2622
} from "../model/props/viewer/ColorSchemeRequestProp";
23+
import {
24+
type ControlPanelThemeWithAuto,
25+
controlsLocations,
26+
} from "../model/props/viewer/ControlPanelProp";
2727
import type { ViewerLinkPageWithAuto } from "../model/props/viewer/ViewerLinkProp";
2828
import type { VisualizationFormatWithAuto } from "../model/props/viewer/VisualizationProp";
2929
import type { VisualizationStrategy } from "../model/props/viewer/VisualizationStrategyProp";
@@ -169,6 +169,22 @@ const propOnly: Record<string, boolean> = {
169169
experimentalMovePressCancelOptions: true,
170170
};
171171

172+
let cachedSharedIntersectionObserver: IntersectionObserver | undefined;
173+
const intersectedCallback = Symbol("intersectedCallback");
174+
function waitForIntersection(player: TwistyPlayer) {
175+
cachedSharedIntersectionObserver ??= new IntersectionObserver(
176+
(entries, observer) => {
177+
for (const entry of entries) {
178+
if (entry.isIntersecting && entry.intersectionRect.height > 0) {
179+
(entry.target as TwistyPlayer)[intersectedCallback]();
180+
observer.unobserve(entry.target);
181+
}
182+
}
183+
},
184+
);
185+
cachedSharedIntersectionObserver.observe(player);
186+
}
187+
172188
/**
173189
* TwistyPlayer is the heart of `cubing.js`. It can be used to display a puzzle on a web page like this:
174190
*
@@ -232,11 +248,15 @@ export class TwistyPlayer
232248
#errorElem = document.createElement("div"); // TODO: Better pattern.
233249
#alreadyConnected = false; // TODO: support resetting
234250
async connectedCallback(): Promise<void> {
251+
this.addCSS(twistyPlayerCSS);
252+
waitForIntersection(this);
253+
}
254+
255+
async [intersectedCallback](): Promise<void> {
235256
if (this.#alreadyConnected) {
236257
return;
237258
}
238259
this.#alreadyConnected = true;
239-
this.addCSS(twistyPlayerCSS);
240260

241261
this.addElement(this.#visualizationWrapperElem).classList.add(
242262
"visualization-wrapper",

0 commit comments

Comments
 (0)