椭圆
通过前面的文章,我们已经可以绘制大部分图形以及文字。但是,在canvas
中,椭圆是一个复杂的存在,本身我们上学时学习椭圆本身也是一个复杂的结构。我看了很多画椭圆的方案,大部分分为两类:
- 第一类是 使用
arc()
画一个圆形,然后将其缩放变形,完成一个椭圆。 - 第二类是 使用贝塞尔曲线,即使用多条贝塞尔曲线混合拼接为一个椭圆。
经过我的实际测试,采用我认为比较简单并且显示效果比较好的方式,使用贝塞尔曲线的方式绘制椭圆。
刚才说过,贝塞尔的方式是使用多条曲线拼接,一个椭圆可以是两条曲线、三条曲线,或四条甚至更多曲线拼接。我这里使用两条就够了,而且很大程度减少我们的计算量。
关于贝塞尔曲线
贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。
在canvas
中,使用bezierCurveTo()
方法来绘制贝塞尔曲线,该方法通过使用表示三次贝塞尔曲线的指定控制点,向当前路径添加一个点。
- 提示:三次贝塞尔曲线需要三个点。前两个点是用于三次贝塞尔计算中的控制点,第三个点是曲线的结束点。曲线的开始点是当前路径中最后一个点。如果路径不存在,那么请使用 beginPath() 和 moveTo() 方法来定义开始点。
** 以上摘自HTML5 canvas bezierCurveTo()方法说明
通过贝塞尔曲线绘制椭圆
通过上图,我们可以清楚看到绘制一条贝塞尔曲线的基本要素。
所以,我们的椭圆应该大致如下(纯鼠标绘制,别吐~ ~ ~):
从图中可以看到,椭圆可以被分成两条贝塞尔曲线,我们只需要找到对应的点即可。计算过程忽略,直接看代码(代码已经经过位置计算的优化,看上去更像使用鼠标点击的起始点到结束点的椭圆):
let k = ((x2 - x1) / 0.55);
let w = (x2 - x1) / 2;
let h = (y2 - y1) / 2;
// bezier double ellipse algorithm
context.moveTo(x1, y1 + h);
context.bezierCurveTo(x1, y1 + h * 3, x1 + w * 11 / 5, y1 + h * 3, x1 + w * 11 / 5, y1 + h);
context.bezierCurveTo(x1 + w * 11 / 5, y1 - h, x1, y1 - h, x1, y1 + h);
context.stroke();
效果:
这样就绘制出了一个比较完美的椭圆。
橡皮擦
在canvas中,有三个绘制图形函数,分别是fillRect()
、strokeRect()
、clearRect()
,分别是填充图形,空心矩形,和清除矩形,前面两个函数我们已经使用过。最后的函数通常:
clearRect(0, 0, canvas.width, canvas.height)
可以完成清屏的功能。
我们如果想要达到橡皮擦的功能,该如何操作呢?
其实原理是一样的,只不过,我们需要先固定住我们需要修改的图片。
context.arc(x1, y1, 10, 0, 2 * Math.PI); // 画一个圆形,位置即鼠标当前位置,大小就是橡皮擦的半径大小,当然,也可以不适用圆形,任意形状的橡皮擦都可以。
context.clip(); // 这句很重要,它可以将上面一句定义的形状,从当前画布中剪切出来单独操作,如果没有这一句,那么一会操作的仍然是整幅画布。
context.clearRect(0, 0, canvas.width, canvas.height); // 这句很简单,和刚才的清空示例是一样的。
self.X1 = self.X2;
self.Y1 = self.Y2;
所以,它的整体逻辑分三步:
定义橡皮擦的大小和形状,通常我们定义为圆形。
固定画板,将橡皮擦的内容单独剪切出来。
清空剪切出来的内容,就完成了橡皮功能。
是不是很简单,当然,需要注意的是,随后不要忘记和随心画一样,实时修改传入的坐标,达到连续擦出的效果。
这样,简单的橡皮功能也完成了。
完整的内容已经更新在了我的github - canvasPaint中,您也可以通过我的示例来测试一下功能。
前期目录:
JavaScript 之 canvas(一)-- 认识canvas
JavaScript 之 canvas(二)-- 绘制基本图形
JavaScript 之 canvas(三)-- 使用鼠标实时绘制图形
JavaScript 之 canvas(四)-- 绘制文字
文章评论