在画布中的鼠标位置处放大/缩小 [英] Zoom in/out at mouse position in canvas

查看:112
本文介绍了在画布中的鼠标位置处放大/缩小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to implement zoom feature using p5.js. Current zoom level and x and y positions are stored in controls.view object. Default or (0, 0) position is at top left corner. Problem is to adjust x and y position values on zoom in/out so that no matter what the current position of the view is, it will stay at point of zoom or mouse cursor.

缩放和转换是在draw函数中通过x,y和zoom值执行的,因此必须直接修改这些值(不能直接在画布上运行转换).这是 Codepen .

Scaling and translating is executed in draw function by x, y and zoom values so those values must be modified directly (can't run translate on canvas directly). Here is 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);
}

这是我尝试实现的一些类似问题: 将画布缩放到鼠标光标放大某个点(使用比例尺和平移)

This are some of the similar questions that I tried to implement: Zoom Canvas to Mouse Cursor, Zoom in on a point (using scale and translate)

推荐答案

在我看来,缩放的工作方式是基于兴趣点(在您的情况下为鼠标)的位置,缩放对画布的(x, y)位置.如果鼠标在左上方,则此权重应为(0,0),如果在右下方,则其权重应为(1,1).中心应该是(0.5,0.5).

In my mind, the way zoom works is that based on the position of the point of interest (in your case mouse), the zoom has a weighted impact on the (x, y) position of the canvas. If the mouse is at the top left, this weight should be (0,0), if it's at the bottom right, it should be (1,1). And the centre should be (0.5,0.5).

因此,假设我们有权重,则只要缩放比例发生变化,权重就会向我们显示画布的左上角离兴趣点有多远,我们应该通过weight*dimension*(delta zoom)将其移开.

So, assuming we have the weights, whenever the zoom changes, the weights show us how far the top left corner of the canvas is from the point of interest, and we should move it away by the weight*dimension*(delta zoom).

请注意,为了计算权重,我们需要考虑尺寸的放大/缩小值.因此,如果width为100且缩放为0.5,则应假定宽度为50.因此所有值都是绝对的(因为鼠标(x,y)是绝对的).

Note that in order to calculate the weights, we need to consider the zoomed in/out value of the dimensions. So if width is 100 and zoom is 0.5, we should assume the width is 50. So all the values would be absolute (since the mouse (x,y) are absolute).

所以,就像:

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 中进行尝试.

或更简单些,内联计算权重:

Or a bit simpler, compute the weights inline:

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;

这篇关于在画布中的鼠标位置处放大/缩小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆