Graphics editor development: scaling the canvas with the cursor position

Hello everyone, I am the front-end watermelon brother.

Canvas scaling is an important basic capability in graphic design tools.

Through it, we can walk around the world where the graphics are located like holding a camera. Through the lens, we can only see the graphics we want to see; we can zoom in the camera to see the details of the graphics; we can also zoom out the camera, Get an overview of the relationships between multiple graphs.

ok, then let's see how to implement the zoom canvas function.

The animation demo in this article is from the graphic design tool I am developing:

https://github.com/F-star/suika

Online experience:

https://blog.fstars.wang/app/suika/

Scene coordinate system and view coordinate system

The scene coordinate system is the coordinate system used in the two-dimensional plane world where the graphics are located. The unit is pixel (px). The origin of the coordinate system is the upper left corner of the canvas (canvas element), the x-axis is right, and the y-axis is down.

Graphics will be drawn on this plane, and theoretically its range can be extended infinitely . (But in fact we will give an upper limit, but this value is also very large. It is meaningless if it is infinite, and the floating point number is a range of values)

However, the width and height of the display are limited, and only the content within a rectangular range can be viewed.

So we need to introduce a "camera": the view coordinate system , which only looks at part of the area.

In fact, it is to do a linear calculation of the coordinates of the original real graphics.

The first is to move a specific area into the viewport, just like the camera moves from the origin to an object we want to observe. But in fact, the plane where the object is located moves in one direction.

insert image description here

Then do a zoom , just like the camera zooms in or away from the target object, the effect is that the object becomes larger or smaller under the lens.

insert image description here

Transformation is just two steps, move and scale .

view matrix transformation

The transformation from the scene coordinate system to the view coordinate system is achieved by multiplying the view matrix .

In fact, the conversion of coordinates in any two coordinate systems can be realized by a matrix multiplication.

For matrix and matrix multiplication, you can read this article of mine

" Computer Graphics: Transformation Matrices "

The first is to shift the coordinates, the displacement in the x direction -viewport.x, and the displacement in the y direction -viewport.y. Here is a negative number, although we want to move the "camera" because it's the canvas that's moving

<平移矩阵> * 坐标

Then zoom (we will use zoom to represent the zoom value):

<缩放矩阵> * 平移后的坐标

All the processes written together are:

<缩放矩阵> * <平移矩阵> * 坐标

Matrix multiplication is associative , so our view matrix is:

  <视图矩阵>
= <缩放矩阵> * <平移矩阵>

The matrix is ​​expressed as:

The result of the calculation is:

Corresponding Canvas 2D code:

ctx.scale(zoom, zoom);
ctx.translate(-viewport.x, -viewport.y);

Written as a method:

// 场景坐标转视图坐标
function sceneCoordsToViewport(x, y, zoom, scrollX, scrollY) {
    
    
  return {
    
    
		x: (x - scrollX) * zoom,
		y: (y - scrollY) * zoom
  };
}

As for the reverse, the scene coordinate system is converted to the view coordinates, and its inverse matrix can be calculated:

Zoom around the cursor

insert image description here

First of all, let's recognize the essence. What is the so-called zooming around the cursor?

The relative position of the point where the cursor is in the view coordinate system from the upper left corner of the viewport remains unchanged .

What we need to do is to adjust the values ​​of viewport.x and viewport.y after the zoom changes, so that the distance between the cursor and the upper left corner of the viewport in the view coordinate system remains unchanged.

Here is a point of knowledge to add. It is the transformation of the distance in the two coordinate systems:

  1. The scene is turned to the view, and the distance is converted to dist * zoom;
  2. The conversion of the view to the scene and the distance is dist / zoom. Because the graphics seen by the viewport are the results of zooming (multiplied by zoom), so the reverse must be subtracted.

The realization idea is:

  1. Record the scene coordinates of the cursor position before zooming;
  2. Computes (cx, cy)the scene coordinates at the old zoom.
  3. Compute cx at the new zoom ratio (zoom), (cx / zoom, cy / zoom).
  4. Then subtract the two to get the new palatable upper left corner coordinates.

The code is implemented as:

/**
 * 以某点为中心,进行画布缩放
 * @param {number} zoom 新的缩放比
 * @param {number} cx 缩放中心(使用视图坐标)
 * @param {number} cy
 */
const setZoomAndUpdateViewport = (zoom, cx, cy) => {
    
    
  const prevZoom = this.zoom;
  this.zoom = zoom;

  // 计算缩放中点的场景坐标
  const {
    
     x: sceneCX, y: sceneCY } = viewportCoordsToScene(
    cx,
    cy,
    prevZoom,
    this.viewport.x,
    this.viewport.y,
  );

  // 核心代码
  const newViewportX = sceneCX - cx / zoom;
  const newViewportY = sceneCY - cy / zoom;

  this.viewport.x = newViewportX;
  this.viewport.y = newViewportY;

  this.renderScene();
};

Scale around the canvas

If the cursor is not on the canvas when zooming, such as by manually entering a zoom value, it will be zoomed at the center of the canvas .

The implementation is the same as above, except that cx and cy are changed to the width and height of the incoming viewport (that is, the canvas) divided by 2: (viewport.width / 2), (viewport.height / 2).

insert image description here

end

To achieve canvas scaling, it is important to understand the relationship between scene coordinates and view coordinates.

To convert scene coordinates to view coordinates, you first need to move the canvas so that the origin of the scene coordinates aligns with the origin of the view coordinates (scene coordinates move -viewport.x and -viewport.x), and then zoom (multiply by zoom).

I am the front-end watermelon brother, welcome to follow me and learn more about graphic editors.

Guess you like

Origin blog.csdn.net/fe_watermelon/article/details/131342574