Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

wheel zoom plugin not work when I add point #725

Open
GitHubLionel opened this issue Jul 27, 2022 · 3 comments
Open

wheel zoom plugin not work when I add point #725

GitHubLionel opened this issue Jul 27, 2022 · 3 comments

Comments

@GitHubLionel
Copy link

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 &amp; 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>
@GitHubLionel
Copy link
Author

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

@moll33er
Copy link

moll33er commented Aug 8, 2022

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,.

@GitHubLionel
Copy link
Author

Yeah, it work fine with my first demo code :)

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants