Ctrl and the wheel over a PDF
The same gesture Aether has, whose comment already claimed InkNote had it. Registered by hand rather than through onWheel, because React attaches wheel listeners passively and a passive listener may not call preventDefault — without which the browser zooms the whole application instead of the document. What stays still depends on how the zoom was asked for, and the two answers differ: the wheel holds the point under the pointer, the buttons hold the page being read. The buttons held nothing at all before, so three presses of + left you somewhere else in the file; that came out of building this and is fixed with it. The checks are the step, that the default really was prevented, and that a point measured on the page is still under the pointer afterwards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
8f8fecfb96
commit
580eed8992
4 files changed
+211
-7
No files matched your search
+78
@@ -270,6 +270,84 @@ try {
|
||||
await p.getByTitle("Zoom out").click();
|
||||
await p.waitForTimeout(1500);
|
||||
|
||||
/* ── Ctrl and the wheel ──
|
||||
* The claim is not only "the number changes". It is that the browser is kept
|
||||
* out of it — an unprevented Ctrl+wheel zooms the whole application, toolbar
|
||||
* and all — and that the spot under the pointer is the spot that stays put,
|
||||
* which is the whole difference between a viewer that feels steady and one
|
||||
* that jumps to the top of the page at every step. */
|
||||
const zoomNow = () => p.locator("[data-pdf-zoom]").evaluate((el) => Number(el.dataset.pdfZoom));
|
||||
const pageRect = () =>
|
||||
p.locator('[data-pdf-page="1"]').evaluate((el) => {
|
||||
const r = el.getBoundingClientRect();
|
||||
return { top: r.top, height: r.height };
|
||||
});
|
||||
|
||||
const firstPage = await p.locator('[data-pdf-page="1"]').boundingBox();
|
||||
// A point well inside the page, and low enough that holding it still means
|
||||
// scrolling rather than doing nothing.
|
||||
const at = { x: firstPage.x + firstPage.width * 0.5, y: firstPage.y + firstPage.height * 0.6 };
|
||||
const beforeRect = await pageRect();
|
||||
const fraction = (at.y - beforeRect.top) / beforeRect.height;
|
||||
const zoomBefore = await zoomNow();
|
||||
|
||||
const wheeled = await p.evaluate(
|
||||
([x, y]) => {
|
||||
const el = document.elementFromPoint(x, y);
|
||||
let prevented = null;
|
||||
const spy = (e) => (prevented = e.defaultPrevented);
|
||||
// Added after the viewer's own listener, so it runs after it and can see
|
||||
// whether the default was called off.
|
||||
document.addEventListener("wheel", spy, { passive: true });
|
||||
el.dispatchEvent(
|
||||
new WheelEvent("wheel", { deltaY: -120, ctrlKey: true, bubbles: true, cancelable: true, clientX: x, clientY: y })
|
||||
);
|
||||
document.removeEventListener("wheel", spy);
|
||||
return prevented;
|
||||
},
|
||||
[at.x, at.y]
|
||||
);
|
||||
await p.waitForTimeout(2000);
|
||||
|
||||
check((await zoomNow()) > zoomBefore, "Ctrl and the wheel zoom in", `${zoomBefore} → ${await zoomNow()}`);
|
||||
check(wheeled === true, "and the browser is stopped from zooming itself instead", `defaultPrevented ${wheeled}`);
|
||||
|
||||
const afterRect = await pageRect();
|
||||
const held = afterRect.top + fraction * afterRect.height;
|
||||
check(
|
||||
Math.abs(held - at.y) < 12,
|
||||
"and the spot under the pointer stays under the pointer",
|
||||
`${Math.round(at.y)} → ${Math.round(held)}`
|
||||
);
|
||||
|
||||
await p.evaluate(
|
||||
([x, y]) => {
|
||||
document
|
||||
.elementFromPoint(x, y)
|
||||
.dispatchEvent(
|
||||
new WheelEvent("wheel", { deltaY: 120, ctrlKey: true, bubbles: true, cancelable: true, clientX: x, clientY: y })
|
||||
);
|
||||
},
|
||||
[at.x, at.y]
|
||||
);
|
||||
await p.waitForTimeout(1800);
|
||||
check((await zoomNow()) === zoomBefore, "and the other way out again", `${await zoomNow()}`);
|
||||
|
||||
/* The buttons hold something else still: the page being read. "Show me this
|
||||
page larger" should not answer by moving to another one. */
|
||||
await p.locator('[data-pdf-page="2"]').scrollIntoViewIfNeeded();
|
||||
await p.waitForTimeout(1200);
|
||||
const readingBefore = await p.locator("[data-pdf-position]").innerText();
|
||||
await p.getByTitle("Zoom in").click();
|
||||
await p.waitForTimeout(2200);
|
||||
check(
|
||||
(await p.locator("[data-pdf-position]").innerText()) === readingBefore,
|
||||
"zooming from the buttons keeps the page being read",
|
||||
`${readingBefore} → ${await p.locator("[data-pdf-position]").innerText()}`
|
||||
);
|
||||
await p.getByTitle("Zoom out").click();
|
||||
await p.waitForTimeout(1500);
|
||||
|
||||
/* ── hiding it ── */
|
||||
await p.locator("[data-pdf-hide-ink]").click();
|
||||
await p.waitForTimeout(600);
|
||||
|
||||
Reference in new issue
Block a user