You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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 });
}

代码说明

  1. 坐标转换:将鼠标在页面上的坐标转换为SVG viewBox内部的坐标,确保缩放中心正确对应鼠标指向的区域。
  2. 缩放逻辑:根据滚轮方向调整缩放级别,计算缩放后的viewBox尺寸,并通过偏移量让鼠标位置保持在屏幕原位置,实现聚焦缩放效果。
  3. 边界限制:保持缩放级别在0到5之间,和原有的zoomin/zoomout函数逻辑一致。

现在当你将鼠标移到SVG内的任意区域(比如蓝色方块),滚动滚轮就能针对该区域进行放大或缩小了。


内容的提问来源于stack exchange,提问作者Theodore John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 14:25:48