前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >图形编辑器开发:网格与网格吸附

图形编辑器开发:网格与网格吸附

作者头像
前端西瓜哥
发布2024-05-15 18:21:42
1280
发布2024-05-15 18:21:42
举报

大家好,我是前端西瓜哥。

今天我们来学习图形编辑器的网格模块要怎么设计和实现。

我正在开发的 suika 图形编辑器: https://github.com/F-star/suika 线上体验: https://blog.fstars.wang/app/suika/

网格是什么?

网格,指的是渲染在画布上的,按照特定间距绘制垂直和水平直线,所构成的网格。

作用是让用户可以较 直观 地观察到图形的距离和大小关系,以及实现网格吸附

网格绘制

考虑到性能,我们 只绘制视口范围内的网格线。其他超出的部分不同绘制出来。因为是重复图案(可以视作两条线组成的 L 形的平铺),可以考虑用纹理平铺渲染以提高性能。

网格通常渲染在图形的下方,并在画布缩放前后,维持线宽为 1 像素不变。

关于渲染实现,我之前写过 画布标尺的绘制的文章,思路其实是一样的。

代码语言:javascript
复制
let startXInScene = getClosestTimesVal(viewport.x, gridSpacingX);
// 从左往右,每移动网格间隔距离绘制一条从上到下的线
while (startXInScene <= endXInScene) {
  const x = nearestPixelVal((startXInScene - viewport.x) * zoom);
  ctx.beginPath();
  ctx.moveTo(x, 0);
  ctx.lineTo(x, viewport.height);
  ctx.stroke();
  ctx.closePath();
  startXInScene += gridSpacingX;
}

也有网格线在图形上方的,比如 Figma。这样有填充内容的图形不会覆盖和它重叠的网格,就能大概知道它占据了多少格子。

但这种情况下注意给网格线 设置滤镜效果或透明度,使在与其颜色相近的图形上方也能有一个较好的渲染效果,能够被分辨出来。

网格间距通常会是可配置的。

  • gripOn:网格是否开启;
  • gridSpacingX:网格水平方向间距。
  • gridSpacingY:网格垂直方向间距。

特殊的,当网格间距设置为 1 时,就变成 像素网格 了,Figma 的网格就是像素网格,不可设置网格间距。

网格线的颜色通常是灰色,不能存在感太强。

gridSpacingX 和 gridSpacingY 通常为整数,但也可以用小数。

gridSpacingX 和 gridSpacingY 的值理论上应该相等(加上限制)。但也可以不相等,比较少见,但此时格子从正方形变成了长方形。

大网格和小网格

有时候我们觉得连续的网格,不好肉眼测量。此时我们可以引入大网格。有点类似刻度尺,没隔几个小的刻度,会绘制一个长一点的大刻度。

即每 n x n 个小格子组成一个大格子。

绘制上就是在原来网格线的基础上,再画一个放大了 n 倍的网格线。注意这个大网格颜色相比小网格颜色要不同,以看出区别。

这里我们也可以考虑做成配置化:

  • majorLineColor:主网格线颜色
  • minorLineColor:辅网格线颜色
  • smallSpacingCount:网格数(每条主线之间的网格数),也就是前面所说的 n。

网格线颜色一般默认会比较浅,以免喧宾夺主。

网格样式

除了网格线,还有另一种网格的表示方式:用圆点表示。

点的位置对应原来网格线与线之间的交点位置。

该效果常见于白板工具。

因为密度的降低,此时可以考虑让点跟随画布缩放而缩放(还有一个前提是画布不能放得很大)。

网格密度过大

当缩小画布时,网格会跟随缩小。当缩放得非常小时,网格线就会显得非常密集。

为了解决网格密度过大的问题,通常我们有两种做法。

(1)视口上的网格间距小到一定程度,就不再显示。Figma 是这么做的。

代码语言:javascript
复制
// 最小间距,小于这个要把间距放大
const MIX_SPACING_IN_VIEWPORT = 8;
// 视口上的网格尺寸
const gridSpacingInViewport = zoom * Math.min(gridSpacingX, gridSpacingY);
let gridVisible = true;

// 密度过大,不绘制网格
if (gridSpacingInViewport < MIX_SPACING_IN_VIEWPORT) {
  gridVisible = false;
}

(2)调整网格间距为原来的整数倍,让密度动态变化,不突破阈值。

代码语言:javascript
复制
// 最小间距,小于这个要把间距放大
const MIX_SPACING_IN_VIEWPORT = 8;
// 视口上的网格尺寸
let gridSpacingInViewport = zoom * Math.min(gridSpacingX, gridSpacingY);

while (viewportGridSpaceX <= MIX_SPACING_IN_VIEWPORT) {
  gridSpacingInViewport *= smallSpacingCount;
  gridSpacingX *= smallSpacingCount;
  gridSpacingY *= smallSpacingCount;
}

上面两种方式也可以做成可配置化。

网格吸附

网格通常配套吸附效果。这样用户可以明确知道自己在用网格吸附,以及新的点大概会吸附到哪里。

找到某个值距离最近的 spacing 整数倍值的方法:

代码语言:javascript
复制
const getClosestTimesVal = (value, spacing) => {
  const n = Math.floor(value / spacing);
  const left = spacing * n;
  const right = spacing * (n + 1);
  return value - left <= right - value ? left : right;
};

则对于一个点,网格吸附后的位置为:

代码语言:javascript
复制
const gripSnapPt = {
  x: getClosestTimesVal(point.x, spacingSnapX),
  y: getClosestTimesVal(point.y, spacingSnapY),
}

网格吸附相关配置项:

  • gripSnapOn:是否开启网格吸附;
  • gridSpacingSnapX:网格水平方向吸附间距;
  • gridSpacingSnapY:网格垂直方向吸附间距。

通常吸附间距应该和网格渲染间距相同,这样吸附到网格上的界面就比较符合直觉。

但实际上是可以不一样的。尤其是网格密度过大时如果使用了动态改变网格间距的方案。

结尾

网格比较重要的大概就是这些。

我是前端西瓜哥,欢迎关注我,学习更多图形编辑器知识。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端西瓜哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 网格是什么?
  • 网格绘制
  • 大网格和小网格
  • 网格样式
  • 网格密度过大
  • 网格吸附
  • 结尾
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档