Lightbox: Stop slideshow on sphere gestures

This commit is contained in:
Ömer Duran 2026-06-18 05:19:31 +02:00
parent d52554fc50
commit 75b007b502
No known key found for this signature in database
GPG key ID: 2550B0D579890013
2 changed files with 51 additions and 4 deletions

View file

@ -757,11 +757,17 @@ export default {
// reveals the prev/next arrows and top bar on touch devices. To restore that, a short
// press without a pan toggles the controls here. Only touch/pen taps do so mouse
// users still get the controls via mousemove, so desktop behavior is unchanged.
//
// Grabbing or wheel-zooming the sphere also stops a running slideshow, matching flat
// slides (whose pointer events reach captureDialogPointerDown this container swallows
// them first). When a tap is what paused it, the tap-toggle is skipped so the controls
// revealed by the pause stay visible and the user lands on the paused view.
trapSphereGestures(el) {
const stop = (e) => e.stopPropagation();
let tapX = 0;
let tapY = 0;
let tapping = false;
let pausedByGesture = false;
el.addEventListener(
"pointerdown",
(e) => {
@ -769,6 +775,10 @@ export default {
tapping = e.pointerType !== "mouse";
tapX = e.clientX;
tapY = e.clientY;
pausedByGesture = this.slideshow.active;
if (pausedByGesture) {
this.pauseSlideshow();
}
},
{ capture: false }
);
@ -776,10 +786,11 @@ export default {
"pointerup",
(e) => {
e.stopPropagation();
if (tapping && Math.abs(e.clientX - tapX) < SPHERE_TAP_SLOP && Math.abs(e.clientY - tapY) < SPHERE_TAP_SLOP) {
if (tapping && !pausedByGesture && Math.abs(e.clientX - tapX) < SPHERE_TAP_SLOP && Math.abs(e.clientY - tapY) < SPHERE_TAP_SLOP) {
this.toggleControls();
}
tapping = false;
pausedByGesture = false;
},
{ capture: false }
);
@ -788,12 +799,21 @@ export default {
(e) => {
e.stopPropagation();
tapping = false;
pausedByGesture = false;
},
{ capture: false }
);
el.addEventListener("pointermove", stop, { capture: false });
el.addEventListener(
"wheel",
(e) => {
e.stopPropagation();
if (this.slideshow.active) {
this.pauseSlideshow();
}
},
{ capture: false }
);
["pointermove", "wheel"].forEach((type) => {
el.addEventListener(type, stop, { capture: false });
});
},
// setSphereClass toggles the `pswp--sphere` marker on the PhotoSwipe root element so
// CSS can keep the prev/next arrows reachable on touch devices for 360° slides, where

View file

@ -1404,6 +1404,8 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
const trap = () => {
const wrapper = mountLightbox();
wrapper.vm.toggleControls = vi.fn();
wrapper.vm.pauseSlideshow = vi.fn();
wrapper.vm.slideshow = { active: false };
const registered = [];
const el = { addEventListener: (type, handler, opts) => registered.push({ type, handler, opts }) };
wrapper.vm.$options.methods.trapSphereGestures.call(wrapper.vm, el);
@ -1453,6 +1455,31 @@ describe("PLightbox (low-mock, jsdom-friendly)", () => {
fire("pointerup", { pointerType: "touch", clientX: 100, clientY: 100 });
expect(vm.toggleControls).not.toHaveBeenCalled();
});
it("pauses an active slideshow when the sphere is grabbed", () => {
const { vm, fire } = trap();
vm.slideshow.active = true;
fire("pointerdown", { pointerType: "touch", clientX: 100, clientY: 100 });
expect(vm.pauseSlideshow).toHaveBeenCalledTimes(1);
});
it("does NOT pause when no slideshow is running", () => {
const { vm, fire } = trap();
fire("pointerdown", { pointerType: "mouse", clientX: 100, clientY: 100 });
expect(vm.pauseSlideshow).not.toHaveBeenCalled();
});
it("pauses an active slideshow when zooming the sphere with the wheel", () => {
const { vm, fire } = trap();
vm.slideshow.active = true;
fire("wheel", {});
expect(vm.pauseSlideshow).toHaveBeenCalledTimes(1);
});
it("keeps the controls shown when a touch tap pauses the slideshow", () => {
const { vm, fire } = trap();
vm.slideshow.active = true;
fire("pointerdown", { pointerType: "touch", clientX: 100, clientY: 100 });
fire("pointerup", { pointerType: "touch", clientX: 102, clientY: 101 });
expect(vm.pauseSlideshow).toHaveBeenCalledTimes(1);
expect(vm.toggleControls).not.toHaveBeenCalled();
});
});
// setSphereClass marks the PhotoSwipe root so CSS can keep the prev/next arrows