Skip to content

Commit 9b33689

Browse files
authored
Merge pull request #1707 from Kobzol/graph-zoom
Allow zooming with mouse wheel in graphs
2 parents 3aa9777 + 6cb2d39 commit 9b33689

File tree

1 file changed

+114
-0
lines changed

1 file changed

+114
-0
lines changed

site/frontend/src/graph/render.ts

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,119 @@ function tooltipPlugin({
147147
};
148148
}
149149

150+
// Taken from https://leeoniya.github.io/uPlot/demos/zoom-wheel.html
151+
function wheelZoomPlugin(opts) {
152+
let factor = opts.factor || 0.75;
153+
154+
let xMin, xMax, yMin, yMax, xRange, yRange;
155+
156+
function clamp(nRange, nMin, nMax, fRange, fMin, fMax) {
157+
if (nRange > fRange) {
158+
nMin = fMin;
159+
nMax = fMax;
160+
} else if (nMin < fMin) {
161+
nMin = fMin;
162+
nMax = fMin + nRange;
163+
} else if (nMax > fMax) {
164+
nMax = fMax;
165+
nMin = fMax - nRange;
166+
}
167+
168+
return [nMin, nMax];
169+
}
170+
171+
return {
172+
hooks: {
173+
ready: (u) => {
174+
xMin = u.scales.x.min;
175+
xMax = u.scales.x.max;
176+
yMin = u.scales.y.min;
177+
yMax = u.scales.y.max;
178+
179+
xRange = xMax - xMin;
180+
yRange = yMax - yMin;
181+
182+
let over = u.over;
183+
let rect = over.getBoundingClientRect();
184+
185+
// wheel drag pan
186+
over.addEventListener("mousedown", (e) => {
187+
if (e.button == 1) {
188+
// plot.style.cursor = "move";
189+
e.preventDefault();
190+
191+
let left0 = e.clientX;
192+
// let top0 = e.clientY;
193+
194+
let scXMin0 = u.scales.x.min;
195+
let scXMax0 = u.scales.x.max;
196+
197+
let xUnitsPerPx = u.posToVal(1, "x") - u.posToVal(0, "x");
198+
199+
function onmove(e) {
200+
e.preventDefault();
201+
202+
let left1 = e.clientX;
203+
// let top1 = e.clientY;
204+
205+
let dx = xUnitsPerPx * (left1 - left0);
206+
207+
u.setScale("x", {
208+
min: scXMin0 - dx,
209+
max: scXMax0 - dx,
210+
});
211+
}
212+
213+
function onup(e) {
214+
document.removeEventListener("mousemove", onmove);
215+
document.removeEventListener("mouseup", onup);
216+
}
217+
218+
document.addEventListener("mousemove", onmove);
219+
document.addEventListener("mouseup", onup);
220+
}
221+
});
222+
223+
// wheel scroll zoom
224+
over.addEventListener("wheel", (e) => {
225+
e.preventDefault();
226+
227+
let {left, top} = u.cursor;
228+
229+
let leftPct = left / rect.width;
230+
let btmPct = 1 - top / rect.height;
231+
let xVal = u.posToVal(left, "x");
232+
let yVal = u.posToVal(top, "y");
233+
let oxRange = u.scales.x.max - u.scales.x.min;
234+
let oyRange = u.scales.y.max - u.scales.y.min;
235+
236+
let nxRange = e.deltaY < 0 ? oxRange * factor : oxRange / factor;
237+
let nxMin = xVal - leftPct * nxRange;
238+
let nxMax = nxMin + nxRange;
239+
[nxMin, nxMax] = clamp(nxRange, nxMin, nxMax, xRange, xMin, xMax);
240+
241+
let nyRange = e.deltaY < 0 ? oyRange * factor : oyRange / factor;
242+
let nyMin = yVal - btmPct * nyRange;
243+
let nyMax = nyMin + nyRange;
244+
[nyMin, nyMax] = clamp(nyRange, nyMin, nyMax, yRange, yMin, yMax);
245+
246+
u.batch(() => {
247+
u.setScale("x", {
248+
min: nxMin,
249+
max: nxMax,
250+
});
251+
252+
u.setScale("y", {
253+
min: nyMin,
254+
max: nyMax,
255+
});
256+
});
257+
});
258+
},
259+
},
260+
};
261+
}
262+
150263
function genPlotOpts({
151264
width,
152265
height,
@@ -253,6 +366,7 @@ function genPlotOpts({
253366
isInterpolated,
254367
absoluteMode,
255368
}),
369+
wheelZoomPlugin({factor: 0.75}),
256370
],
257371
};
258372
}

0 commit comments

Comments
 (0)