在画布上以鼠标位置为中心进行缩放

3
我正在尝试使用p5.js实现缩放功能。当前的缩放级别和x、y位置存储在controls.view对象中。默认或(0,0)位置在左上角。问题是在缩放时调整x和y位置值,使得无论视图的当前位置如何,它都将停留在缩放点或鼠标光标处。
通过x、y和缩放值在draw函数中执行缩放和平移操作,因此必须直接修改这些值(不能直接在画布上运行translate)。这里是Codepen

let canvas, circles;
const controls = {
  view: {
    x: 0,
    y: 0,
    zoom: 1
  },
  viewPos: {
    prevX: null,
    prevY: null,
    isDragging: false
  },
}

function setup() {
  canvas = createCanvas(window.innerWidth, window.innerHeight);
  canvas.mouseWheel(e => Controls.zoom(controls).worldZoom(e))
  circles = Circle.create(100)
}

function draw() {
  background(100)
  translate(controls.view.x, controls.view.y);
  scale(controls.view.zoom)
  circles.forEach(circle => circle.show());
}

window.mousePressed = e => Controls.move(controls).mousePressed(e)
window.mouseDragged = e => Controls.move(controls).mouseDragged(e);
window.mouseReleased = e => Controls.move(controls).mouseReleased(e)


class Controls {
  static move(controls) {
    function mousePressed(e) {
      controls.viewPos.isDragging = true;
      controls.viewPos.prevX = e.clientX;
      controls.viewPos.prevY = e.clientY;
    }

    function mouseDragged(e) {
      const {
        prevX,
        prevY,
        isDragging
      } = controls.viewPos;
      if (!isDragging) return;

      const pos = {
        x: e.clientX,
        y: e.clientY
      };
      const dx = pos.x - prevX;
      const dy = pos.y - prevY;

      if (prevX || prevY) {
        controls.view.x += dx;
        controls.view.y += dy;
        controls.viewPos.prevX = pos.x, controls.viewPos.prevY = pos.y
      }
    }

    function mouseReleased(e) {
      controls.viewPos.isDragging = false;
      controls.viewPos.prevX = null;
      controls.viewPos.prevY = null;
    }

    return {
      mousePressed,
      mouseDragged,
      mouseReleased
    }
  }

  static zoom(controls) {
    // function calcPos(x, y, zoom) {
    //   const newX = width - (width * zoom - x);
    //   const newY = height - (height * zoom - y);
    //   return {x: newX, y: newY}
    // }

    function worldZoom(e) {
      const {
        x,
        y,
        deltaY
      } = e;
      const direction = deltaY > 0 ? -1 : 1;
      const factor = 0.1;
      const zoom = 1 * direction * factor;

      controls.view.zoom += zoom;

      controls.view.x += -(x * direction * factor);
      controls.view.y += -(y * direction * factor);
    }

    return {
      worldZoom
    }
  }
}


class Circle {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  show() {
    fill(255);
    noStroke();
    ellipse(this.x, this.y, 15, 15);
  }

  static create(count) {
    return Array.from(Array(count), () => {
      const x = random(-500, width + 500);
      const y = random(-500, height + 500);
      return new this(x, y);
    })
  }
}
body {margin: 0; padding: 0;}
canvas {vertical-align: top;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.min.js"></script>

function worldZoom(e) {
  const {x, y, deltaY} = e;
  const direction = deltaY > 0 ? -1 : 1;
  const factor = 0.1;
  const zoom = 1 * direction * factor;

  controls.view.zoom += zoom;

  controls.view.x += -(x * direction * factor);
  controls.view.y += -(y * direction * factor);
}

以下是我试图实现的一些类似问题: 将画布缩放到鼠标光标以某个点为中心进行缩放(使用比例和平移)

1个回答

9
在我的想法中,缩放的方式是基于所关注点的位置(在你的情况下是鼠标),缩放对画布的 (x, y) 位置产生加权影响。如果鼠标在左上角,这个权重应该是 (0,0),如果在右下角,应该是 (1,1)。而中心应该是 (0.5,0.5)
因此,假设我们有了这些权重,每当缩放改变时,权重就会告诉我们左上角离关注点有多远,我们应该通过 weight*dimension*(delta zoom) 将其移开。
请注意,为了计算权重,我们需要考虑维度的缩放值。因此,如果宽度是 100,缩放比例是 0.5,我们应该假设宽度是 50。因此所有的值都是绝对值(因为鼠标 (x,y) 是绝对的)。
所以,就像这样:
function worldZoom(e) {
    const {x, y, deltaY} = e;
    const direction = deltaY > 0 ? -1 : 1;
    const factor = 0.01;
    const zoom = 1 * direction * factor;

    // compute the weights for x and y
    const wx = (x-controls.view.x)/(width*controls.view.zoom);
    const wy = (y-controls.view.y)/(height*controls.view.zoom);

    // apply the change in x,y and zoom.
    controls.view.x -= wx*width*zoom;
    controls.view.y -= wy*height*zoom;
    controls.view.zoom += zoom;
}

你可以在这个CodePen中尝试它。
或者更简单一点,在行内计算权重:
controls.view.x -= (x-controls.view.x)/(controls.view.zoom)*zoom;
controls.view.y -= (y-controls.view.y)/(controls.view.zoom)*zoom;
controls.view.zoom += zoom;

终于,在将近两个小时之后,找到了一个简单的解决方案。是时候去睡觉了! - undefined

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