SVG 鼠标缩放 - 数学模型

8
在你想“为什么这个人要在这个问题上寻求帮助,这个问题肯定已经被实现了1000次”之前 - 虽然你大多数情况下是正确的,但我尝试使用几个开源库来解决这个问题,但我还是失败了。
我正试图从头开始实现基于SVG的“鼠标滚轮缩放,以鼠标为中心”的功能。
我知道有许多库可以实现这一点,例如d3和svg-pan-zoom。不幸的是,我使用这些库的实现并没有达到我的期望。我希望能够从社区获得关于此类UI功能的基本数学模型方面的帮助。
基本上,期望的行为类似于Google地图,用户将鼠标悬停在某个位置上,滚动鼠标滚轮(向内),地图图像的比例增加,而悬停位置成为视口的水平和垂直中心。
自然地,我可以访问视口的宽度/高度和鼠标的x / y坐标。
在这个例子中,我只关注x轴,视口宽度为900个单位,正方形宽度为100个单位,其x偏移量为400个单位,比例为1:1。
<g transform="translate(0 0) scale(1)">

enter image description here

假设鼠标的x位置在或接近450个单位,如果用户滚动缩放到2:1,则我期望x偏移量达到-450个单位,使焦点点居中。
<g transform="translate(-450 0) scale(2)">

enter image description here

每次滚轮滚动增量时,x和y偏移量需要根据当前缩放/鼠标偏移量的函数重新计算。我所有的尝试都未能达到期望的行为,感谢任何建议。虽然我感谢任何帮助,但请不要回答使用第三方库、jQuery插件等建议。我的目的是在一般意义上理解这个问题背后的数学模型,我使用SVG主要是为了说明性。
1个回答

12

我通常做的是维护三个变量:offset x、offset y 和 scale。它们将被应用作一个容器组的变换,比如你的元素 <g transform="translate(0 0) scale(1)">

如果鼠标在原点上方,新的平移会很容易计算。你只需将 offset x 和 y 乘以比例尺的差值:

offsetX = offsetX * newScale/scale
offsetY = offsetY * newScale/scale

你可以将偏移量翻译为鼠标处于原点。然后缩放并将所有东西平移回去。看一下这个TypeScript类,它具有一个scaleRelativeTo方法,可以实现你想要的功能:

export class Point implements Interfaces.IPoint {
    x: number;
    y: number;

    public constructor(x: number, y: number) {
      this.x = x;
      this.y = y;
    }

    add(p: Interfaces.IPoint): Point {
      return new Point(this.x + p.x, this.y + p.y);
    }

    snapTo(gridX: number, gridY: number): Point {
      var x = Math.round(this.x / gridX) * gridX;
      var y = Math.round(this.y / gridY) * gridY;
      return new Point(x, y);
    }

    scale(factor: number): Point {
      return new Point(this.x * factor, this.y * factor);
    }

    scaleRelativeTo(point: Interfaces.IPoint, factor: number): Point {

      return this.subtract(point).scale(factor).add(point);
    }

    subtract(p: Interfaces.IPoint): Point {
      return new Point(this.x - p.x, this.y - p.y);
    }

  }

如果你有一个由translate(offsetX,offsetY) scale(scale)给出的变换,并且发生了一个滚动事件,其位置为(mouseX,mouseY),导致新的比例尺为newScale,则你可以通过以下方式计算新的变换:

offsetX = (offsetX - mouseX) * newScale/scale + mouseX
offsetY = (offsetY - mouseY) * newScale/scale + mouseY

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接