From fdaa8dae948db928a4103d5e693a28960a5a03d0 Mon Sep 17 00:00:00 2001
From: x2605 <69812394+x2605@users.noreply.github.com>
Date: Tue, 24 Mar 2026 11:04:15 +0900
Subject: [PATCH] fix cursor offset inside CSS zoom containers, add setZoom()
API and demo
---
demos/inside-css-zoom.html | 65 ++++++++++++++++++++++++++++++++++++++
src/uPlot.js | 21 ++++++++++--
2 files changed, 84 insertions(+), 2 deletions(-)
create mode 100644 demos/inside-css-zoom.html
diff --git a/demos/inside-css-zoom.html b/demos/inside-css-zoom.html
new file mode 100644
index 00000000..7519e01c
--- /dev/null
+++ b/demos/inside-css-zoom.html
@@ -0,0 +1,65 @@
+
+
+
+
+ Inside CSS Zoom
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/uPlot.js b/src/uPlot.js
index 9c875adc..fa864d7f 100644
--- a/src/uPlot.js
+++ b/src/uPlot.js
@@ -3093,6 +3093,17 @@ export default function uPlot(opts, data, then) {
}
let rect = null;
+ let cssZoom = 1;
+
+ function getEffectiveZoom(el) {
+ let z = 1;
+ while (el) {
+ let s = getComputedStyle(el).zoom;
+ if (s && s !== 'normal') z *= parseFloat(s);
+ el = el.parentElement;
+ }
+ return z;
+ }
Object.defineProperty(self, 'rect', {
get() {
@@ -3108,6 +3119,7 @@ export default function uPlot(opts, data, then) {
rect = null;
else {
rect = over.getBoundingClientRect();
+ cssZoom = getEffectiveZoom(over);
fire("syncRect", rect);
}
}
@@ -3139,8 +3151,8 @@ export default function uPlot(opts, data, then) {
setCursorEvent(e);
if (e != null) {
- _l = e.clientX - rect.left;
- _t = e.clientY - rect.top;
+ _l = (e.clientX - rect.left) / cssZoom;
+ _t = (e.clientY - rect.top) / cssZoom;
}
else {
if (_l < 0 || _t < 0) {
@@ -3408,6 +3420,11 @@ export default function uPlot(opts, data, then) {
cursorPlots.add(self);
self.syncRect = syncRect;
+
+ // allows external code to notify uPlot when container CSS zoom changes,
+ // e.g. u.syncZoom() to auto-detect, or u.syncZoom(1.5) to set explicitly.
+ // avoids the performance cost of recalculating on every mousemove.
+ self.syncZoom = (z) => { cssZoom = z ?? getEffectiveZoom(over); };
}
// external on/off