7

图形编辑器:图形和辅助线绘制的坐标问题

 1 year ago
source link: https://www.51cto.com/article/745560.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

图形编辑器:图形和辅助线绘制的坐标问题

作者:前端西瓜哥 2023-02-02 14:07:00
有一样非常低效的做法,就是生成一个非常大的 Canvas 画布,将其中的图形全部都画出来,然后用一个 div 容器装下,然后给 div 设置 overflow: scroll。然后调整一下 div 的 scrollLeft 和 scrollTop 就好。不推荐,效率很差。
e898b13665198466f722346c3649612b82494d.png

大家好,我是前端西瓜哥。今天看看绘制图形和辅助线时,坐标转换的一些注意点。

项目地址,欢迎 star:

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

线上体验:

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

图片

视口转场景:

function viewportCoordsToSceneCoords(x, y, scrollX, scrollY, zoom) {
  return {
  x: scrollX + x / zoom,
  y: scrollY + y / zoom
  }
}

场景转视口:

function sceneCoordsToViewportCoords(x, y, scrollX, scrollY, zoom) {
  return {
  x: (x - scrollX) * zoom,
  y: (y - scrollY) * zoom
  };
}

图形的绘制

场景很大,但能画的范围其实就视口大小。所以,我们需要将使用了场景坐标的图形的位置,转换为视口坐标,再绘制。

有一样非常低效的做法,就是生成一个非常大的 Canvas 画布,将其中的图形全部都画出来,然后用一个 div 容器装下,然后给 div 设置 overflow: scroll。然后调整一下 div 的 scrollLeft 和 scrollTop 就好。不推荐,效率很差。

对于图形我们的做法是在绘制图形前,先做矩阵变换,让之后绘制的所有像素都自动做一个转换,不用自己一个个手动转换。

有的朋友看着前面的 sceneCoordsToViewportCoords 方法,有:

viewportX = (sceneX - scrollX) * zoom;

于是认为 ctx 的变换对应的写法是这样的:

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

// 绘制各种图形
// ...

这个写法思路是对的,但细节有错误。因为 ctx.scale 的缩放中心因为前面的ctx.tranlate 从 (0, 0) 变成了 (-viewport.x, -viewport.y) 。

正确的写法其实是缩放时调整一下缩放中心,缩放后再移回去,即:

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

然后你会发现,第一行和第二行抵消了,于是化简得到:

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

// 绘制各种图形
// ...

辅助线的绘制

上面的效果,是无差别给之后绘制的所有图形做缩放。也就是说,zoom 变大时,线宽(ctx.lineWidth)也会跟着变大。

图形编辑器要绘制的除了图形外,还有非常重要的一样东西:辅助线。

(辅助线的坐标我们也是用场景坐标系的)

对于辅助线,我们希望 zoom 改变时,还能让线宽保持原来的 1px,还有让控制点的尺寸不变,如下图效果:

图片

缩放功能演示

解决方案是,我们自己算辅助线上的点在视口坐标的位置,不借助 ctx.scale 和 ctx.translate。

// 变换矩阵重置
ctx.setTransform(1, 0, 0, 1, 0, 0);
// 计算视口坐标系下的坐标值
const {x, y} = sceneCoordsToViewportCoords(rotationX, rotationY, viewport.x, viewport.y, zoom)

首先用 ctx.setTransform 将变换矩阵重置,将之前 ctx.scale 等造成的影响消除掉。

然后就是用前面写好的 sceneCoordsToViewportCoords 方法转换一下,得到视口坐标系下的位置,然后进行绘制即可。

其实就是局部做坐标系转换,比如坐标会转换、线宽不转换。其实也有另一种思路,就是让线宽除以 zoom,或尺寸除以 zoom,都可以。

场景坐标和视口坐标转换,贯穿于整个编辑器项目,还是很重要的,要好好消化。

责任编辑:姜华 来源: 前端西瓜哥

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK