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;