canvas插值优化笔触连贯性

282 阅读2分钟

一、背景

前段时间根据业务需要,通过canvas实现一个图片擦除、修复的功能。

具体效果如下图:左侧修复和擦除的效果,主要使用canvas的arc函数不断绘制圆形来进行清除和绘制。 iShot_2023-08-30_15.35.14.gif

二、绘制时的问题

在实际绘制过程中,当鼠标慢速滑动的时候,绘制的线条比较连贯。 但当鼠标快速滑动时,绘制的线条出现了断层,视觉上不再是一条连贯的直线,而是一个一个间断的圆。(见下图)

因为鼠标快速移动时,事件触发过于频繁,我们通常会做一些类似节流的处理,事件与事件之间存在一定的时间差,所以导致圆的绘制不连贯。(即便我们自己不做节流处理,程序处理这种高频事件存在时间差)

如下图:

iShot_2023-08-30_16.01.00.gif

三、插值补帧法提高连贯性

早些年使用flash时,其实就有补帧的功能,设置两个关键帧,然后通过flash的补帧功能,会自动补齐两个关键帧之间的路径。其实道理是一样的,比如我们两个蓝点就是两个关键帧,我们要做的就是,在已知的两点间不断的插入圆来填充我们的路径,形成一条连贯的路径。 iShot_2023-08-30_15.54.50.png

/**
 * 计算补帧路径坐标集
 *
 * curX和curY是当前鼠标的位置,相当于结束位置
 * lastX和lastY是最后一次回执的坐标,相当于起始位置
 * step可根据实际场景进行设置,非固定值,比如在我做的项目中画笔宽度可设置,那么这个值是根据画笔宽度动态设置的
 */
getInterpolation(curX,curY) {
  let points = [];
  let disX = curX - lastX,
    disY = curY - lastY;
  let distance = Math.sqrt(disX ** 2 + disY ** 2)
  let step = 3;
  for(let i = 0; i < distance; i+=step) {
    let p = i / distance;
    points.push({x: this.lastX + disX * p, y:this.lastY + disY * p})
  }
  return points;
}

// 根据坐标集循环绘制圆点进行补帧
let points = this.getInterpolation(curX, curY);
for(let v of points) {
  ctx.beginPath();
  ctx.arc(v.x, v.y, 10, 0, 2*Math.PI)
  ctx.fillStyle = '#fff';
  ctx.fill();
}

最终效果如下,连贯性提升好几个level iShot_2023-08-30_16.03.29.gif

四、插值方法解析

1.首先获取补帧路径的长度

  • 要进行补帧,首先我们得知道补帧的距离,也就是线段c的长度
  • 已知起始点和结束点的位置,就能得出A、B的值
  • 根据勾股定理 A² + B² = C²
  • 然后对C²做开方处理就是C的长度

C长度 = Math.sqrt(disX ** 2 + disY ** 2) image.png

2.根据已知路径长度,结合step计算出每次绘制的路径坐标集,循环绘制即可

五、Demo

点击查看【juejin】