放大/缩小画布中的鼠标位置

时间:2018-03-12 22:07:15

标签: javascript canvas p5.js

我正在尝试使用p5.js实现缩放功能。当前缩放级别以及x和y位置存储在controls.view对象中。默认或(0,0)位置位于左上角。问题是在放大/缩小时调整x和y位置值,这样无论视图的当前位置是什么,它都将保持在缩放点或鼠标光标处。

缩放和翻译在draw函数中通过x,y和缩放值执行,因此必须直接修改这些值(不能直接在画布上运行转换)。这是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);
}

这是我尝试实施的一些类似问题: Zoom Canvas to Mouse CursorZoom in on a point (using scale and translate)

1 个答案:

答案 0 :(得分:4)

在我看来,缩放的工作方式是基于感兴趣点的位置(在您的情况下是鼠标),缩放对画布的(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;
}

您可以在this 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;