基于SVG实现鼠标滚轮光标指向区域缩放的技术求助
鼠标滚轮聚焦区域缩放实现方案
我已经基于现有代码实现了基础缩放功能,现在需要添加鼠标滚轮对光标指向区域进行缩放的功能——比如将光标移至目标区域,滚动鼠标时就能针对该特定区域进行缩放。
原实现代码
HTML代码
<div onclick="zoomin()" style="display: block;float: left;border: 1px solid;cursor: pointer"> ZoomIn </div> <div onclick="zoomout()" style="display: block;float: left;border: 1px solid;cursor: pointer;margin-left: 10px"> ZoomOut </div> <svg id="mainsvg" width="600px" height="500px" viewBox="0 0 600 500"> <g id="gnode"> <rect id="boundry" x="0" y="0" width="599" height="499" fill="none" stroke='black'/> <circle id="centernode" cx="300" cy="250" r="5" fill="red" stroke="none" /> <rect id="selected" x="450" y="100" width="50" height="50" fill="blue" stroke='none'/> </g> </svg>
JavaScript代码
var svg = document.getElementById('mainsvg'); var gnode = document.getElementById('gnode'); var zoomPercentage = 0.25; var MAXIMUM_ZOOM_HEIGHT = 1400; var baseBox = {}; var level = 0; var widthRatio, heightRatio; var clientheight = document.documentElement.clientHeight; var clientwidth = document.documentElement.clientWidth; function setup() { var baseX, baseY, baseWidth, baseHeight, percentageDifference, heightDifference; svg.setAttribute('height', clientheight); svg.setAttribute('width', clientwidth); var boundry = document.getElementById('boundry'); boundry.setAttribute('height', clientheight - 1); boundry.setAttribute('width', clientwidth - 1); var centernode = document.getElementById('centernode'); centernode.setAttribute('cy', clientheight / 2); centernode.setAttribute('cx', clientwidth / 2); if (svg.height.baseVal.value >= MAXIMUM_ZOOM_HEIGHT) baseHeight = MAXIMUM_ZOOM_HEIGHT; else baseHeight = Math.round(gnode.getBBox().height) + 60; baseY = (svg.height.baseVal.value - baseHeight) / 2; percentageDifference = baseHeight / svg.height.baseVal.value; baseWidth = percentageDifference * svg.width.baseVal.value; baseX = (svg.width.baseVal.value - baseWidth) / 2; baseBox.x = baseX; baseBox.y = baseY; baseBox.width = baseWidth; baseBox.height = baseHeight; level = 0; heightDifference = MAXIMUM_ZOOM_HEIGHT - baseHeight; zoomPercentage = (heightDifference / 10) / heightDifference; setViewBox(baseBox); } function setViewBox(viewBox) { svg.viewBox.baseVal.x = Math.round(viewBox.x); svg.viewBox.baseVal.y = Math.round(viewBox.y); svg.viewBox.baseVal.width = Math.round(viewBox.width); svg.viewBox.baseVal.height = Math.round(viewBox.height); setRatios(); } function setRatios() { widthRatio = svg.viewBox.baseVal.width / svg.width.baseVal.value; heightRatio = svg.viewBox.baseVal.height / svg.height.baseVal.value; } function calculateViewBox(level) { var height = baseBox.height - (zoomPercentage * level * baseBox.height), y = baseBox.y + (baseBox.height - height) / 2, width = baseBox.width - (zoomPercentage * level * baseBox.width), x = baseBox.x + (baseBox.width - width) / 2, viewBox = { x: x, y: y, width: width, height: height } return viewBox; } function zoomin() { level++; if (level > 5) level = 5; var x, y, paperViewBox = svg.viewBox.baseVal, previousViewBox = calculateViewBox(level - 1), newViewBox = calculateViewBox(level); if (Math.round(paperViewBox.x) > Math.round(newViewBox.x)) x = paperViewBox.x - (previousViewBox.width - newViewBox.width) / 2; else if (Math.round(paperViewBox.x) < Math.round(previousViewBox.x) - (Math.round(newViewBox.x) - Math.round(previousViewBox.x))) x = paperViewBox.x + (previousViewBox.width - newViewBox.width) + (previousViewBox.width - newViewBox.width) / 2; else x = newViewBox.x; if (Math.round(paperViewBox.y) > Math.round(newViewBox.y)) y = paperViewBox.y - (previousViewBox.height - newViewBox.height) / 2; else if (Math.round(paperViewBox.y) < Math.round(previousViewBox.y) - (Math.round(newViewBox.y) - Math.round(previousViewBox.y))) y = paperViewBox.y + (previousViewBox.height - newViewBox.height) + (previousViewBox.height - newViewBox.height) / 2; else y = newViewBox.y; var data = { viewBox: { x: x, y: y, width: newViewBox.width, height: newViewBox.height } } SetZoomViewBox(data); } function SetZoomViewBox(data) { var viewBox = data.viewBox; svg.viewBox.baseVal.x = Math.round(viewBox.x); svg.viewBox.baseVal.y = Math.round(viewBox.y); svg.viewBox.baseVal.width = Math.round(viewBox.width); svg.viewBox.baseVal.height = Math.round(viewBox.height); setRatios(); } function zoomout() { level--; if (level < 0) level = 0; var x, y, paperViewBox = svg.viewBox.baseVal, previousViewBox = calculateViewBox(level + 1), newViewBox = calculateViewBox(level); if (Math.round(paperViewBox.x) > Math.round(previousViewBox.x) + (Math.round(previousViewBox.x) - Math.round(newViewBox.x))) x = paperViewBox.x - (newViewBox.width - previousViewBox.width); else if (Math.round(paperViewBox.x) < Math.round(previousViewBox.x)) x = paperViewBox.x; else x = newViewBox.x; if (Math.round(paperViewBox.y) > Math.round(previousViewBox.y) + (Math.round(previousViewBox.y) - Math.round(newViewBox.y))) y = paperViewBox.y - (newViewBox.height - previousViewBox.height); else if (Math.round(paperViewBox.y) < Math.round(previousViewBox.y)) y = paperViewBox.y; else y = newViewBox.y; var data = { viewBox: { x: x, y: y, width: newViewBox.width, height: newViewBox.height } } SetZoomViewBox(data); } setup();
鼠标滚轮聚焦区域缩放实现
要实现围绕鼠标位置缩放,核心是将鼠标在SVG上的坐标转换为SVG viewBox坐标系下的位置,然后调整viewBox的偏移量,让缩放中心对齐鼠标位置。以下是修改和添加的代码:
新增鼠标滚轮事件监听
在setup()函数末尾添加:
// 绑定鼠标滚轮事件 svg.addEventListener('wheel', handleWheelZoom);
实现滚轮缩放处理函数
添加以下函数到代码中:
function handleWheelZoom(e) { e.preventDefault(); // 阻止默认页面滚动 // 获取鼠标在SVG元素上的坐标 const rect = svg.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; // 将鼠标坐标转换为SVG viewBox内的坐标 const viewBoxX = svg.viewBox.baseVal.x + mouseX * widthRatio; const viewBoxY = svg.viewBox.baseVal.y + mouseY * heightRatio; // 判断滚轮方向,确定缩放级别变化 const delta = e.deltaY > 0 ? -1 : 1; const newLevel = Math.max(0, Math.min(5, level + delta)); if (newLevel === level) return; // 缩放级别未变化则退出 const currentViewBox = svg.viewBox.baseVal; const currentWidth = currentViewBox.width; const currentHeight = currentViewBox.height; // 计算缩放后的viewBox尺寸 const newWidth = currentWidth * (delta > 0 ? (1 - zoomPercentage) : (1 + zoomPercentage)); const newHeight = currentHeight * (delta > 0 ? (1 - zoomPercentage) : (1 + zoomPercentage)); // 计算偏移量,让鼠标位置保持在屏幕原位置 const offsetX = (viewBoxX - currentViewBox.x) * (newWidth / currentWidth - 1); const offsetY = (viewBoxY - currentViewBox.y) * (newHeight / currentHeight - 1); const newViewBox = { x: currentViewBox.x + offsetX, y: currentViewBox.y + offsetY, width: newWidth, height: newHeight }; level = newLevel; SetZoomViewBox({ viewBox: newViewBox }); }
代码说明
- 坐标转换:将鼠标在页面上的坐标转换为SVG viewBox内部的坐标,确保缩放中心正确对应鼠标指向的区域。
- 缩放逻辑:根据滚轮方向调整缩放级别,计算缩放后的viewBox尺寸,并通过偏移量让鼠标位置保持在屏幕原位置,实现聚焦缩放效果。
- 边界限制:保持缩放级别在0到5之间,和原有的
zoomin/zoomout函数逻辑一致。
现在当你将鼠标移到SVG内的任意区域(比如蓝色方块),滚动滚轮就能针对该区域进行放大或缩小了。
内容的提问来源于stack exchange,提问作者Theodore John
相关产品推荐
相关产品推荐

