最近被问的一个问题,canvas中如何画一个椭圆。作为一名前端工程师,平时接触图形学和webgl很少,对canvas了解仅限于看过canvas画图相应的api,遇到这个问题一脸蒙蔽,依稀记得canvas原生有个画圆的api,想当然的认为应该也有个对应的画椭圆的api,传入坐标点/长轴/短轴参数就可以。后面专门去尝试并研究了下,分享一下。
canvas如何画圆
原生api:arc接收:x坐标/y坐标/开始角度/结束角度/绘制方向false为顺时针(默认)几个参数
const ctx = canvas.getContext('2d');
ctx.arc(x,y,radius,startAngle,endAngle,anticlockwise)
了解了一下,实际上计算机内部调用的是光栅学,圆的参数方程x=rcosθ y=rsinθ,由此可以自己写一个画圆的方法
function circle(context, x, y, r) {
const step = 1/r;
context.beginPath();
context.moveTo(x + r, y);
for(let i = 0; i < 2 * Math.PI; i += step) {
context.lineTo(x + r * Math.cos(i), y + r * Math.sin(i));
}
context.closePath();
context.fill();
}
x、y是坐标,r是半径,1/r是1px的线段对应的角度值,也就是精细程度,当r较大时,可以写个固定的,比如1/10,不然会影响性能
开始画椭圆
其实最新版的chrome中已经有了原生画椭圆的api,只是兼容性不太好,参数x坐标/y坐标/横轴长度/竖轴长度/旋转角度/其实角度/结束角度/方向
ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
和圆类似,利用椭圆的参数方程:x=acosθ | y=bsinθ,也可以自己实现一个绘制椭圆的方法
function ellipse(context, x, y, a, b) {
const step = (a > b) ? 1 / a : 1 / b;
context.beginPath();
context.moveTo(x + a, y);
for (let i = 0; i < 2 * Math.PI; i += step) {
context.lineTo(x + a * Math.cos(i), y + b * Math.sin(i));
}
context.closePath();
ctx.fillStyle = "rgba(0,0,0,.2)";
context.fill();
}
参数与原生和画圆的类似,step可以依据实际情况调整
还有一种比较简单的方法,性能也更高,一看代码就明白了,将标准圆压缩成一个椭圆,不过用这种方法当边框宽度较大的时候会把宽度也一起压缩了
function ellipse(context, x, y, a, b) {
context.save();
const r = (a > b) ? a : b;
const ratioX = a / r;
const ratioY = b / r;
context.scale(ratioX, ratioY);
context.beginPath();
context.arc(x / ratioX, y / ratioY, r, 0, 2 * Math.PI, false);
context.closePath();
context.restore();
ctx.fillStyle = "rgba(255,0,0,.2)";
context.fill();
}
与君共勉,需要学习的还有很多。
最后推荐个学习canvas相关的地址:地址