一、背景
前段时间根据业务需要,通过canvas实现一个图片擦除、修复的功能。
具体效果如下图:左侧修复和擦除的效果,主要使用canvas的arc函数不断绘制圆形来进行清除和绘制。
二、绘制时的问题
在实际绘制过程中,当鼠标慢速滑动的时候,绘制的线条比较连贯。 但当鼠标快速滑动时,绘制的线条出现了断层,视觉上不再是一条连贯的直线,而是一个一个间断的圆。(见下图)
因为鼠标快速移动时,事件触发过于频繁,我们通常会做一些类似节流的处理,事件与事件之间存在一定的时间差,所以导致圆的绘制不连贯。(即便我们自己不做节流处理,程序处理这种高频事件存在时间差)
如下图:
三、插值补帧法提高连贯性
早些年使用flash时,其实就有补帧的功能,设置两个关键帧,然后通过flash的补帧功能,会自动补齐两个关键帧之间的路径。其实道理是一样的,比如我们两个蓝点就是两个关键帧,我们要做的就是,在已知的两点间不断的插入圆来填充我们的路径,形成一条连贯的路径。
/**
* 计算补帧路径坐标集
*
* 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
四、插值方法解析
1.首先获取补帧路径的长度
- 要进行补帧,首先我们得知道补帧的距离,也就是线段c的长度
- 已知起始点和结束点的位置,就能得出A、B的值
- 根据勾股定理 A² + B² = C²
- 然后对C²做开方处理就是C的长度
C长度 = Math.sqrt(disX ** 2 + disY ** 2)
2.根据已知路径长度,结合step计算出每次绘制的路径坐标集,循环绘制即可