图形编辑器基于Paper.js教程26:如何在canvas上实现无线网格的功能,高性能,共用网格线
最近在重构TC的UI部分,新版的设计中有一个无限网格线的功能,简单理解就是你的画布上,每隔10px就有一条垂直线或水平线,参考 啄木鸟激光的 软件 LaserPecker 如下图:
主要有两个大特性, 1: 拖动画布时,无论往哪个方向拖动,无论拖动多少距离,画布上都充满了网格,无止境 2: 缩放画布时,依然保持满屏的网格线,在不同的缩放比下,可能网格的间隔会改变
这个需求是想表达创作是无止境的,不受限于屏幕大小,网格线所在的地方,就是你创作的地方。但可能对密集恐惧症的不太友好。
这个功能最大的难点是 性能。因为在小视图的情况下,画布上的网格线有可能非常多,每一条线都是一个对象,100条垂直线,100条水平线,还没开始创作,200多个对象,就已经吃了浏览器100多M的内存,这要是再导入一个700万像素的图片,内存随时都可能溢出。
针对这个需求,我们有一个很简单的实现方案,就是在拖动画布的事件,缩放画布的事件里 将先前的网格线清空,然后再创建新的网格线,创建的网格线,只需要创建视图内的网格线即可。
在paperjs中 可以很简单地获取到当前视图的四个顶点paper.view.bounds
根据顶点的坐标来确定创建网格线的起点和终点, 需要注意的是,网格线的起点并不一定是视图的起点,因为视图的起点就是就是左上角,有可能是不能被 网格线的间隔整除。 比如视图的起点是27,17,而你的网格线间隔是10,那么你画网格线的起点可以是 30,20,也有是20,10。需要找到距离起点最近的网格线开始画。如果你以视图的起点为网格线的起点,那么你在拖动画布时,网格线是不会动的,因为它始终以画布左上角为起点。这是开发者容易犯的一个错误。
在拖动时清空之前的网格,重新创建网格,这里面的性能消耗,不用说你也知道开销很大。特别是遇到比较卡的电脑。
于是我就想能否创建一个比视图稍微大一点的网格,然后在移动画布时网格页按照某种规律进行移动,共用网格,不必清空网格,重新创建。
于是为了一下AI,结果给的方案很不靠谱,最后还是我自己思考。
假设我们的canvas 大小是100100,那么我们的视图就是100100。网格线按10px的分割。
我们创建网格线时按照 120* 120来创建,就是在视图外多创建两条网格线,我们称之为边界缓冲线。
默认情况下,我们拖动画布往左拉动,那么画布上所有的元素都会往左移动,网格线也不例外。
如下图:
那么当我们往左拖动画布的距离超过了一个网格线间隔,我们就让网格线整体往右移动一个网格线间隔。比如你此次拖动往左移动了17px,那么网格线整体就向右移动10px
基于上面的思想,我们可以在移动画布的事件里,累计一次拖动的移动距离,当移动的距离超够网格间隔时,就向相反方向移动网格,并将累计的距离减去网格间隔,进行继续累加。
在拖动画布时的事件里,可以这样处理
代码语言:javascript代码运行次数:0运行复制 tool.onMouseDrag = (event) => {
if (dragging && lastPoint) {
lastViewCenter = paper.view.center;
const delta = lastPoint.subtract(event.point);
if (!moveGridDelta) {
moveGridDelta = delta
} else {
moveGridDelta = moveGridDelta.add(delta);
}
paper.view.center = paper.view.center.add(delta);
lastPoint = event.point.add(paper.view.center.subtract(lastViewCenter));
// 如果moveGridDelta的任意一边超过了gridSpacing,则更新网格线,移动网格线
if (Math.abs(moveGridDelta.x) > gridSpacing || Math.abs(moveGridDelta.y) > gridSpacing) {
moveGridDelta = moveGrid(moveGridDelta, gridSpacing, gridLinesGroup)
}
}
};
核心函数是 moveGrid
函数。
看一下最后的效果图: