diff --git a/bundle/latex2html5.bundle.js b/bundle/latex2html5.bundle.js
index 0b608e7a..e8bc1f15 100644
--- a/bundle/latex2html5.bundle.js
+++ b/bundle/latex2html5.bundle.js
@@ -3302,6 +3302,23 @@ var LaTeX2HTML5 = (() => {
}
};
var psgraph = {
+ /**
+ * Maps a pointer's client position into the picture's own pixel space. A
+ * page may draw the svg smaller than its natural size (CSS `zoom` or a
+ * transform to fit a phone screen), and neither `offsetX` nor a raw
+ * `clientX - rect.left` accounts for that, so divide out the drawn scale.
+ * Measuring the svg itself, not the event target, also keeps a pointer over
+ * a child path from reading coordinates relative to that path.
+ */
+ pointerCoords(svg, clientX, clientY) {
+ if (!svg) return [0, 0];
+ const rect = svg.getBoundingClientRect();
+ const width = parseFloat(svg.getAttribute("width") || "") || rect.width;
+ const height = parseFloat(svg.getAttribute("height") || "") || rect.height;
+ const scaleX = rect.width && width ? rect.width / width : 1;
+ const scaleY = rect.height && height ? rect.height / height : 1;
+ return [(clientX - rect.left) / scaleX, (clientY - rect.top) / scaleY];
+ },
env: null,
getSize() {
const padding = 20;
@@ -3832,16 +3849,14 @@ var LaTeX2HTML5 = (() => {
function(event) {
event.preventDefault();
var touch = event.touches ? event.touches[0] : null;
- var rect = event.target.getBoundingClientRect();
- var touchcoords = touch ? [touch.clientX - rect.left, touch.clientY - rect.top] : [0, 0];
- drawLayer(touchcoords, { pointer: true });
+ if (!touch) return;
+ drawLayer(psgraph.pointerCoords(svg.node(), touch.clientX, touch.clientY), { pointer: true });
}
);
svg.on(
"mousemove",
function(event) {
- var coords = [event.offsetX || 0, event.offsetY || 0];
- drawLayer(coords, { pointer: true });
+ drawLayer(psgraph.pointerCoords(svg.node(), event.clientX, event.clientY), { pointer: true });
}
);
psgraph.processRputElements.call(this, el);
diff --git a/packages/html5/test/pspicture.test.ts b/packages/html5/test/pspicture.test.ts
index 97fc2a7a..40a1a3e0 100644
--- a/packages/html5/test/pspicture.test.ts
+++ b/packages/html5/test/pspicture.test.ts
@@ -180,6 +180,31 @@ describe('pspicture component (SVG rendering)', () => {
expect(shapeOrder(svg)).toEqual(before);
});
+ it('maps the pointer into picture space when the svg is drawn scaled down', () => {
+ // A page fitting a 200px picture onto a phone draws it at 100px (CSS zoom
+ // or a transform). The pointer's screen offset must be divided back out,
+ // or a dragged vector lands at half the distance from the finger.
+ const env = parsePspicture(`
+\\begin{pspicture}(0,0)(4,4)
+\\userline[linewidth=2pt]{->}(0,0)(2,2)
+\\end{pspicture}
+ `);
+
+ const div = pspicture(env);
+ document.body.appendChild(div);
+ const svg = div.querySelector('svg')!;
+ const natural = parseFloat(svg.getAttribute('width')!);
+ svg.getBoundingClientRect = () =>
+ ({ left: 10, top: 20, width: natural / 2, height: natural / 2, right: 0, bottom: 0, x: 10, y: 20 }) as DOMRect;
+
+ svg.dispatchEvent(new MouseEvent('mousemove', { bubbles: true, clientX: 10 + natural / 4, clientY: 20 + natural / 8 }));
+
+ const d = svg.querySelector('path')!.getAttribute('d')!;
+ const [x2, y2] = d.trim().split(/[\s,LM]+/).filter(Boolean).slice(-2).map(Number);
+ expect(x2).toBeCloseTo(natural / 2);
+ expect(y2).toBeCloseTo(natural / 4);
+ });
+
it('renders psdots as small circles', () => {
const env = parsePspicture(`
\\begin{pspicture}(0,0)(4,4)
diff --git a/packages/pstricks/src/lib/psgraph.ts b/packages/pstricks/src/lib/psgraph.ts
index b75f8278..d2c9d16c 100644
--- a/packages/pstricks/src/lib/psgraph.ts
+++ b/packages/pstricks/src/lib/psgraph.ts
@@ -583,6 +583,24 @@ class PatchSelection {
}
const psgraph: any = {
+ /**
+ * Maps a pointer's client position into the picture's own pixel space. A
+ * page may draw the svg smaller than its natural size (CSS `zoom` or a
+ * transform to fit a phone screen), and neither `offsetX` nor a raw
+ * `clientX - rect.left` accounts for that, so divide out the drawn scale.
+ * Measuring the svg itself, not the event target, also keeps a pointer over
+ * a child path from reading coordinates relative to that path.
+ */
+ pointerCoords(svg: Element | null, clientX: number, clientY: number): number[] {
+ if (!svg) return [0, 0];
+ const rect = svg.getBoundingClientRect();
+ const width = parseFloat(svg.getAttribute('width') || '') || rect.width;
+ const height = parseFloat(svg.getAttribute('height') || '') || rect.height;
+ const scaleX = rect.width && width ? rect.width / width : 1;
+ const scaleY = rect.height && height ? rect.height / height : 1;
+ return [(clientX - rect.left) / scaleX, (clientY - rect.top) / scaleY];
+ },
+
env: null as any,
getSize(): { width: number; height: number } {
const padding = 20;
@@ -1596,17 +1614,15 @@ const psgraph: any = {
function (this: any, event: any) {
event.preventDefault();
var touch = event.touches ? event.touches[0] : null;
- var rect = event.target.getBoundingClientRect();
- var touchcoords = touch ? [touch.clientX - rect.left, touch.clientY - rect.top] : [0, 0];
- drawLayer(touchcoords, { pointer: true });
+ if (!touch) return;
+ drawLayer(psgraph.pointerCoords(svg.node(), touch.clientX, touch.clientY), { pointer: true });
}
);
svg.on(
'mousemove',
function (this: any, event: any) {
- var coords = [event.offsetX || 0, event.offsetY || 0];
- drawLayer(coords, { pointer: true });
+ drawLayer(psgraph.pointerCoords(svg.node(), event.clientX, event.clientY), { pointer: true });
}
);
diff --git a/packages/react/src/components/pspicture.tsx b/packages/react/src/components/pspicture.tsx
index 8b8fc69f..dcbbcf96 100644
--- a/packages/react/src/components/pspicture.tsx
+++ b/packages/react/src/components/pspicture.tsx
@@ -35,17 +35,14 @@ export default (props: PspictureProps) => {
}, [props]);
const handleMouseMove = (event: React.MouseEvent) => {
- const coords = [event.nativeEvent.offsetX || 0, event.nativeEvent.offsetY || 0];
- userEventHandler(coords);
+ userEventHandler(psgraph.pointerCoords(svgRef.current, event.clientX, event.clientY));
};
const handleTouchMove = (event: React.TouchEvent) => {
event.preventDefault();
const touch = event.touches[0];
if (touch && svgRef.current) {
- const rect = svgRef.current.getBoundingClientRect();
- const coords = [touch.clientX - rect.left, touch.clientY - rect.top];
- userEventHandler(coords);
+ userEventHandler(psgraph.pointerCoords(svgRef.current, touch.clientX, touch.clientY));
}
};