mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-10 18:59:56 +00:00
fix(highway): stop stale viz frame bleeding through after switching visualizations (#565)
Switching between the 3D drum highway (renders onto #highway) and the 3D guitar highway (renders into its own .h3d-wrap overlay) left the previous drum frame showing through the gap the overlay did not cover. Core: _setRenderer now replaces #highway on a genuine viz change (keyed on viz id via _rendererVizKey, not object identity, so benign same-viz re-installs don't churn the canvas) as well as on a context-type change. highway_3d: applySize pins the .h3d-wrap overlay to #highway's exact box, derived from the same getBoundingClientRect measurements that size the renderer (sub-pixel correct under zoom). Re-pins once the canvas lays out (init race) and resets to the static anchor in the not-laid-out fallback. Reviewed locally via codex (5 rounds, converged clean). CI checks are the known org Actions billing block, not real failures.
This commit is contained in:
+45
-1
@@ -973,6 +973,23 @@ function createHighway() {
|
||||
return '2d';
|
||||
}
|
||||
|
||||
// Logical identity of a renderer for swap detection: its viz id, not
|
||||
// its object reference. app.js stamps `pluginId`/`source` (the viz
|
||||
// picker id) onto every renderer it installs via _tagVizRenderer, and
|
||||
// setViz()/_autoMatchViz() build a FRESH renderer object with factory()
|
||||
// on every (re)apply — even when the selected viz did not change (e.g.
|
||||
// Auto re-resolving to the same viz across consecutive songs). Keying
|
||||
// the canvas-replacement decision on object identity would therefore
|
||||
// treat those benign re-installs as a swap and needlessly clone
|
||||
// #highway, dropping listeners/expando state attached to the element.
|
||||
// The default renderer has no id, so it keys on its own singleton; an
|
||||
// untagged custom renderer falls back to its object reference (the safe
|
||||
// "treat as a swap" answer).
|
||||
function _rendererVizKey(r) {
|
||||
if (r === _defaultRenderer) return _defaultRenderer;
|
||||
return (r && (r.pluginId || r.source)) || r;
|
||||
}
|
||||
|
||||
// Replace the underlying <canvas> element with a fresh one because
|
||||
// the next renderer needs a different context type than the current
|
||||
// canvas is locked to. Preserves the DOM position, id, classes,
|
||||
@@ -1038,6 +1055,11 @@ function createHighway() {
|
||||
}
|
||||
|
||||
function _setRenderer(r) {
|
||||
// Capture the outgoing renderer before _destroyCurrentIfInited /
|
||||
// the `_renderer = next` assignment below overwrite it — the
|
||||
// canvas-replacement decision needs to know whether we're
|
||||
// actually swapping to a *different* renderer instance.
|
||||
const prev = _renderer;
|
||||
_destroyCurrentIfInited();
|
||||
// null/undefined reverts to default. Anything else must provide
|
||||
// at minimum a draw(bundle) function — without it the rAF loop
|
||||
@@ -1070,7 +1092,29 @@ function createHighway() {
|
||||
// destroyed by _destroyCurrentIfInited above, so it's safe to
|
||||
// detach the element from the DOM here.
|
||||
const nextType = _resolveRendererContextType(next);
|
||||
if (nextType !== _currentCanvasContextType) {
|
||||
// Replace the underlying <canvas> when (a) the new renderer needs
|
||||
// a different context type than the one currently bound, OR (b)
|
||||
// we're swapping to a *different* visualization while keeping the
|
||||
// same context type. Case (b) prevents a stale frame from the
|
||||
// previous renderer bleeding through: renderers that draw into a
|
||||
// sibling overlay (e.g. 3D Highway's `.h3d-wrap`) never paint
|
||||
// #highway, so whatever the *previous* renderer left on the shared
|
||||
// canvas would otherwise stay visible in any area the overlay does
|
||||
// not cover. The reported symptom was the 3D drum highway (which
|
||||
// renders directly onto #highway) showing through the gap below
|
||||
// the 3D guitar highway's overlay after switching between them —
|
||||
// both are webgl2, so the type-change check alone never fired. A
|
||||
// fresh canvas starts blank/transparent, so the incoming renderer
|
||||
// always begins over a clean surface. The swap test keys on the
|
||||
// viz id (_rendererVizKey), NOT object identity: setViz/_autoMatchViz
|
||||
// build a fresh renderer object on every apply, so an object-identity
|
||||
// check would clone #highway on every benign same-viz re-install
|
||||
// (e.g. Auto re-resolving to the same viz across songs) and drop the
|
||||
// element's listeners/expando state. Skipped when re-installing the
|
||||
// same viz, and on the very first install (prev === null) where
|
||||
// there is no prior frame to clear.
|
||||
const _vizChanged = prev && _rendererVizKey(next) !== _rendererVizKey(prev);
|
||||
if (nextType !== _currentCanvasContextType || _vizChanged) {
|
||||
_replaceCanvas(nextType);
|
||||
}
|
||||
const bundle = _makeBundle();
|
||||
|
||||
Reference in New Issue
Block a user