fix(reduce-motion): re-roll the clip on an instant-cut altitude change
With reduce motion on, selecting a new altitude jumps (autoScrub's reduced branch → setPos) and skips the segment that would re-roll the destination clip, so activeClipId stayed on the previous altitude's clip and the video didn't change. Self-heal at the landing point: if the landed altitude's pool doesn't contain activeClipId, pickPoolClip a fresh one. TDD: new altitude-lock e2e. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -108,6 +108,20 @@ test("dragging the dial scrubs morph currentTime and audio gains", async ({ page
|
||||
await page.mouse.up();
|
||||
});
|
||||
|
||||
test("reduce motion: selecting a new altitude still swaps to a fresh clip (instant cut)", async ({ page }) => {
|
||||
await boot(page);
|
||||
// #scale-name reads "<scale> · <clip> (i/n)"; extract the clip id.
|
||||
const clipId = (s: string) => (s.split("·")[1] || "").split("(")[0].trim();
|
||||
await page.locator('label[for="reduce-motion"]').click(); // engage reduce motion
|
||||
await expect(page.locator("#reduce-motion")).toBeChecked();
|
||||
const before = (await page.locator("#scale-name").textContent())!;
|
||||
await wheelOnStage(page, 60); // descend one altitude (instant cut)
|
||||
await page.waitForFunction(() => /orbit/.test(document.querySelector("#scale-name")?.textContent || ""), null, { timeout: 20000 });
|
||||
const after = (await page.locator("#scale-name").textContent())!;
|
||||
expect(after).toContain("orbit");
|
||||
expect(clipId(after)).not.toBe(clipId(before)); // the clip actually changed (not stuck on the old scale's clip)
|
||||
});
|
||||
|
||||
test("wheel auto-scrubs one altitude and locks", async ({ page }) => {
|
||||
await boot(page);
|
||||
const start = (await page.locator("#scale-name").textContent())!;
|
||||
|
||||
@@ -1015,6 +1015,12 @@ function setPos(next) {
|
||||
const { lo, frac } = HEFScrub.segmentOf(pos);
|
||||
if (frac === 0) { // settled exactly on an altitude → lock + base loop + single soundtrack
|
||||
busy = false;
|
||||
// If we JUMPED straight here (a reduced-motion instant cut skips the segment that
|
||||
// would re-roll the destination), activeClipId still points at the previous
|
||||
// altitude's clip. Re-roll from this altitude's pool so the video actually changes.
|
||||
if (!poolIds(ring.scales[ringIndex]).includes(activeClipId)) {
|
||||
activeClipId = pickPoolClip(ringIndex, activeClipId);
|
||||
}
|
||||
delete vid.dataset.morph; // clear so re-entering the same segment reloads the morph (not the base)
|
||||
currentClipId = null; // force ensureClipMedia to reload + loop the locked clip
|
||||
// Land the loop on the frame the morph last showed for THIS travel direction:
|
||||
|
||||
Reference in New Issue
Block a user