javascript - 在 Canvas 中的鼠标位置放大/缩小

标签 javascript canvas p5.js

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

缩放和平移是在 draw 函数中通过 x、y 和缩放值执行的,因此必须直接修改这些值(不能直接在 Canvas 上运行 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);
}

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

最佳答案

在我看来,缩放的工作方式是基于兴趣点的位置(在您的例子中是鼠标),缩放对 (x, y) 位置有加权影响的 Canvas 。如果鼠标在左上角,这个权重应该是(0,0),如果它在右下角,它应该是(1,1)。中心应该是 (0.5,0.5)

因此,假设我们有权重,每当缩放发生变化时,权重会向我们显示 Canvas 左上角与感兴趣点的距离,我们应该将其移开 weight*dimension *(增量缩放)

请注意,为了计算权重,我们需要考虑尺寸的放大/缩小值。因此,如果宽度为 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;

关于javascript - 在 Canvas 中的鼠标位置放大/缩小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49245168/

相关文章:

javascript - Storybook vue composition api,_router of undefined

javascript - 如何通过 JavaScript 动态更改 Canvas 的样式大小?

html - 将 Canvas 导出到视频

javascript - 如何使用 p5.js 清除部分缓冲图像

javascript - 如何处理 javascript 弹出窗口上的 ESC keydown

javascript - 阻止所有滚动,直到函数触发

javascript - p5.j​​s clear() 函数在移动设备和某些浏览器中无法正常工作

javascript - P5 的 react 速度为 60 FPS

javascript - 如何在新的vue自定义元素中正确注册和使用props?

python - 删除 Canvas 上的点/线 Python Matplotlib