diff --git a/src/components/image-editor/ImageCanvasAudioPresetMarquee.tsx b/src/components/image-editor/ImageCanvasAudioPresetMarquee.tsx index 35c8128ba..496c8bcfb 100644 --- a/src/components/image-editor/ImageCanvasAudioPresetMarquee.tsx +++ b/src/components/image-editor/ImageCanvasAudioPresetMarquee.tsx @@ -394,7 +394,13 @@ export function ImageCanvasAudioPresetMarquee<
{ ).toBe(true); }); + it('stops the track from being scrolled by hand while the panel is locked', () => { + const onSelectPreset = vi.fn(); + const { container, rerender } = renderMarquee({ onSelectPreset }); + expand(); + const viewport = getViewport(container); + + // 未锁定时轨道就是可滚动容器,交给原生横向滚动。 + expect( + viewport.classList.contains( + 'image-canvas-editor__background-music-presets-viewport--locked', + ), + ).toBe(false); + + rerender( + , + ); + + // 锁定时不只停自动循环,轨道本身也不再接受触摸 / 触控板的横向滚动。 + expect(frameCallbacks.size).toBe(0); + expect( + viewport.classList.contains( + 'image-canvas-editor__background-music-presets-viewport--locked', + ), + ).toBe(true); + // 相位保留:解锁后从原处继续,不跳回队列头。 + expect(viewport.scrollLeft).toBe(QUEUE_WIDTH); + + rerender( + , + ); + + expect( + viewport.classList.contains( + 'image-canvas-editor__background-music-presets-viewport--locked', + ), + ).toBe(false); + expect(viewport.scrollLeft).toBe(QUEUE_WIDTH); + }); + it('never starts the loop under reduced motion and reacts to runtime changes', () => { prefersReducedMotion = true; const { container } = renderMarquee(); diff --git a/src/components/image-editor/ImageCanvasSoundEffectPresetMarquee.test.tsx b/src/components/image-editor/ImageCanvasSoundEffectPresetMarquee.test.tsx index 772937ae8..699a8b6a2 100644 --- a/src/components/image-editor/ImageCanvasSoundEffectPresetMarquee.test.tsx +++ b/src/components/image-editor/ImageCanvasSoundEffectPresetMarquee.test.tsx @@ -44,7 +44,7 @@ describe('ImageCanvasSoundEffectPresetMarquee', () => { expect(onSelectPreset).toHaveBeenCalledWith(SOUND_EFFECT_PROMPT_PRESETS[0]); }); - it('keeps expansion and preset controls truly disabled while locked', () => { + it('keeps expansion, preset controls and the track scroll truly disabled while locked', () => { const view = render( { }) as HTMLButtonElement ).disabled, ).toBe(true); + // 「优化中锁定滚动」按轨道自身不可被手动横向滚动验收,不只是停掉自动循环。 + expect( + view.container.querySelector( + '.image-canvas-editor__background-music-presets-viewport--locked', + ), + ).toBeTruthy(); }); }); diff --git a/src/index.css b/src/index.css index 9a81e9423..ffdd1ce69 100644 --- a/src/index.css +++ b/src/index.css @@ -17105,6 +17105,13 @@ button { display: none; } +.image-canvas-editor__background-music-presets-viewport--locked { + /* 锁定期间轨道不再是用户可滚动容器,但保留 scrollLeft 相位,解锁后循环从原处继续。 */ + overflow-x: hidden; + /* 横向手势不再作用于轨道,纵向手势仍旧交给画布。 */ + touch-action: pan-y; +} + .image-canvas-editor__background-music-presets-queue { display: flex; flex: 0 0 auto;