uPlot
uPlot copied to clipboard
wheel zoom plugin not work when I add point
Hi,
I have a problem with the wheel zoom plugin. When I add new points, the zoom stay with only the first point. How can I "refresh" the zoom ? Regards, Lionel
Here is a demo of the problem :
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Wheel Zoom & Drag</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="../dist/uPlot.min.css">
</head>
<body>
<button class="" id="add" title="Add point." onclick="doAdd()">Add point</button>
<script src="../dist/uPlot.iife.js"></script>
<script>
function getSize() {
return {
"width": window.innerWidth - 50,
"height": window.innerHeight - 100
};
}
function wheelZoomPlugin(opts) {
let factor = opts.factor || 0.75;
let xMin, xMax, yMin, yMax, xRange, yRange;
function clamp(nRange, nMin, nMax, fRange, fMin, fMax) {
if (nRange > fRange) {
nMin = fMin;
nMax = fMax;
}
else if (nMin < fMin) {
nMin = fMin;
nMax = fMin + nRange;
}
else if (nMax > fMax) {
nMax = fMax;
nMin = fMax - nRange;
}
return [nMin, nMax];
}
return {
hooks: {
ready: u => {
xMin = u.scales.x.min;
xMax = u.scales.x.max;
yMin = u.scales.y.min;
yMax = u.scales.y.max;
xRange = xMax - xMin;
yRange = yMax - yMin;
let over = u.over;
let rect = over.getBoundingClientRect();
// wheel drag pan
over.addEventListener("mousedown", e => {
if (e.button == 1) {
// plot.style.cursor = "move";
e.preventDefault();
let left0 = e.clientX;
// let top0 = e.clientY;
let scXMin0 = u.scales.x.min;
let scXMax0 = u.scales.x.max;
let xUnitsPerPx = u.posToVal(1, 'x') - u.posToVal(0, 'x');
function onmove(e) {
e.preventDefault();
let left1 = e.clientX;
// let top1 = e.clientY;
let dx = xUnitsPerPx * (left1 - left0);
u.setScale('x', {
min: scXMin0 - dx,
max: scXMax0 - dx,
});
}
function onup(e) {
document.removeEventListener("mousemove", onmove);
document.removeEventListener("mouseup", onup);
}
document.addEventListener("mousemove", onmove);
document.addEventListener("mouseup", onup);
}
});
// wheel scroll zoom
over.addEventListener("wheel", e => {
e.preventDefault();
let {left, top} = u.cursor;
let leftPct = left/rect.width;
let btmPct = 1 - top/rect.height;
let xVal = u.posToVal(left, "x");
let yVal = u.posToVal(top, "y");
let oxRange = u.scales.x.max - u.scales.x.min;
let oyRange = u.scales.y.max - u.scales.y.min;
let nxRange = e.deltaY < 0 ? oxRange * factor : oxRange / factor;
let nxMin = xVal - leftPct * nxRange;
let nxMax = nxMin + nxRange;
[nxMin, nxMax] = clamp(nxRange, nxMin, nxMax, xRange, xMin, xMax);
let nyRange = e.deltaY < 0 ? oyRange * factor : oyRange / factor;
let nyMin = yVal - btmPct * nyRange;
let nyMax = nyMin + nyRange;
[nyMin, nyMax] = clamp(nyRange, nyMin, nyMax, yRange, yMin, yMax);
u.batch(() => {
u.setScale("x", {
min: nxMin,
max: nxMax,
});
u.setScale("y", {
min: nyMin,
max: nyMax,
});
});
});
}
}
};
}
let plot;
let data = [
[ 1, 2, 3, 4, 5, 6, 7],
[40,43,60,65,71,73,80],
[18,24,37,55,55,60,63],
];
function makeChart() {
console.time('chart');
let opts = {
title: "Wheel Zoom & Drag",
width: 600,
height: 400,
plugins: [
wheelZoomPlugin({factor: 0.75})
],
...getSize(),
scales: {
x: {
time: true,
},
// y: {
// auto: false,
// }
},
series: [
{},
{
label: "One",
stroke: "red",
},
{
label: "Two",
stroke: "blue",
},
]
};
plot = new uPlot(opts, data, document.body);
window.addEventListener("resize", e => {
plot.setSize(getSize());
});
console.timeEnd('chart');
}
function doAdd() {
data[0].push(data[0].length+1);
data[1].push(Math.random()*80);
data[2].push(Math.random()*70);
plot.setData(data);
}
makeChart();
</script>
</body>
</html>
After several try, I found a solution. I have added this in the hook part of the plugin :
setData: u => {
xMin = u.scales.x.min;
xMax = u.scales.x.max;
yMin = u.scales.y.min;
yMax = u.scales.y.max;
xRange = xMax - xMin;
yRange = yMax - yMin;
}
That works, but I don't know if it is a good solution ! Regards, Lionel
I code new plugin base on wheel demo, it's work. This plugin can zoom or drag all series and scales.
// 滚轮缩放插件
function wheelZoomPlugin(opts) {
// 缩放倍率
let factor = opts.factor || 0.75;
return {
hooks: {
ready: u => {
// 获取绘制曲线区域
let over = u.over;
let rect = over.getBoundingClientRect();
// 给坐标轴添加鼠标滚动缩放和拖拽事件
for (let key in u.axes) {
// 获取坐标轴css区域
let axis = u.axes[key]._el;
// 获取坐标轴面积等参数
let a_rect = axis.getBoundingClientRect();
// 添加参数
axis._params = [key, a_rect];
// 坐标轴区域监听鼠标按下
axis.addEventListener("mousedown", e => {
// 鼠标中键按下
if (e.button == 1) {
e.preventDefault();
// 获取传递的参数
let key = e.target._params[0];
// 获取对应坐标轴的ID
let scale_key = u.axes[key].scale;
// 取得坐标轴
let scale = u.scales[scale_key]
let umin = scale.min;
let umax = scale.max;
// 记录坐标轴和像素的比例
let scale_scale = u.posToVal(0, scale_key) - u.posToVal(1, scale_key);
// 记录鼠标起始位置
let left0 = e.clientX;
let top0 = e.clientY;
// 当坐标轴按下鼠标中键且移动时
function onmove(e) {
e.preventDefault();
// 记录移动的位置
let left1 = e.clientX;
let top1 = e.clientY;
// 计算X Y偏移量
let dt = 0;
if (scale.ori === 0) { // 为X轴
let dx = (left1 - left0);
dt = dx;
} else if (scale.ori === 1) {
let dy = top1 - top0;
dt = dy;
} else { return; }
// 记录其他坐标的范围,防止被设为自动
let urange = []
for (let key in u.scales) {
urange[key] = [u.scales[key].min, u.scales[key].max];
}
// 开始缩放,设置坐标轴最值
u.setScale(scale_key, {
min: umin + dt * scale_scale,
max: umax + dt * scale_scale,
});
// 复原其他坐标轴
for (let key in urange) {
if (key !== scale_key) {
u.setScale(key, {
min: urange[key][0],
max: urange[key][1],
});
}
}
}
// 鼠标中键松开事件
function onup(e) {
// 移除事件回调
document.removeEventListener("mousemove", onmove);
document.removeEventListener("mouseup", onup);
}
// 监听鼠标移动、松开事件
document.addEventListener("mousemove", onmove);
document.addEventListener("mouseup", onup);
}
});
// 坐标轴区域添加鼠标滚轮事件
axis.addEventListener("wheel", e => {
e.preventDefault();
// 获取参数
let key = e.target._params[0];
let scale_key = u.axes[key].scale;
// 对范围进行缩放,以中点为基点
let range = u.scales[scale_key].max - u.scales[scale_key].min;
let val = (u.scales[scale_key].max + u.scales[scale_key].min) / 2;
// 缩放的比例
range = e.deltaY < 0 ? range * factor : range / factor;
let nMin = val - range / 2;
let nMax = val + range / 2;
let urange = []
for (let key in u.scales) {
urange[key] = [u.scales[key].min, u.scales[key].max];
}
// 设置坐标轴范围
u.batch(() => {
u.setScale(scale_key, {
min: nMin,
max: nMax,
});
});
// 恢复其他坐标轴
for (let key in urange) {
if (key !== scale_key) {
u.setScale(key, {
min: urange[key][0],
max: urange[key][1],
});
}
}
});
}
// 曲线区域添加拖放操作
over.addEventListener("mousedown", e => {
// 鼠标中键按下
if (e.button == 1) {
// plot.style.cursor = "move";
e.preventDefault();
// 记录按下位置
let left0 = e.clientX;
let top0 = e.clientY;
// 获取各个Y轴的最大值、最小值、坐标轴比例(应该是比例?)和轴类型(0是x轴,1是y轴)加入字典
let scY = new Array()
for (var key in u.scales) {
scY[key] = [u.scales[key].min, u.scales[key].max, u.posToVal(1, key) - u.posToVal(0, key), u.scales[key].ori];
}
// let scXMin0 = u.scales.x.min;
// let scXMax0 = u.scales.x.max;
// let xUnitsPerPx = u.posToVal(1, 'x') - u.posToVal(0, 'x');
// 当按下鼠标中键且移动时
function onmove(e) {
e.preventDefault();
// 记录移动的位置
let left1 = e.clientX;
let top1 = e.clientY;
// 计算X Y偏移量
let dx = (left1 - left0);
let dy = top1 - top0;
// 开始移动,设置所有坐标轴
for (var key in u.scales) {
if (scY[key] !== null) {
let dt = 0;
if (scY[key][3] === 0) {
// x轴
dt = dx;
}
else if (scY[key][3] === 1) {
// y 轴
dt = dy;
} else {
continue;
}
u.setScale(key, {
min: scY[key][0] - dt * scY[key][2],
max: scY[key][1] - dt * scY[key][2],
});
}
}
}
// 鼠标中键松开事件
function onup(e) {
// 移除事件回调
document.removeEventListener("mousemove", onmove);
document.removeEventListener("mouseup", onup);
}
// 监听鼠标移动、松开事件
document.addEventListener("mousemove", onmove);
document.addEventListener("mouseup", onup);
}
});
// 曲线区域添加滚轮缩放操作
over.addEventListener("wheel", e => {
e.preventDefault();
// 获取鼠标位置
let { left, top } = u.cursor;
let oRange = new Array();
// 遍历各个刻度范围
for (var key in u.scales) {
let Val = 0;
// 判断坐标轴
if (u.scales[key].ori === 0) {
Val = u.posToVal(left, key);
} else if (u.scales[key].ori === 1) {
Val = u.posToVal(top, key)
} else { continue; }
// 获取原始坐标轴范围和缩放后的范围
let range = u.scales[key].max - u.scales[key].min;
let nRange = e.deltaY < 0 ? range * factor : range / factor;
// 计算鼠标所在位置的相对比例并计算新范围
let Pct = (Val - u.scales[key].min) / range;
let nMin = Val - Pct * nRange;
let nMax = nMin + nRange;
oRange[key] = [nMin, nMax];
}
u.batch(() => {
for (var key in oRange) {
u.setScale(key, {
min: oRange[key][0],
max: oRange[key][1],
});
}
});
});
}
}
};
}
it's work for me,.
Yeah, it work fine with my first demo code :)